Possible Duplicates:
nth ugly number
Find the Kth least number for expression (2^x)*(3^y)*(5^z)
I'm wondering how to solve this problem in a fast and elegant way:
We define "ugly" every number n which can be written in the form: 2^x * 3^y * 5^z;, where x,y and z are natural numbers. Find the 1500th ugly number.
E.g. the first "ugly" numbers are:
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, ...
I've tried to solve this problem using brute-force, in this way:
import itertools as it
def is_ugly(n):
'''Return `True` if *n* is an ugly number.'''
if n == 1:
return True
while not n % 2:
n //= 2
while not n % 3:
n //= 3
while not n % 5:
n //= 5
return n == 1
def nth_ugly(n):
'''Return the nth ugly number.'''
num = 0
for i in it.count(1):
if is_ugly(i):
num += 1
if num == n:
return i
But it takes quite a lot of time, and I'd like to find a faster and better solution.
I know the prime factors of ugly numbers, but I can't think of a way to generate these numbers following the correct order.
I think there must be a way to generate these numbers without having to check all the numbers. The problem is that it seems like exponents of the prime factors are distributed quite randomly.
Look at this table:
n |number| x | y | z |
------------------------
1 | 1 | 0 | 0 | 0 |
------------------------
2 | 2 | 1 | 0 | 0 |
------------------------
3 | 3 | 0 | 1 | 0 |
------------------------
4 | 4 | 2 | 0 | 0 |
------------------------
5 | 5 | 0 | 0 | 1 |
------------------------
6 | 6 | 1 | 1 | 0 |
------------------------
7 | 8 | 3 | 0 | 0 |
------------------------
8 | 9 | 0 | 2 | 0 |
------------------------
9 | 10 | 1 | 0 | 1 |
------------------------
10 | 12 | 2 | 1 | 0 |
------------------------
11 | 15 | 0 | 1 | 1 |
------------------------
12 | 16 | 4 | 0 | 0 |
------------------------
13 | 18 | 1 | 2 | 0 |
------------------------
14 | 20 | 2 | 0 | 1 |
------------------------
15 | 24 | 3 | 1 | 0 |
------------------------
As you can see x,y and z values don't seem to follow any rule.
Can someone of you find any solution to this problem?
I'm thinking of trying to divide the problem in different parts. Since the problem is determined by the randomness of exponents, I could try to generate independently the powers of 2s,3s,5s and then the numbers of the form 2^x*3^y,2^x*5^z etc. And finally put them together, but I don't know if this will solve my issue.
There are two common ways to perform prime factorization. The first is called the Prime Factor Tree, and the second is known as the Upside-Down Division.
Here is a complete solution. O(n) complexity, it generates every number once and in order.
# http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF
n = 15
bases = [2, 3, 5]
nums = [1] * n
candidates_indexes = [0 for _ in bases]
candidates = [base for base in bases]
for i in range(1, n):
nextn = min(candidates)
nums[i] = nextn
for index, val in enumerate(candidates):
if val == nextn:
candidates_indexes[index] += 1
candidates[index] = bases[index] * nums[candidates_indexes[index]]
print(nums)
Here's a solution using a heap. The idea is that we keep track of the exponents along with the ugly product. For each iteration, the algorithm performs up to 3 find_min operations and 3 insert operations, The find_mins can be redundant because you can get to each by adding one to any exponent, and there are three exponents. We do an insert three times because we add one to each exponent and insert that into the heap. To find the nth ugly number, we thus have to perform N operations that are 6 * O(log n), thus the algorithm has time complexity of O(n log n). The heap itself, since it can only grow by a constant amount for each iteration, is SPACE(O(n))
>>> import heapq, itertools
>>> class UglyGen(object):
... def __init__(self, x, y, z):
... self.x, self.y, self.z = x, y, z
... self.value = 2**self.x * 3**self.y * 5**self.z
... def incr(self):
... x, y, z = self.x, self.y, self.z
... return (UglyGen(x+1, y, z),
... UglyGen(x, y+1, z),
... UglyGen(x, y, z+1))
... def __lt__(self, other):
... if not isinstance(other, UglyGen):
... return NotImplemented
... return self.value < other.value
...
>>> def gen_ugly():
... gens = [UglyGen(0, 0, 0)]
... last = 0
... while gens:
... while gens[0].value == last:
... heapq.heappop(gens)
... top = heapq.heappop(gens)
... succ_items = top.incr()
... for succ in succ_items:
... heapq.heappush(gens, succ)
... last = top.value
... yield last
...
>>> def find_nth_ugly(n):
... for n0, u in itertools.izip(xrange(n), gen_ugly()):
... pass
... return u
...
>>> find_nth_ugly(15)
24
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With