You are given the following problem:
An ugly number is a positive integer whose prime factors are limited to 2
, 3
, and 5
.
Given an integer n
, return the n<sup>th</sup>
ugly number.
For example:
n = 10
, the expected output is 12
because the sequence of the first 10 ugly numbers is [1, 2, 3, 4, 5, 6, 8, 9, 10, 12]
.n = 1
, the expected output is 1
because 1 has no prime factors, and therefore all of its prime factors are (vacuously) limited to 2, 3, and 5.Write a function that solves the ugly number problem, taking into account the following constraints:
1 <= n <= 1690
Explain the time and space complexity of your solution. Also, discuss a naive solution and why it would not be optimal.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to finding the nth ugly number involves systematically checking every positive number until we've found enough ugly numbers. We keep checking numbers one by one, deciding if each one is ugly or not, until we've identified the nth ugly number.
Here's how the algorithm would work step-by-step:
def find_nth_ugly_number_brute_force(n):
ugly_number_count = 0
current_number = 1
while ugly_number_count < n:
if is_ugly(current_number):
# Increment the count if it's an ugly number
ugly_number_count += 1
if ugly_number_count == n:
return current_number
current_number += 1
def is_ugly(number):
if number <= 0:
return False
# Keep dividing by 2, 3, and 5 until it is no longer divisible
while number % 2 == 0:
number //= 2
while number % 3 == 0:
number //= 3
while number % 5 == 0:
number //= 5
# If the remaining number is 1, then it is an ugly number
if number == 1:
return True
return False
To find the nth ugly number efficiently, we don't check every number. Instead, we generate ugly numbers in increasing order using the prime factors 2, 3, and 5. We maintain separate sequences of numbers multiplied by each of these primes and merge them smartly.
Here's how the algorithm would work step-by-step:
def nth_ugly_number(n):
ugly_numbers = [1]
index_for_2 = 0
index_for_3 = 0
index_for_5 = 0
while len(ugly_numbers) < n:
# Find the next potential ugly numbers.
next_multiple_of_2 = ugly_numbers[index_for_2] * 2
next_multiple_of_3 = ugly_numbers[index_for_3] * 3
next_multiple_of_5 = ugly_numbers[index_for_5] * 5
# Choose the smallest one as the next ugly number.
next_ugly_number = min(next_multiple_of_2, next_multiple_of_3, next_multiple_of_5)
ugly_numbers.append(next_ugly_number)
# Advance the indices.
if next_ugly_number == next_multiple_of_2:
index_for_2 += 1
if next_ugly_number == next_multiple_of_3:
index_for_3 += 1
if next_ugly_number == next_multiple_of_5:
index_for_5 += 1
return ugly_numbers[-1]
Case | How to Handle |
---|---|
n = 0 or negative | Return 1 since the sequence starts with 1, and the 0th ugly number can be considered as 1 or throw an IllegalArgumentException. |
n = 1 | Return 1 as the first ugly number is 1. |
Large n exceeding integer range during multiplication (integer overflow) | Use long data type to store intermediate ugly numbers to prevent integer overflow. |
Extremely large n causing memory issues | Consider using a more memory-efficient data structure or algorithm, or limiting the maximum value of n. |
n is close to the maximum integer value | Check if the generated ugly number exceeds the maximum integer value and handle the overflow appropriately, such as throwing an exception or returning the maximum integer value. |
The same ugly number is generated by multiple factors simultaneously | The min heap or queue based approach inherently handles duplicates as it always picks the smallest available number. |
The algorithm gets stuck in an infinite loop because of incorrect indices updates | Carefully manage the indices for 2, 3, and 5 multipliers to ensure progress towards generating larger ugly numbers. |
Potential performance bottleneck for very large n | Optimize the min-heap implementation or explore alternative approaches like dynamic programming with memoization to improve performance. |