开发者

how to generate numbers given their prime factors, but with unknown exponents? [duplicate]

开发者 https://www.devze.com 2023-04-03 18:32 出处:网络
This question already has answers here: Closed 11 years ago. Possible Duplicates: nth ugly number Find the Kth least number for expression (2^x)*(3^y)*(5^z)
This question already has answers here: Closed 11 years ago.

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 |
-----------------开发者_StackOverflow-------
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.


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


use a list (n in the following code) to store all the prev ugly numbers, the next ugly number is the min number of (n*2, n*3, n*5) that is larger then n[-1]:

n = [1]
while len(n) < 1500:
    n.append(min([next(x*s for x in n if x*s>n[-1]) for s in (2,3,5)]))    
print n[-1]

It's a O(n^2) solution, so don't try large number.


I suggest you solve this incrementally and store all the ugly numbers you find in a list.

When checking a number for ugliness then, you only have to check whether it's one of your number times 2, 3 or 5.

EDIT: I just tried to implement that like this

ugly = [1]
candidate = 2
while len(ugly) < 1500:
    for u in ugly:
        if any([(u * x == candidate) for x in (2, 3 ,5)]):
            ugly.append(candidate)
            break
    candidate += 1

print ugly[-1]

but this approach stagnates hopelessly. Too naive. :) Use some sort of Sieve of Eratosthenes rather.

0

精彩评论

暂无评论...
验证码 换一张
取 消