Расчет очень большой мощности

Я хочу рассчитать очень большие числа, например n = 10 ^ 15.

Как-то не могу из-за OverflowError.

xd = lambda n : ((((5+ sqrt(17)) * ((3 + sqrt(17)) ** n)) - ((5-sqrt(17))* ((3 - sqrt(17)) ** n)))/((2 ** (n+1)) * sqrt(17)))

даже для n = 1000 он не будет рассчитан.

Хотя, я должен упомянуть, что мне нужен модульный (1000000007)

Какое было бы решение?


person Mikey Freeman    schedule 13.12.2020    source источник
comment
google modpow ... это намного быстрее modpow(a,b,c), чем pow(a,b) mod c, потому что вам не нужны большие числа ... Я видел некоторые люди (более молодые и на более новых языках, таких как JAVA или python) вместо этого используют имя powmod, однако IIRC должно быть modpow Так что просто возьмите власть в квадрат с помощью модульной арифметики ... как я сделал в связанной реализации modpow   -  person Spektre    schedule 13.12.2020
comment
@Spektre как насчет чисел с плавающей запятой? в моем случае (3 + sqrt (17)) ^ n. Это тоже modpow?   -  person Mikey Freeman    schedule 13.12.2020
comment
хм, это проблема, потому что это зависит от того, что вы считаете модом float?   -  person Spektre    schedule 13.12.2020
comment
вы можете легко использовать подход возведения в квадрат или log / exp для чисел с плавающей запятой и фиксированной запятой так же, как и для целых чисел, но мод особый случай ... поэтому, если вы усекаете свой float до int и thed mod, тогда да, если вы используете что-то вроде fmod, вам нужно умножить на число, которое вы по модулю, чтобы усечь до целого числа, а затем каким-то образом восстановить результат ...   -  person Spektre    schedule 13.12.2020
comment
@Spektre Я имею в виду, что мод не нужен, но я также не могу вычислить очень большое n для (3 + sqrt (17)) ^ n: - ??   -  person Mikey Freeman    schedule 13.12.2020
comment
Следовать этому. Надеюсь, это будет эффективно stackoverflow.com/a/538583/5929910   -  person mhhabib    schedule 13.12.2020
comment
@toRex Спасибо, я видел это раньше, и я использую Python 3.8, но мои цифры очень-очень большие, я думаю, что он не может с этим справиться.   -  person Mikey Freeman    schedule 13.12.2020
comment
Возможно, вам следует немного объяснить обстоятельства, которые приводят к этой формуле, потому что это кажется немного странным: очевидно, вы хотите выполнять модульную арифметику, но здесь также есть (обязательно неточный) квадратный корень из 17, а 17 - это неквадратичный вычет по модулю 1000000007   -  person harold    schedule 13.12.2020
comment
@Spektre Спасибо, я буду искать мощность в квадрате: -?   -  person Mikey Freeman    schedule 13.12.2020
comment
@harold, это об этом. (Sn)   -  person Mikey Freeman    schedule 13.12.2020
comment
проблема в том, что реализация функции мощности на числах с плавающей запятой, скорее всего, использует подход log / exp, и вычислить, что на произвольных больших числах с плавающей запятой не так просто, поскольку LUT не может быть и речи ... если это нормально для результата, попробуйте усечь (3 + sqrt(17)) до целого числа x, а затем используйте x**n, который должен заставить использовать целочисленную мощность, что должно быть хорошо ... однако я не кодирую на python, поэтому я не знаю наверняка, насколько хорошо это реализовано. Вы также можете выполнить вычисление с фиксированной точкой, если вам нужно также несколько десятичных знаков после точки ...   -  person Spektre    schedule 13.12.2020
comment
@Spektre, который дал бы мне неправильный ответ (я думаю)   -  person Mikey Freeman    schedule 13.12.2020
comment
@Spektre об этом math.stackexchange.com/a/686374/842598 (часть Sn)   -  person Mikey Freeman    schedule 13.12.2020
comment
Повсюду используйте целочисленную арифметику: вы можете вычислить xd(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.2020
comment
@MikeyFreeman Хорошо, я рекомендую использовать другое математическое решение, например, на основе мощности матрицы (это, вероятно, возможно, но я не работал), которое не будет включать квадратные корни или деления   -  person harold    schedule 13.12.2020
comment
просто попробуйте, если он переполнится ... если нет, тогда сделайте что-то вроде a = (x*1000)**n; b =1000**n;, а затем конвертируйте в float y = a/b, который должен дать вам ваш ответ с ограниченной точностью, но все же лучше, чем одни целые числа ... 1000 можно увеличить .... до повысить точность   -  person Spektre    schedule 13.12.2020
comment
@MarkDickinson Спасибо, я думаю, это будет решение, я буду работать над этим.   -  person Mikey Freeman    schedule 13.12.2020
comment
@harold Да, я над этим поработаю. Спасибо   -  person Mikey Freeman    schedule 13.12.2020
comment
@Spektre Я попробую, спасибо   -  person Mikey Freeman    schedule 13.12.2020
comment
@MarkDickinson Извините, как вы пришли к [[3, 2], [1, 0]] или [4, 1]?   -  person Mikey Freeman    schedule 13.12.2020
comment
@MikeyFreeman: Если вы выразите xd(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
comment
xd (n + 1) = xd (n + 2) + xd (n + 1). Это Sn (xd = Sn), а не An (math.stackexchange.com/a/686374/842598). Или я не прав?   -  person Mikey Freeman    schedule 13.12.2020
comment
@MarkDickinson Нет, я ошибался, спасибо, я ценю вашу помощь   -  person Mikey Freeman    schedule 13.12.2020


Ответы (3)


Глядя на ответ на maths.stackexchange, откуда взялась формула, кажется что проще всего вычислить a (n).

Таким образом, это можно очень просто вычислить с помощью повторения, и на этот раз, поскольку мы используем только умножения и сложения, мы можем воспользоваться правилами арифметики по модулю и сохранить небольшие числа, которыми мы манипулируем:

def s(n, mod):
    a1 = 1
    a2 = 3
    for k in range(n-1):
        a1, a2 = a2, (3*a2 + 2* a1) % mod
    return (a1 + a2) % mod


mod = 1000000007

print(s(10, mod))
# 363314, as with the other formulas...

print(s(10**6, mod))
# 982192189

%timeit s(10**6, mod)
# 310 ms ± 6.46 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit s(10**7, mod)
# 3.39 s ± 93.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Мы получаем те же результаты, что и с другими формулами (что действительно хорошо ...). Поскольку числа, используемые во время вычисления, сохраняют тот же размер, не более чем в 5 раз больше по модулю, время вычисления составляет около O (n) - s(10**7) занимает всего в 10 раз больше времени, чем s(10**6).

person Thierry Lathuille    schedule 13.12.2020
comment
Вы абсолютно правы, что решение с точки зрения a[n] намного чище, чем работа с числами с плавающей запятой. Следует отметить, что любое линейное повторение допускает O(log n) решение. - person user58697; 13.12.2020

Поскольку значение n очень велико, переполнение целого числа очевидно.

Следуйте следующим правилам для модульной арифметики:

  1. Сложение: (a + b)% m = (a% m + b% m)% m
  2. Вычитание: (a-b)% m = (a% m + b% m + m)% m.
  3. Умножение: (a * b)% m = (a% m * b% m)% m
  4. Экспоненциальный: используйте цикл.

Пример: Для a ^ n используйте a = (a% m * a% m)% m, n количество раз.

Для больших значений n используйте функцию python pow (x, e, m), чтобы вычислить модуль, что занимает намного меньше времени.

person Deepak Tatyaji Ahire    schedule 13.12.2020
comment
это будет хорошо только для маленьких n в a^n, для больших показателей вам нужна мощность возведением в квадрат ... - person Spektre; 13.12.2020
comment
Правильный @Spektre. - person Deepak Tatyaji Ahire; 13.12.2020
comment
Ваш метод сработает, но очень медленно ... - person Spektre; 13.12.2020
comment
Однако OP пояснил, что в a^b a это float !!! поэтому необходима мощность с фиксированной или с плавающей запятой ... Я бы выбрал фиксированную, поскольку это можно сделать с целыми числами без пробников с произвольным журналом точности, exp, которые, на мой взгляд, являются причиной переполнения - person Spektre; 13.12.2020
comment
@Spektre, но OP использует принципиально неправильный подход к исходной проблеме, ни мощность с плавающей запятой, ни мощность с фиксированной запятой не решат ее - person harold; 13.12.2020

Рабочий способ вычислить его только с целыми числами - разработать выражение с использованием биномиального разложения. Немного изменив его порядок, мы получаем довольно простой способ его вычисления с почти идентичной формулой для членов четной и нечетной степени:

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
comment
Спасибо, я ценю ваше время и концентрацию. - person Mikey Freeman; 13.12.2020
comment
Я собираюсь использовать умножение матриц - person Mikey Freeman; 13.12.2020