Рабочий способ вычислить его только с целыми числами - разработать выражение с использованием биномиального разложения. Немного изменив его порядок, мы получаем довольно простой способ его вычисления с почти идентичной формулой для членов четной и нечетной степени:
def form(n, mod):
cnk = 1
total = 0
for k in range(n+1):
term = cnk * 3**k * 17**((n-k)//2)
if (n-k) % 2 == 1:
term *= 5
total += term
cnk *= (n-k)
cnk //= (k+1)
return (total // (2**n)) #% mod
Мы можем сравнить его с вашей исходной формулой, чтобы проверить результаты:
from math import sqrt
def orig(n):
return ((((5+ sqrt(17)) * ((3 + sqrt(17)) ** n)) - ((5-sqrt(17))* ((3 - sqrt(17)) ** n)))/((2 ** (n+1)) * sqrt(17)))
for n in range(20):
print(n, orig(n), form(n, mod))
Выход:
0 1.0000000000000002 1
1 4.0 4
2 14.000000000000002 14
3 50.0 50
4 178.0 178
5 634.0000000000001 634
6 2258.0 2258
7 8042.0 8042
8 28642.000000000004 28642
9 102010.00000000001 102010
10 363314.0 363314
11 1293962.0000000002 1293962
12 4608514.0 4608514
13 16413466.000000004 16413466
14 58457426.00000001 58457426
15 208199210.00000003 208199210
16 741512482.0000001 741512482
17 2640935866.000001 2640935866
18 9405832562.0 9405832562
19 33499369418.000004 33499369418
Это довольно быстро для небольших значений n (проверено на старой машине):
#%timeit form(1000, mod)
# 9.34 ms ± 87.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
#%timeit form(10000, mod)
# 3.79 s ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#%timeit form(20000, mod)
# 23.6 s ± 37.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Для последнего теста, перед тем как взять модуль, у нас есть число из 11033 цифр.
Основная проблема с этим подходом заключается в том, что, поскольку мы должны разделить на 2 ** n в конце, мы не можем брать по модулю на каждом шаге и держать числа, которыми мы манипулируем, маленькими.
Однако использование предложенного подхода с умножением матриц (я не видел ссылки на формулу рекурсии, когда начинал с этого ответа, очень плохо!) Позволит вам это сделать.
person
Thierry Lathuille
schedule
13.12.2020
modpow(a,b,c)
, чемpow(a,b) mod c
, потому что вам не нужны большие числа ... Я видел некоторые люди (более молодые и на более новых языках, таких как JAVA или python) вместо этого используют имяpowmod
, однако IIRC должно бытьmodpow
Так что просто возьмите власть в квадрат с помощью модульной арифметики ... как я сделал в связанной реализацииmodpow
- person Spektre   schedule 13.12.2020(3 + sqrt(17))
до целого числаx
, а затем используйтеx**n
, который должен заставить использовать целочисленную мощность, что должно быть хорошо ... однако я не кодирую на python, поэтому я не знаю наверняка, насколько хорошо это реализовано. Вы также можете выполнить вычисление с фиксированной точкой, если вам нужно также несколько десятичных знаков после точки ... - person Spektre   schedule 13.12.2020xd(n)
, найдяn
-ю степень целочисленной матрицы 2 на 2[[3, 2], [1, 0]]
и умножив на вектор[4, 1]
, что даст вамxd(n+1)
вместе сxd(n)
. Чтобы вычислить n-ю степень матрицы эффективно по модулю1000000007
, используйте стандартный алгоритм модульного возведения в степень (но применяется к целочисленным матрицам 2 на 2, а не к простым целым числам). - person Mark Dickinson   schedule 13.12.2020a = (x*1000)**n; b =1000**n;
, а затем конвертируйте в floaty = a/b
, который должен дать вам ваш ответ с ограниченной точностью, но все же лучше, чем одни целые числа ...1000
можно увеличить .... до повысить точность - person Spektre   schedule 13.12.2020[[3, 2], [1, 0]]
или[4, 1]
? - person Mikey Freeman   schedule 13.12.2020xd(n+1)
иxd(n)
в терминахxd(n)
иxd(n-1)
, вы получите матричное уравнение:[xd(n+1), xd(n)] = [[3, 2], [1, 0]] * [xd(n), xd(n-1)]
(неудобно писать в комментарии - думайте о векторах как о векторах столбцов здесь). Верхняя строка матрицы получается из рекуррентного отношенияxd(n+1) = 3*xd(n) + 2*xd(n-1)
; в нижнем ряду просто указан идентификаторxd(n) = 1*xd(n) + 0*xd(n-1)
. Начальный вектор[4, 1]
равен[xd(1), xd[0]]
. - person Mark Dickinson   schedule 13.12.2020