Проект Эйлера, 12 задача. Программа выводит пустой результат после того как число делителей превышает 90
Всем привет. Пытаюсь решить 12 задачу из Проекта Эйлера https://projecteuler.net/problem=12.
Ожидается, что программа выведет triangle, как только количество count его делителей достигнет определенного значения. Написал такой код:
triangle = 0
c = 1
count = 0
while count < 91:
count = 0
triangle += c
c += 1
for i in range(1, triangle + 1):
if triangle % i == 0:
count += 1
if count == 90:
print(triangle)
break
Пока count меньше 91 результат выводится корректно, когда count становится равным 91 или большему числу, то программа работает, но не выводит triangle. В чем заключается проблема?
Ответы (2 шт):
Ваш код работает правильно, и проблема вероятнее всего в том, что количество вычислений очень велико, поэтому после 90 делителей ваша программа либо падает, либо решает очень долго.
Попробуйте погуглить оптимальные решения задачи по нахождению количества делителей, они помогут вам сократить количество вычислений. Я предлагаю вам такую оптимизацию: не проверять делимость после triangle/2, так как известно, что x после x/2 натуральных делителей иметь не может. Так вы сокращаете количество вычислений вдвое. На 500 не пробовал, попробовал на 200, через две минуты решилось.
DIVIDERS_COUNT = 200 # количество делителей искомого числа
triangle = 0 #треугольные числа
c = 1 # переменная для натуральных чисел
count = 1 # счетчик количества делителей
while count < DIVIDERS_COUNT:
count = 1 # счетчику сразу присваивается 1, так как мы не будем считать triangle
triangle += c
for i in range(1, int(triangle/2) + 1 ):
if triangle % i == 0:
count += 1
c += 1
print(triangle)
Проблема в условии while count < 91:. Треугольное число T224 = 25200 имеет ровно 90 делителей. Следующее треугольное число с большим количеством делителей – T384 = 73920. У него 112 делителей. Вообще количество делителей числа очень любит само состоять из большого количества маленьких множителей. Например 90 = 2·3·3·5, 112 = 2·2·2·2·7. А если вы захотите отыскать треугольное число с 91 множителем ровно, вам придётся ждать долго, возможно, бесконечно долго.
Если заменить условие на while True:, ваша программа отыщет T384 быстрее чем за секунду.
Для запуска программы с разными параметрами я её переделал:
def tau(n):
c = 0
for i in range(1, n + 1):
if n % i == 0:
c += 1
return c
k = int(input())
i = 1
while True:
t_i = i * (i + 1) // 2
count = tau(t_i)
if count >= k:
print(i, t_i, count)
break
i += 1
Пользоваться так: на входе желаемое число делителей, на выходе индекс i, само Ti и количество делителей τ(Ti). Чтобы отыскать следующее число с большим количеством делителей, подайте на вход число τ(Ti) + 1. Получится цепочка вроде:
| n | 1 | 2 | 3 | 5 | 7 | 10 | 17 | 19 | 21 | 25 | 37 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| i | 1 | 2 | 3 | 7 | 8 | 15 | 24 | 32 | 35 | 63 | 80 |
| Ti | 1 | 3 | 6 | 28 | 36 | 120 | 300 | 528 | 630 | 2016 | 3240 |
| τ(Ti) | 1 | 2 | 4 | 6 | 9 | 16 | 18 | 20 | 24 | 36 | 40 |
| n | 41 | 49 | 91 | 113 | 129 | 145 | 163 | 169 | ... |
|---|---|---|---|---|---|---|---|---|---|
| i | 104 | 224 | 384 | 560 | 935 | 1224 | 1664 | 1728 | ... |
| Ti | 5460 | 25200 | 73920 | 157080 | 437580 | 749700 | 1385280 | 1493856 | ... |
| τ(Ti) | 48 | 90 | 112 | 128 | 144 | 162 | 168 | 192 | ... |
Чем дальше, тем дольше ждать следующее число. В конце таблицы время подбирается к минуте на новое число. А двигаться далеко, нужно > 500, мы добрались до 192. Нужна оптимизация.
Процедура tau(n) вычисляет количество делителей числа n: τ(n). Подробности в статье Функция делителей. τ(n) мультипликативна. Треугольное число вычисляется как: Ti = i(i + 1)/2. Множители i и i + 1 взаимнопросты, а тогда:
- если i = 2k, то k и 2k + 1 взаимнопросты и τ(Ti) = τ(k(2k + 1)) = τ(k)τ(2k + 1);
- если i = 2k + 1, то 2k + 1 и k + 1 взаимнопросты и τ(Ti) = τ((2k + 1)(k + 1)) = τ(2k + 1)τ(k + 1).
Время работы tau(n) пропорционально n, следовательно такое разбиение может серъёзно сократить время поиска: одно n заменяется на два сомножителя порядка √n. В конце таблицы мы перевалили через миллион, можно ожидать ускорения в тысячу раз. Код:
def tau(n):
c = 0
for i in range(1, n + 1):
if n % i == 0:
c += 1
return c
k = int(input())
i = 1
while True:
f1 = i
f2 = i + 1
if f1 % 2 == 0:
f1 //= 2
else:
f2 //= 2
count = tau(f1) * tau(f2)
if count >= k:
print(i, f1 * f2, count)
break
i += 1
И продолжение таблицы:
| n | 193 | 241 | 321 | 481 |
|---|---|---|---|---|
| i | 2015 | 2079 | 5984 | 12375 |
| Ti | 2031120 | 2162160 | 17907120 | 76576500 |
| τ(Ti) | 240 | 320 | 480 | 576 |
Задача решена. Новая программа тратит на неё меньше семи секунд:
$ time echo 500 | python pe12.py 12375 76576500 576 real 0m6.361s user 0m6.356s sys 0m0.004s
P.S. Можно улучшить процедуру tau(n), перебирать множители только до √n это ещё ускорит поиск. Можно считать число делителей по разложению n на простые множители, и это тоже ускорит поиск. Я этого не делаю, довольно того что задача номер двеннадцать решена сравнительно просто и можно переходить к следующей. Их там много.