今天在网上看了一些快速幂取模算法的介绍,总体感觉要么文章介绍的很简略,导致我搞了半天才搞明白什么意思,还有的文章直接放上了错误的代码,真是坑爹啊!所以我就特意写一篇文章方便大家理解一下这个算法的原理和代码是什么意思。原理介绍: 目标:快速求出 ab Mod c (注意:b是一个大数) 数学原理工具: (a*b) Mod c = [(a Mod c)*(b Mod c)] Mod c (加上括号是为了方便理解运算顺序,证明不难,可以自己百度) 考虑到b是一个大数,直接算 ab 会很慢,所以首先把 b 转换成二进制形式(这个转换不用再写一个程序,计算机里面就是二进制保持的)
b=(bnbn-1bn-2…b3b2b1b0)2 b=b0 + b1*21 + b2*22 + b3*23 + b4*24 +…+ bn-1*2n-1 + bn*2n ab = ab0+b1*2^1 + b2*2^2+ b3*2^3 + b4*2^4 +…+ bn-1*2^(n-1) + bn*2^n = ab0 * ab1*2^1 * ab2*2^2 * ab3*2^3 *…* abn*2^n ab % c = (ab0*ab1*2^1*ab2*2^2*ab3*2^3*…*abn*2^n) % c 设 An=(ab0*ab1*2^1*ab2*2^2*ab3*2^3*…*abn*2^n) % c 考虑到 (a*b) Mod c = [(a Mod c)*(b Mod c)] Mod c An=[(ab0*ab1*2^1*ab2*2^2*…*ab(n-1)*2^(n-1))%c*(abn*2^n)%c]%c =[ An-1* (abn*2^n) % c ] % c 为了方便这里设 Kn=(abn*2^n) % c 于是我们就得到一个递推关系
An = (An-1*Kn) % c A0 = ab0 % c 这里有一个问题就是Kn怎么求,考虑到Kn和bn有关,当bn取0时,Kn=1.当bn取1时,Kn = (a2^n) % c 所以关键是计算 (a2^n) % c 的值, 可以发现 (a2^n) % c = ( a2^(n-1)* a2^(n-1))% c = [ (a2^n-1) % c * (a2^n-1) % c ] % c 所以可以设 Tn= (a2^n) % c 那么可以得到
Tn= (a2^n) % c
= ( a2^(n-1)* a2^(n-1))% c = [(a2^n-1) % c * (a2^n-1) % c ] % c
= ( Tn-1 * Tn-1 ) % c 得到以下递推关系 Tn=( Tn-1 * Tn-1 ) % c
T0= a % c 现在总结以下,首先我们的目标是求出An,我们已经得到递推关系,但是在递推过程中我们还要Kn,所以我们得计算Kn,而Kn和bn有关,当bn=0,Kn=1,当bn=1,Kn=Tn,考虑到我们可能在任何地方需要Tn,因为b的二进制的1的位置是都有可能的,所以我们需要一直计算Tn,就是说当bn=0时我们也是要计算Tn的,因为可能以后会用到,而求Tn我们也已经给出了递推式,所以问题可以解决了。下面看一下代码
代码: int quick(int a,int b,int c)
{
int A=1; //结果的保存,就是An,初始化一下
T=a%c; //首先计算T0的值,用于Tn的递推
while(b!=0)
{
//这个if是判断目前最右边的一位bn是不是1,如果是1,那么Kn=Tn直接用Tn递推,具体看上面原理,如果bn=0,那么Kn=1,考虑到An-1是小于c的,所以 An=(An-1)%c =An-1 就是说可以不用计算了 因为相当于直接 A=A
if(b&1) {
A = ( A * T ) % c;
}
b>>=1; //二进制位移,相当于从右到左读取位b0 b1 b2 b3 b4等等
T=(T*T)%c; //更新T,如果下一位是1就可以用这个算A,具体的可以看上面原理的递推关系
}
return A;
}