快速幂取模详解与拓展
紫书上同余与模算术一节关于此问题一笔带过,讲的不是很详细。
本文原创,转载请注明出处 。:)
问题定义:
数论中经常出现的一个问题是对一个数的幂取模,也称为模取幂,即求a^b mod n。如果计算量较小,可以直接计算出a^b的值,再作模n运算。但是如果a和b的值都非常大,a^b的值用计算机难以表示,或者即使可以用大数运算的方式用计算机表示,也会因为耗时过长难以应用。基于模运算的基本性质,可以设计出一种算法,快速求解这一问题。这种方法为“快速幂取模”,也称为“反复平方法“。
利用公式a*b%n=((a%n)*b)%n=((a%n)*(b%n))%n,可以先解决a^b可能太大存不下的问题
d=ab mod n=(…((((a mod n)*a)mod n)*a)mod n…*a)mod n {共b个a}
由此可以引出一个迭代式
d=a;
for i=2 to b do d=d mod n*a;
d=d mod n;
时间复杂度为O(b),当b很大时,效率很低。
设b(i)指的是二进制下b的第i位,比如当b=13时,其二进制为1101,则b(2)表示0,b(1)表示1
b = b(k) * 2^k + b(k-1)*2^(k-1) + ... + b(1) * 2^1 +b(0)*2^0
那么
a ^ b = a ^ (b(k)*2^k + b(k-1)*2^(k-1) + ... + b(1)*2 + b(0) )
= [a ^ (b(k)*2^k)] * [a^(b(k-1)*2^(k-1))] *...*[a^(b(1)*2) ]* a
= p(k) * p(k-1) * ... * p(1) * p(0)
其中,p(i)=a ^ (b(i)*2^i),b(i)=1或是0,则p(i)=a ^ (2^i) 或者 p(i) = 1
注意,p(i)可以一步一步的由前一个生成,即当p(i)=a ^ (2^(i-1)*2*b(i)),当p(i-1)!=0,b(i)!=0时,p(i)=p(i-1)*2
那么此时结合公式a*b%n=((a%n)*b)%n
p(i)%n=((p(i-1)%n)*b)%n
a ^ b % n =( p(k) * p(k-1) * ... * p(1) * p(0) )%n
=(…((((p(k) mod n)*p(k))mod n)*p(k-1))mod n…*p(0))mod n
结合上述迭代式,得到如下算法:
int PowerMod(int a, int b, int c)
{
int ans = 1;
a = a % c;
while(b>0) {
if(b % 2 == 1)
ans = (ans * a) % c;
b = b/2;
a = (a * a) % c;
}
return ans;
}