An ugly number is a positive integer that is divisible by a
, b
, or c
.
Given four integers n
, a
, b
, and c
, return the nth
ugly number.
Example 1:
Input: n = 3, a = 2, b = 3, c = 5 Output: 4 Explanation: The ugly numbers are 2, 3, 4, 5, 6, 8, 9, 10... The 3rd is 4.
Example 2:
Input: n = 4, a = 2, b = 3, c = 4 Output: 6 Explanation: The ugly numbers are 2, 3, 4, 6, 8, 9, 10, 12... The 4th is 6.
Example 3:
Input: n = 5, a = 2, b = 11, c = 13 Output: 10 Explanation: The ugly numbers are 2, 4, 6, 8, 10, 11, 12, 13... The 5th is 10.
Constraints:
1 <= n, a, b, c <= 109
1 <= a * b * c <= 1018
[1, 2 * 109]
.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 checking every number, one by one, until we've found enough ugly numbers. We systematically test each number to see if it meets the 'ugly number' criteria.
Here's how the algorithm would work step-by-step:
def nth_ugly_number_brute_force(nth, first_prime, second_prime, third_prime):
ugly_number_count = 0
current_number = 0
while ugly_number_count < nth:
current_number += 1
temporary_number = current_number
# Repeatedly divide by each prime
# factor until it's no longer divisible.
while temporary_number % first_prime == 0:
temporary_number //= first_prime
while temporary_number % second_prime == 0:
temporary_number //= second_prime
while temporary_number % third_prime == 0:
temporary_number //= third_prime
# If the final result is 1, it's an ugly number
if temporary_number == 1:
ugly_number_count += 1
return current_number
We need to find the nth number that's divisible by at least one of a given set of numbers. The trick is to efficiently count how many such numbers exist below a certain value, and then use this counting method to narrow down our search.
Here's how the algorithm would work step-by-step:
def ugly_number_iii(n, first_divisor, second_divisor, third_divisor):
def count_ugly_numbers(value):
# Use inclusion-exclusion principle.
count = value // first_divisor + value // second_divisor + value // third_divisor
count -= value // least_common_multiple(first_divisor, second_divisor)
count -= value // least_common_multiple(first_divisor, third_divisor)
count -= value // least_common_multiple(second_divisor, third_divisor)
count += value // least_common_multiple(first_divisor, least_common_multiple(second_divisor, third_divisor))
return count
def least_common_multiple(first_number, second_number):
return (first_number * second_number) // greatest_common_divisor(first_number, second_number)
def greatest_common_divisor(first_number, second_number):
while second_number:
first_number, second_number = second_number, first_number % second_number
return first_number
low_value = 1
high_value = 2 * 10**9
while low_value < high_value:
mid_value = low_value + (high_value - low_value) // 2
# Count numbers divisible by at least one divisor
count = count_ugly_numbers(mid_value)
if count < n:
low_value = mid_value + 1 # Guess was too low
else:
high_value = mid_value # Guess was too high, try lower
# Binary search converges, 'low_value' is the nth ugly number
return low_value
Case | How to Handle |
---|---|
n is 0 or negative | Return 0 or throw an exception, as there's no 0th or negative ugly number. |
a, b, or c is 0 | Division by zero error needs to be prevented, either by returning an appropriate error message or treating them as 1 since any number is divisible by 1. |
n is a very large number (e.g., close to the maximum integer value) | The nth ugly number could be very large, potentially leading to integer overflow; use long integers to avoid this issue. |
a, b, and c are all large prime numbers | The numbers will grow very quickly, potentially leading to inefficiencies if a binary search is used and the search space is too large, optimize the initial range of the binary search. |
a, b, and c are all equal to 1 | The first ugly number is 1, and all subsequent ugly numbers are also 1; return n. |
a, b, and c have large common factors | The inclusion-exclusion principle should be used carefully to handle potential integer overflows during LCM calculations; use long data types for LCM. |
n is 1 | The smallest of a, b, and c will be the first ugly number; return min(a,b,c). |
a, b, and c are all very small (e.g., 2, 3, 5) | The nth ugly number will not be very large, and the solution should perform efficiently, ensuring the binary search range is reasonably bounded to start. |