绪论
说到求幂,很多有经验的程序员第一时间想到的或许都是math.h当中的pow()函数,刚入门的小白应该也能够想到累乘法一次一次地乘,似乎没什么问题
那么为什么我们还要再去研究快速幂算法呢?
常规的求幂的算法,不难看出,时间复杂度为O(N),看起来已经没有什么能够优化的地方了,但是我们依然能够感受得到,在N较大、计算项目多的时候,计算的时间往往会超出预期(尤其是OIer最担心的事情之一TLE往往会在这个时候出现)
于是我们还需要进一步将求幂的方法进行优化,因而就有了快速幂算法,时间复杂度为O(log2N),将我们计算所需的时间进一步的压缩
基本原理
(1)思路:类比二进制转化为十进制的过程
还记得在学习计算机导论的时候,我们是如何计算二进制数的吗?
我们拿二进制数1110010来举个例子:
1110010(2) = 1*26+1*25+1*24+0*23+0*22+1*21+0*20 = 114(10)
相信思维敏捷的读者已经猜到为什么快速幂的时间复杂度当中的底数为2了(手动滑稽)
没错,利用二进制数转化为十进制的方法,我们可以得到类似的思路来求快速幂:
an = a1+2+4+8+… = a1*a2*a4*a8*…
(由于CSDN编辑器过于蛋疼,做不了幂中幂,所以这里的式子暂且只好理想的假设n(10)=1111111…(2))
(2)位运算符:将幂以二进制数的方式进行运算
两个重要的位运算符:
1.&:按位与
利用这个运算符,我们可以比对幂的最后一位数与1是否相等,相等时表达式的值为1(true),反之为0(false)
先将n的值赋予临时变量j,并将n初始化为1(即0次幂)
每次迭代时,检查幂的最后一位,若是1,则将n乘以相应的a2的i次方(i为迭代次数)
每次迭代后临时变量j的值变为自身原值的二次方,以对应21->22->24->28->…
if(m&1)
n*=j;
j*=j;
2.>>:右移运算符
>>运算符是C/C++的位运算当中极为重要的一个运算符,我们都知道在计算机当中整型变量常以二进制形式储存,他的作用便是将左边整型数“向右移动右边整型数”位二进制数,并将其余位补0
利用这个运算符,在每一次迭代后我们都将幂右移一位,便能很好地实现我们快速幂算法的一个运算过程:
while(m)
{
...
m>>=1;
}
代码实现
因为快速幂是一个十分简单的算法,就不过多解析了,直接给出完整代码实现:
(如果实在理解不了,把上文和代码多看几遍,或者只记怎么写也可以)
//To calculate n^m in fast power
#include<iostream>
using namespace std;
int main(void)
{
unsigned long long int n,m,i,j;
cin>>n>>m;
j=n;
n=1;
while(m)
{
if(m&1)
n*=j;
j*=j;
m>>=1;
}
cout<<n<<endl;
}