快速幂取模算法
Posted 王陸
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速幂取模算法相关的知识,希望对你有一定的参考价值。
1.大数模幂运算的缺陷:
2.快速幂的引入:
1.朴素模幂运算过程:
1 #define ans=1 2 for(int i=1;i<=b;i++) 3 { 4 ans*=a; 5 }
2.快速幂引入:
1 from time import * 2 def orginal_algorithm(a,b,c): #a^b%c 3 ans=1 4 a=a%c #预处理,防止出现a比c大的情况 5 for i in range(b): 6 ans=(ans*a)%c 7 return ans 8 9 def quick_algorithm(a,b,c): 10 a=a%c 11 ans=1 12 #这里我们不需要考虑b<0,因为分数没有取模运算 13 while b!=0: 14 if b&1: 15 ans=(ans*a)%c 16 b>>=1 17 a=(a*a)%c 18 return ans 19 20 time=clock() 21 a=eval(input("底数:")) 22 b=eval(input("指数:")) 23 c=eval(input("模:")) 24 print("朴素算法结果%d"%(orginal_algorithm(a,b,c))) 25 print("朴素算法耗时:%f"%(clock()-time)) 26 time=clock() 27 print("快速幂算法结果%d"%(quick_algorithm(a,b,c))) 28 print("快速幂算法耗时:%f"%(clock()-time))
我们现在知道了快速幂取模算法的强大了,我们现在来看核心原理:
对于任何一个整数的模幂运算
a^b%c
对于b我们可以拆成二进制的形式
b=b0+b1*2+b2*2^2+...+bn*2^n
这里我们的b0对应的是b二进制的第一位
那么我们的a^b运算就可以拆解成
a^b0*a^b1*2*1...*a^(bn*2^n)
对于b来说,二进制位不是0就是1,那么对于bx为0的项我们的计算结果是1就不用考虑了,我们真正想要的其实是b的非0二进制位
那么假设除去了b的0的二进制位之后我们得到的式子是
a^(bx*2^x)*...*a(bn*2^n)
这里我们再应用我们一开始提到的公式,那么我们的a^b%c运算就可以转化为
(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)
这样的话,我们就很接近快速幂的本质了
(a^(bx*2^x)%c)*...*(a^(bn*2^n)%c)
我们会发现令
A1=(a^(bx*2^x)%c)
...
An=(a^(bn*2^n)%c)
这样的话,An始终是A(n-1)的平方倍(当然加进去了取模匀速那),依次递推
现在,我们基本的内容都已经了解到了,现在我们来考虑实现它:
1 int fast_pow(int a,int b,int c) 2 { 3 int ans=1; ///记录结果 4 a=a%c; ///预处理,使得a处于c的数据范围之下 5 while(b!=0) 6 { 7 if(b&1) 8 { 9 ans=(ans*a)%c; ///如果b的二进制位不是0,那么我们的结果是要参与运算的 10 } 11 b>>=1; ///二进制的移位操作,不断的遍历b的二进制位 12 a=(a*a)%c; ///不断的加倍 13 } 14 return ans; 15 }
在这里还要进行几点说明:
1.二进制的几个运算符& 和 >> 。
&运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶,x&1==0为偶,x&1==1为奇。
>>运算比较单纯,二进制去掉最后一位,移位操作,不断遍历b的二进制位。
2. a=(a*a)%c这一步的作用是用来不断的加倍,在先不看同余定理的情况下,a*a==a^2,下一步再乘,就是a^2*a^2==a^4,然后同理 a^4 * a4 =a^8 .........?是不是做到了
a-->a^2-->a^4-->a^8-->a^16-->a^32.......指数正是 2^i 啊,再看上面的例子,a¹¹ = a^(2^0) * a^(2^1) * a^(2^3),这三项是不是完美解决了,快速幂就是这样。
以上是关于快速幂取模算法的主要内容,如果未能解决你的问题,请参考以下文章