Как найти число у которого 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 шт):

Автор решения: MBo

Получается вот что. Поскольку теория чисел говорит нам, что число, которое разлагается на простые множители в виде 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
→ Ссылка
Автор решения: Stanislav Volodarskiy

Если вы ищете минимальное число, у которого ровно 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
→ Ссылка