初学 快速幂 的理解

  博客停了差不多三个月, 虽然这一段时间在学算法, 但从来没有写博客。 今天看了一上午的快速幂,突然想写写博客, 增加一下自己的记忆!这个博文知识简单介绍一下算法中取余的原因


 

 1 至于快速幂的概念不详细记录了。当我们想求a的b次幂对c取余时,我们会直接想到用这个算法:

int ans = 1;

  for( i  = 1; i <= b; i++)

  {

    ans = ans * a;

  }

  ans %= c;

  这个算法的时间复杂度体现在for循环中,为O(b).这个算法存在着明显的问题,如果a和b过大,很容易就会溢出。因此需要用到离散数学知识(该知识点我也没学过,度娘教的^_^)


 

 

  定理1:

  

 


 

 2 由上面的公式可以推出: 

    

     把a看成 a * 1, 故由定理1可得出上面的公式。由此得到改进版本:

  int ans = 1; 

  a = a % c; //加上这一句

  for(int i = 1;i<=b;i++)

  {   

  ans = (ans * a) % c;//这里再取了一次余 

  } 

  ans = ans % c;

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


 

3   快速幂算法依赖于以下明显的公式,我就不证明了,很简单理解。

  

    


4    由此得到改进版本:

  int ans = 1;

  a = a % c;

   if(b%2==1) 

  ans = (ans * a) mod c; //如果是奇数,要多求一步,可以提前算到ans中

  k = (a*a) % c; //我们取a2而不是a

  for(int i = 1;i<=b/2;i++)

  { 

    ans = (ans * k) % c;

  } 

  ans = ans % c;

 

 5  我们可以看到,我们把时间复杂度变成了O(b/2).当然,这样子治标不治本。但我们可以看到,当我们令k = (a * a) mod c时,

   状态已经发生了变化,我们所要求的最终结果即为(k)^b/2 mod c而不是原来的a^b mod c,所以我们发现这个过程是可以迭代下去的。

 ((迭代就是把这一次算的值作为下一次循环的初始值,所以可以更简化该算法))

   当然,对于奇数的情形会多出一项a mod c,所以为了完成迭代,当b是奇数时,我们通过  ans = (ans * a) % c;来弥补多出来的这一项,

   此时剩余的部分就可以进行迭代了。   形如上式的迭代下去后,当b=0时,所有的因子都已经相乘,算法结束。于是便可以在O(log b)的时间内完成了。

   于是,有了最终的算法:快速幂算法。

 ((说实话, 我对这个简化有点糊涂,关于在O(log b)的时间内就可以完成, 就假装懂了吧, 以后慢慢理解吧))


 6

 int ans = 1;

   a = a % c;

  while(b>0)

  {   

    if(b % 2 == 1) 

    ans = (ans * a) % c;

    b = b/2; 

    a = (a * a) % c; 

  } 

 


 

 

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

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;

  }

 

  


 

 

      这就是最后的优化代码了。以上内容是抄袭的, 只不过自己又写了一遍。如果看不懂我写的的可以点下面网址:

  http://wenku.baidu.com/link?url=PdtuRuYTZSIfAC3TZCiHSx3fk7cEn3yuZBiYwbx-b7h_TOxNyOQtNOaUepEmxw56jhnPePqAdebOH6QL- pvmhFCYYdzGhYRneUM_oZTpxwO

   有关于快速幂的算法的推导,还可以从另一个角度来想。我就不介绍了。

原文地址:https://www.cnblogs.com/lj-1568/p/4754336.html