快速幂的一般格式为:给定三个正整数
a
a
a、
b
b
b、
m
m
m,求
a
b
%
m
a^b\%m
ab%m。
如果按照时间复杂度为
O
(
b
)
O(b)
O(b)的写法,在
b
b
b 超过
1
0
8
10^8
108时就很容易超时,因此需要引入快速幂。
递归写法
快速幂基于以下两个条件:
- 如果 b b b 是奇数,那么有 a b = a ∗ a b − 1 a^b=a*a^{b-1} ab=a∗ab−1
- 如果 b b b 是偶数,那么有 a b = a b / 2 ∗ a b / 2 a^b=a^{b/2}*a^{b/2} ab=ab/2∗ab/2
对于奇数的情况总可以在下一步时转化为偶数,而偶数的情况总可以在下一步减小一半,这样在进行对数级别的次数转换后,就可以把
b
b
b 变成0,而所有的正整数的0次幂都是1。
因此,就可以得到时间复杂度为
O
(
l
o
g
b
)
O(logb)
O(logb)的快速幂递归写法:
typedef long long LL;
LL binaryPow(LL a, LL b, LL m) {
if(b == 0) return 1;
else if(b & 1) return a * binaryPow(a,b-1,m) % m;
else {
LL tmp = binaryPow(a,b/2,m) % m;
return tmp * tmp % m;
}
}
迭代写法
对于
a
b
a^b
ab来说,如果把
b
b
b写成二进制,那么
b
b
b就可以写成若干二次幂之和。
例如:
13
13
13的二进制是
1101
1101
1101,那么
13
=
2
3
+
2
2
+
2
0
=
8
+
4
+
1
13=2^3+2^2+2^0=8+4+1
13=23+22+20=8+4+1,所以
a
13
=
a
8
+
4
+
1
=
a
8
∗
a
4
∗
a
1
a^{13}=a^{8+4+1}=a^8*a^4*a^1
a13=a8+4+1=a8∗a4∗a1。
因此可以得到计算
a
b
a^b
ab的思路:遍历
b
b
b的二进制的每一位,注意到序列
a
2
i
、
⋅
⋅
⋅
、
a
8
、
a
4
、
a
2
、
a
1
a^{2^i}、···、a^8、a^4、a^2、a^1
a2i、⋅⋅⋅、a8、a4、a2、a1的前一项总是后一项的平方,所以在编程时,可以每次都把
a
i
+
1
a^{i+1}
ai+1变为
a
i
a^i
ai的平方,如果当前位为1,则乘上当前的
a
i
a^i
ai。
typedef long long LL;
LL binaryPow(LL a,LL b,LL m) {
LL ans = 1;
while(b > 0) {
//如果当前二进制位是1就累积上a
if(b & 1) ans = ans * a % m;
//令a平方
a = a * a % m;
//b的二进制右移一位
b >>= 1;
}
return ans;
}
两个细节
还需要注意两个细节:
- 如果初始时 a a a大于等于 m m m,那么需要在进入函数前就让 a a a对 m m m取模。
- 如果 m m m为1,可以直接在函数外部特判为0,不需要计算,因为等于1取模一定为0。