传统幂运算

  幂运算应该说是一种很简单的运算,在计算机中,可以通过循环手段实现。要计算$n^m$,需要进行m-1次乘法运算,所以时间复杂度为O(m)。

 1 #include <iostream>
 2 #define LL long long
 3 using namespace std;
 4 
 5 LL pow(LL base,LL pow){
 6     LL res = base;
 7     for(int i=1;i<pow;i++)
 8         res*=base;
 9     return res;
10 }

快速幂的原理

  当m较小时,O(m)的算法完全可以满足我们的要求,可是当m较大时,幂运算就可能无法满足我们的要求了。挑剔的计算机科学家想到,能不能在传统的幂运算上进一步优化呢?他们还真想到了办法,首先,幂运算在指数层面,执行的是加法运算,例如,计算$2^1000$,它的计算步骤是

$$2^{1} = 2^{0+1}\\ 2^{2} = 2^{1+1}\\2^{3} = 2^{2+1}\\...\\2^{1000} = 2^{999+1}$$

也就是说,$n^m$中的m,是在从0开始,逐步自增,最终达到m的。那么如果为这个自增过程加速,那么就可以降低整个算法的时间复杂度了。那么怎么增加速度呢,科学家们想到了用二进制做文章。我们可以将一个数转化为一个二进制,例如:

$$97 =(1100001)_2$$

在二进制中,一位上的数为1,代表该数存在对应的二进制整数,为0则不存在

$$97 = (1100001)_2 = 64+32+1 = (1000000)_2+(100000)_2+(1)_2$$

根据这种特征,科学家创造出了快速幂算法:

首先将指数m转化为二进制:

$$1000 = (1111101000)_2$$

则原值可以表示为:

$$2^{1000} = 2^{(1111101000)_2}$$

然后不断改变底数,就是不断让它和自己相乘:

$$2^1 = 2^{(1)_2}\\ 2^2 = 2^1*2^1=2^{(10)_2}\\2^4 = 2^2*2^2 = 2^{(100)_2}\\...$$

当底数自增时,每一次都判断当前这个数对应的指数在指数m的二进制表示中是否存在,如果存在,这将该底数和当前结果相乘,否则忽略。

代码如下:

#include<iostream>
#define LL long long
using namespace std;
LL mod = 1000;
LL fastPow(LL base,LL pow)
{
    LL res = 1;
    while(pow)
    {
        if(pow%2)
            res = (res*base)%mod;
        base =(base*base)%mod;
        pow/=2;
    }
    return res;
}
int main()
{
    LL c = fastPow(2,1000000000);
    printf("%lld",c);
    return 0;
}