Как найти число у которого 5000 делителей?
Необходимо найти наименьшее число, имеющее 5000 делителей. Я порыскал по интернету, нашёл самый(вроде бы) оптимизированный алгоритм на python,написал такой код:
from sympy import factorint
from time import time
def count_divisors(n):
factors = factorint(n)
count = 1
for factor in factors.values():
count *= (factor + 1)
return count
a = 1000000
t0 = 0
t1 = time()
while True:
if count_divisors(a) == 5000:
print(f"5000 делителей у числа {a}")
break
a += 1
t2 = time()
if t2 - t1 > 60:
t0 += 1
print(f"Работаем {t0} минут. Текущее число - {a}")
t1 = time()
time добавил, чтобы не свихнуться от неизвестности. Проблема в том, что код работает уже почти 3 часа, а число дошло до 213 миллиардов, но пока глухо. А дальше каждое число будет тратить всё больше и больше ресурсов, пока комп не зависнет. Кто-нибудь знает, как сделать обратную функцию(дабы искала число по количеству делителей) или хоть сократить диапазон поиска? Upd: 5000 любых делителей, так что 1 и само число также подходят
Ответы (2 шт):
Получается вот что. Поскольку теория чисел говорит нам, что число, которое разлагается на простые множители в виде 2^p*3^q*5^r...
, имеет число делителей
num_divisors = (p+1)*(q+1)*(r+1)*...
то мы можем разложить число num_divisors=5000 = 2*2*2*5*5*5*5
на множители всем возможными способами (их немало, но обозримое количество), и для каждого разложения посчитать результат:
primes = [2,3,5,7,11,13,17]
minn = -1
def fmx(lst):
num = 1
global minn
for i, p in enumerate(lst):
num *= primes[i]**(p-1)
if minn == -1:
minn = num
else:
minn = min(num, minn)
def f5000(lst=[], p2=3, p5=4):
if p2+p5==0:
fmx(lst)
return
for i in range(p2+1):
for j in range(p5+1):
if i+j:
f5000(lst + [2**i * 5**j], p2-i, p5-j)
f5000()
print(minn)
Результат
>>> 4727833110000
2^4 * 3^4 * 5^4 * 7^4 * 11 * 13 * 17
Если вы ищете минимальное число, у которого ровно 5000 делителей, то посмотрите соседний ответ. Или посмотрите раздел = 5000 ниже в этом ответе. А пока
≥ 5000
В статье Сверхсоставное число описаны числа, которые имеют число делителей, большее чем у любого предыдущего. Наша задача – найти первое сверхсоставное число с количеством делителей больше заданного порога.
Сверхсоставные числа имеют вид
∏i pici, где
pi – последовательные простые числа,
ci – невозрастающая последовательность степеней.
Произведение конечно, так как начиная с некоторого i ci становится равным нулю.
Число делителей равно d(∏i pici) = ∏i (ci + 1). (статья Функция делителей).
Наша задача перебрать все невозрастающие последовательности ci, у которых ∏i (ci + 1) больше заданного порога, и выбрать из них одну с минимальным ∏i pici.
Ещё понадобится свойство монотонности: если в ci увеличить любой элемент на единицу, увеличатся и произведение и число делителей. Всё, этого достаточно чтобы написать код.
is_prime
и next_prime
отыскивают последовательные простые. Прошу прощения за их медлительность. В нашей задаче большие простые числа не понадобятся, а так код проще.
search
отыскивает минимальное сверхсоставное число с нужным количеством делителей. Поиск рекурсивный. Поддерживается последовательность степеней c
, количество делителей n_divs
и само число-кандидат product
. least_product
хранит минимального кандидата, что ускоряет поиск (выше было про монотонность).
def is_prime(n):
return all(n % d != 0 for d in range(2, n - 1))
def next_prime(n):
while not is_prime(n):
n += 1
return n
def search(target):
least_product = float('inf')
p = [1]
c = [target - 1]
def search(i, n_divs, product):
nonlocal least_product
if i >= len(p):
p.append(next_prime(p[-1] + 1))
c.append(0)
if product >= least_product:
return
if n_divs >= target:
if product < least_product:
least_product = product
if c[i] > 0:
search(i + 1, n_divs, product)
if c[i - 1] > c[i]:
c[i] += 1
search(i, n_divs // c[i] * (c[i] + 1), product * p[i])
c[i] -= 1
search(1, 1, 1)
return least_product
print(search(int(input())))
Почти мгновенно можно отыскать ответ для пяти тысяч и восемь секунд нужно для миллиарда:
$ time echo 5000 | python highly-composite-number.py 293318625600 real 0m0.036s user 0m0.024s sys 0m0.008s $ time echo 1000000000 | python highly-composite-number.py 474386058264023040500951689662949694304000 real 0m8.062s user 0m8.056s sys 0m0.004s
Поиск устроен так чтобы быстро построить праймориал с нужным количеством делителей. Сам он обычно не является минимальным ответом, но даёт хорошее ограничение сверху. Для 5000 это:
304250263527210 = 2·3·5·7·11·13·17·19·23·29·31·37·41
У него 8192 (= 213) делителей, а вектор степеней ci состоит из триннадцати единиц.
Это не лучшее решение. Чтобы отыскать минимум, поиск проверяет ещё около девяти тысяч векторов. Вот некоторые, в порядке улучшения разультата:
кандидат | разложение | число делителей |
---|---|---|
304250263527210 | 2·3·5·7·11·13·17·19·23·29·31·37·41 | 8192 |
14841476269620 | 22·3·5·7·11·13·17·19·23·29·31·37 | 6144 |
6016814703900 | 22·32·52·7·11·13·17·19·23·29·31 | 6912 |
1358635578300 | 22·32·52·72·11·13·17·19·23·29 | 5184 |
1030689059400 | 23·32·52·72·112·13·17·19·23 | 5184 |
776363187600 | 24·32·52·7·11·13·17·19·23·29 | 5760 |
465817912560 | 24·33·5·7·11·13·17·19·23·29 | 5120 |
401567166000 | 24·33·53·7·11·13·17·19·23 | 5120 |
374796021600 | 25·32·52·72·11·13·17·19·23 | 5184 |
321253732800 | 26·33·52·7·11·13·17·19·23 | 5376 |
293318625600 | 26·34·52·72·11·13·17·19 | 5040 |
= 5000
Чтобы перебрать все числа, имеющие ровно 5000 делителей, нужно разложить 5000 во всевозможные произведения и для каждого произведения составить число с пятью тысячами делителей. Например:
5000 = | соответствующее число с пятью тысячами делителей |
---|---|
= 5000 | pi4999 |
= 625·8 | pi624pj7 |
= 5·5·5·5·2·2·2 | pi4pj4pk4pl4pmpnpo |
Любое число вида pi624pj7, где pi и pj различные простые, будет иметь ровно 5000 делителей. Рецепт прост: различные простые возводятся в степени на единицу меньшие множителей из разложения.
Так как мы ищем минимальные кандидаты, то простые будем брать из начала ряда и расположим множители по убыванию. Всего у пяти тысяч – 57 разложений.
Код не очень эффективный, зато простой:
def is_prime(n):
return all(n % d != 0 for d in range(2, n - 1))
def next_prime(n):
while not is_prime(n):
n += 1
return n
def search(target):
p = [2]
def factorizations(limit, n):
if n == 1:
yield ()
return
for d in range(2, limit + 1)[::-1]:
if n % d == 0:
for f in factorizations(d, n // d):
yield (d, ) + f
def candidates():
nonlocal p
for f in factorizations(target, target):
while len(p) < len(f):
p.append(next_prime(p[-1] + 1))
r = 1
for p_i, f_i in zip(p, f):
r *= p_i ** (f_i - 1)
yield r
return min(candidates())
print(search(int(input())))
Для 5000 считает мгновенно. Для миллиарда надо подождать три минуты:
$ time echo 5000 | python highly-composite-number.py 4727833110000 real 0m0.030s user 0m0.024s sys 0m0.004s $ time echo 1000000000 | python highly-composite-number.py 683175381189232829757128698827279802247046720000 real 2m39.379s user 2m38.400s sys 0m0.804s