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

六眼飞鱼酱① 提交于 2019-12-20 17:57:06

问题


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.


回答1:


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)



回答2:


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



回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/7333657/how-to-generate-numbers-given-their-prime-factors-but-with-unknown-exponents

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!