快速幂

快速幂取模算法


        在网站上一直没有找到有关于快速幂算法的一个详细的描述和解释,这里,我给出快速幂算法的完整解释,用的是C语言,不同语言的读者只好换个位啦,毕竟读C的人较多~

        所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余)。在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快、计算范围更大的算法,产生了快速幂取模算法。我们先从简单的例子入手:求    = ?

算法1.首先直接地来设计这个算法:

[cpp]  view plain copy
  1. int ans = 1;  
  2. for (int i = 1;i <= b;i++)  
  3.     ans *= a;  
  4. ans %= c;  

这个算法的时间复杂度体现在for循环中,为O(b).这个算法存在着明显的问题,如果a和b过大,很容易就会溢出。那么,我们先来看看第一个改进方案:在讲这个方案之前,要先有这样一个公式:


这个公式大家在离散数学或者数论当中应该学过,不过这里为了方便大家的阅读,还是给出证明:

引理1:




上面公式为下面公式的引理,即积的取余等于取余的积的取余。



证明了以上的公式以后,我们可以先让a关于c取余,这样可以大大减少a的大小,

于是不用思考的进行了改进:

算法2:

[cpp]  view plain copy
  1. int ans = 1;  
  2. ans %= c;//加上这句  
  3. for (int i = 1;i <= b;i++)  
  4.     ans *= a;  
  5. ans %= c;  

聪明的读者应该可以想到,既然某个因子取余之后相乘再取余保持余数不变,那么新算得的ans也可以进行取余,所以得到比较良好的改进版本。

算法3:

[cpp]  view plain copy
  1. int ans = 1;  
  2. ans %= c;  
  3. for (int i = 1;i <= b;i++)  
  4.     ans = (ans*a) % c;//这里再取了一次余  
  5. ans %= c;  

这个算法在时间复杂度上没有改进,仍为O(b),不过已经好很多的,但是在c过大的条件下,还是很有可能超时,所以,我们推出以下的快速幂算法

快速幂算法依赖于以下明显的公式,我就不证明了。


我们可以看到,如果我们在上式中把a = a mod c;改成a=(a*a) mod c;便可以把时间复杂度变成O(b/2),当然,这样子治标不治本,但是,如果我们每次都把a平方一次取余,便可以显著减少要运算的次数,即进行以下的迭代:

形如上式的迭代下去后,当b=0时,所有的因子都已经相乘,算法结束。于是便可以在O(logb)的时间内完成了。于是,有了最终的算法:快速幂算法。

算法4:

[cpp]  view plain copy
  1. int ans = 1;  
  2. a %= c;  
  3. while (b){  
  4.     if (b % 2 == 1)  
  5.         ans = (ans*a) % c;  
  6.     b /= 2;  
  7.     a = (a*a) % c;  
  8. }  

将上述的代码结构化,也就是写成函数:

快速幂模板一:

[cpp]  view plain copy
  1. int PowerMod(int a,int b,int c)  
  2. {  
  3.     int ans = 1;  
  4.     a %= c;  
  5.     while (b){  
  6.         if (b % 2 == 1)  
  7.             ans = (ans*a) % c;  
  8.         b /= 2;  
  9.         a = (a*a) % c;  
  10.     }  
  11.     return ans;  
  12. }  

本算法的时间复杂度为O(logb),能在几乎所有的程序设计(竞赛)过程中通过,是目前最常用的算法之一。


快速幂模板二:

[cpp]  view plain copy
  1. int quickpow(int m,int n,int k)//计算 m^n % k  
  2. {  
  3.     int ans = 1;  
  4.     while (n){  
  5.         if (n & 1)  
  6.             ans = (ans*m)%k;  
  7.         n >>= 1;  
  8.         m = (m*m)%k;  
  9.     }  
  10.     return ans;  
  11. }  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值