神奇的快速幂算法!!

来源:互联网 发布:不该 周杰伦 知乎 编辑:程序博客网 时间:2024/05/29 10:55

日常我们编写代码的时候可能经常会遇到要求一个数的多少次方的步骤,像C++里面虽然说提供了一个pow(int n,int m)的函数,但是用过的人都知道,它的精度损失是极其严重的(小编在一次比赛中就被这一点给坑了),而且其运行速度也是很慢的,并且时间复杂度比较高,是O(n)级别。

对于精度问题倒是好说,自己手写一个函数形参以及返回值定义为自己需要的类型就好了,这一点相信绝大多数人都能做到;然而对于时间复杂度的降低来减少运行时间,这可就有点技术挑战了。快速幂能做到O(logn),快了好多好多.在查看了相关资料学习了以后呢,我想在这里分享一下的的快速幂学习经验。

代码如下:

long long qpow(long long n, long long m) {          long long ans = 1;        while(m){            if( m & 1) ans = ans * n;            m >>= 1;            n = n * n;        }        return ans;    }  
 代码很是简单,大可以把它记住,要理解起来也是很简单的: 假设我们要求n^m,我们把m拆成二进制的,该二进制数第i位的权为2^(i-1),例如当m = 13时,13的二进制是1101B

         n^13 = n^(2^0+2^2+2^3)
                          
代码中用到了&运算和>>运算,&通常用于二进制取位操作,例如一个数m & 1 的结果就是取二进制m的最后一位,>> 就是左移一位了,可以实现去掉二进制最后一位。   

下面我们来手工实现一下代码的运行过程:假设我们要求5^13
首先定义ans = 1,用于存放结果的。先取二进制13的最后一位,我们得到了1,(1代表true,0代表false),因此进入if语句,ans就等于它本身乘以一次基数5,接下来的一步关键了,在m左移了一位之后,再做m & 1的运算相当于取二进制m的倒数第二位,这时它的权值就增大了,这个变化相当于基数翻倍,因此修改基数为上一步基数的平方,依次下去,ans 即为 5*1 25*1 125*0 15625*1 这四个数累乘得出。
还有值得注意的一点是指数爆炸,即可能算着算着就溢出了;得不到正确结果,虽然这里我没用 int 而用 long long 但也不能完全避免,如果具体题目要求结果对某个数取余啊可以稍作修改代码,例如结果对100000007取余:

if( m & 1) ans = ans * n % 100000007;            m >>= 1;            n = n * n % 100000007;
原创粉丝点击