快速幂算法:

1,背景

给定三个整数a、b、m(a < 10^9, b < 10^6, 1< m < 10^9),求a^b%m

typedef long long LL;

LL pow(LL a, LL b, LL m){

    LL ans = 1;

    for (int i = 0; i < b; ++i) {

        ans = ans * a % m;

    }

    return  ans;

}


给定三个整数a、b、m(a < 10^9, b < 10^18,  1< m < 10^9),求a^b%m

这里用上面的做法显然是不行的,O(b)已经到了10^18

这里将使用快速幂算法


2,快速幂算法(递归)

a,如果b是奇数,那么有a^b = a* a^(b-1)

b,如果b是偶数,那么有a^b = a^(b/2)*a^(b/2)

这样,在log(b)级别次数的转换后,就可以把b转变为0,而任何正整数的0次方都是1

举个例子,如果计算2^10:

1)2^10———2^5, 2^10= 2^5*2^5

2)2^5———2^4,  2^5 = 2 * 2^4

3)2^4———2^2,  2^4 = 2^4 * 2*4

4)2^2———2^1,   2^2 = 2^1 *2^1

5)2^1———2^0,   2^1 = 2 * 2^0

6)2^0 = 1, 然后从下往上一次会退计算即可

这显然是递归的思想,于是可以得到快速幂的递归写法,时间复杂度为O(logb)

typedef long long LL;
LL binaryPow(LL a, LL b, LL m){
if(b == 0) return 1;
if(b % 2 == 1) //可以替换为 if(b&1)
return a * binaryPow(a, b - 1, m) % m;
else{
LL mu1 = binaryPow(a, b / 2, m);
/*不可直接返回binaryPow(a, b / 2, m)*binaryPow(a, b / 2, m)
因为会调用两次binaryPow函数,导致复杂度变为O(2^log(b)) = O(b)
*/
return mu1 * mu1 % m;
}
}


⚠️:

1)如果初始a有可能大于等于m,那么需要在进入函数前,就让a对m取模

2)如果m为1,可以直接在函数外部特判为0,不需要进入函数计算(因为任何正整数对1取模一定等于0)


3,快速幂算法(迭代)

对于a^b来说,如果把b写成二进制,那么b就可以写成若干次二次幂之和,

例如13的二进制1101,就可以写成13 = 2^3 + 2^2 +2^0 = 8 + 4 +1

所以a^13 = a^(8+4+1)=a^8 * a^4 * a^1

通过同样的方式,我们可以把任意的a^b表示为a^2k、。。。、a^8、a^4、a^2、a^1中若干项的乘积

步骤:

1)初始令ans为1,用来存放累计的结果

2)判断b的二进制末尾是否为1(即判断b&1 是否为1,也可以理解为判断b是否为奇数),如果是的话,令ans乘上a的值

3)令a平方,并将b右移一位(也可以理解为将b除以2)

4)只要b大于0,就返回2)

typedef long long LL;

LL binaryPow(LL a, LL b, LL m){

LL ans = 1;

while(b > 0){

if(b & 1){ //如果b的二进制末尾为1

ans = ans * a % m;//令ans累计a

}

a = a * a % m; //令a开平方

b >>= 1; //将b的二进制右移一位

}

return ans;
}