快速幂算法:
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;
}