Это можно сделать с помощью метода многократного возведения в квадрат, , который равен O(log(k))
времени или O(log(k)log(m))
времени, если вы считаете m
переменной.
В общем случае a[n]=1+b+b^2+... b^(n-1) mod m
можно вычислить, заметив, что:
a[j+k]==b^{j}a[k]+a[j]
a[2n]==(b^n+1)a[n]
Второе просто является следствием для первого.
В вашем случае b=i^m
может быть вычислено за O(log m)
время.
Следующий код Python реализует это:
def geometric(n,b,m):
T=1
e=b%m
total = 0
while n>0:
if n&1==1:
total = (e*total + T)%m
T = ((e+1)*T)%m
e = (e*e)%m
n = n/2
//print '{} {} {}'.format(total,T,e)
return total
Этот бит магии имеет математическую причину - операция над парами, определенная как
(a,r)@(b,s)=(ab,as+r)
является ассоциативным, и правило 1 в основном означает, что:
(b,1)@(b,1)@... n times ... @(b,1)=(b^n,1+b+b^2+...+b^(n-1))
Повторный квадрат всегда работает, когда операции ассоциативны. В этом случае оператор @
равен O(log(m))
времени, поэтому повторное возведение в квадрат занимает O(log(n)log(m))
.
Один из способов взглянуть на это состоит в том, что матричное возведение в степень:
[[b,1],[0,1]]^n == [[b^n,1+b+...+b^(n-1))],[0,1]]
Вы можете использовать аналогичный метод для вычисления (a^n-b^n)/(a-b)
по модулю m
, поскольку матричное возведение в степень дает:
[[b,1],[0,a]]^n == [[b^n,a^(n-1)+a^(n-2)b+...+ab^(n-2)+b^(n-1)],[0,a^n]]