Как генерировать числа с учетом их основных факторов, но с неизвестными показателями?
Возможные дубликаты:
nth уродливый номер
Найти наименьшее число Kth для выражения (2 ^ x) * (3 ^ y) * (5 ^ z)
Мне интересно, как решить эту проблему быстрым и элегантным способом:
Мы определяем "уродливое" каждое число n, которое можно записать в виде: 2 ^ x * 3 ^ y * 5 ^ z;, где x, y и z - натуральные числа. Найдите 1500-е уродливое число.
например. первые "уродливые" цифры:
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, ...
Я попытался решить эту проблему с помощью грубой силы следующим образом:
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
Но это занимает довольно много времени, и я бы хотел найти более быстрое и лучшее решение.
Я знаю основные факторы уродливых чисел, но я не могу придумать способ генерации этих чисел в правильном порядке.
Я думаю, что должен быть способ генерировать эти числа без необходимости проверять все числа. Проблема состоит в том, что, по-видимому, экспоненты простых факторов распределены довольно случайным образом.
Посмотрите на эту таблицу:
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 |
------------------------
Как вы можете видеть, значения x, y и z, похоже, не следуют никакому правилу.
Может ли кто-нибудь из вас найти какое-либо решение этой проблемы?
Я собираюсь разделить проблему в разных частях.
Поскольку проблема определяется случайностью экспонентов, я мог бы попытаться независимо генерировать степени 2s, 3s, 5s, а затем числа вида 2 ^ x * 3 ^ y, 2 ^ x * 5 ^ z и т.д.
И, наконец, собрать их вместе, но я не знаю, решит ли это мою проблему.
Ответы
Ответ 1
Вот полное решение. O (n), он генерирует каждое число один раз и в порядке.
# 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
Здесь решение с использованием кучи. Идея состоит в том, что мы отслеживаем экспонентов вместе с уродливым продуктом. Для каждой итерации алгоритм выполняет до 3 операций find_min и 3 операции вставки. Параметры find_mins могут быть избыточными, поскольку вы можете получить их каждый, добавив один к любому экспоненте, и есть три показателя. Мы вставляем три раза, потому что добавляем один к каждому экспоненту и вставляем его в кучу. Чтобы найти n-е уродливое число, мы должны выполнить N операций, которые являются 6 * O (log n), поэтому алгоритм имеет временную сложность O (n log n). Сама куча, поскольку она может расти только на постоянную величину для каждой итерации, - это 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
используйте список (n в следующем коде), чтобы сохранить все предыдущие уродливые числа, следующее уродливое число - это минимальное число (n * 2, n * 3, n * 5), которое больше, чем 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]
Это решение O (n ^ 2), поэтому не пытайтесь использовать большое число.
Ответ 4
Я предлагаю вам решить эту проблему постепенно и сохранить все уродливые числа, которые вы найдете в списке.
При проверке номера для уродства тогда вам нужно только проверить, является ли это одним из ваших номеров раз 2, 3 или 5.
EDIT: я просто попытался реализовать это, как этот
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]
но этот подход безнадежно застаивается. Слишком наивный.:) Используйте своеобразное сито Эратосфена.