简单快速幂
对于计算 a b a^b ab ( a , b ∈ N , b ≠ 0 a,b∈N,b\neq0 a,b∈N,b=0),最简单的做法莫过于运用正常的乘法进行实现,则需要令a自乘b-1次,显然该操作需要进行b次循环,记时间复杂度为 O ( n ) O(n) O(n)。
int a,b;
for(int i=1;i<b;i++)
{
a*=a;//直接在变量a上自乘 最后的结果仍然是变量a
}
当b较小时,(例如
b
=
63
b=63
b=63)用这种方式,程序也不会花费太多时间;然而当b很大时(例如
b
=
1
0
10
b=10^{10}
b=1010),程序则要进行
1
0
10
10^{10}
1010次循环,而这种量级很难在1秒内完成,易导致程序TLE。因此,为了处理大量级的幕次运算,就需要引进“快速幂”算法加速幂次运算。
我们知道,
2
15
=
2
14
∗
2
1
=
2
7
∗
2
7
∗
2
=
[
(
2
6
∗
2
)
∗
(
2
6
∗
2
)
]
∗
2
=
[
(
2
3
∗
2
3
∗
2
)
∗
(
2
3
∗
2
3
∗
2
)
]
∗
2
=
.
.
.
.
.
2^{15}=2^{14}*2^1=2^7*2^7*2=[(2^6*2)*(2^6*2)]*2=[(2^3*2^3*2)*(2^3*2^3*2)]*2=.....
215=214∗21=27∗27∗2=[(26∗2)∗(26∗2)]∗2=[(23∗23∗2)∗(23∗23∗2)]∗2=.....
从上例可以看出,2n次幂可以分拆成2个n次幂之积,而2n+1次幂可以分拆成2n次幂和1次幂之积,而其中2个同次幂我们只需要取其一计算就够了,所以,在计算前式时,我们实际上只需要计算
2
14
、
2
7
、
2
3
、
2
2^{14}、2^7、2^3、2
214、27、23、2就可以了。对于
a
b
a^b
ab我们稍加计算就可以得出,我们只需要进行
log
b
\log{b}
logb次运算就可以了,大大加快了幂次运算的速度,时间复杂度达到
O
(
log
n
)
O(\log n)
O(logn)。
//用递归函数实现
int quick_pow(int a,int b){
if(b==1) return a;//最内层递归到一次方时则返回a
if(b&1) return quick_pow(a,b-1)*a;//当幂次为奇数时拆成a与偶数幂之积
int res=quick_pow(a,b>>1);//当幂次为偶数时拆成2个幂次之积 引入变量res是为了只计算一次
return res*res;
快速幂取模
然而,在C++中,当数字过大时会出现溢出的情况,因此我们通常会遇到对大数取模的要求。
先介绍一个简单的数论知识:
a
∗
b
m
o
d
n
=
(
(
a
m
o
d
n
)
∗
(
b
m
o
d
n
)
)
m
o
d
n
a*b \mod n=((a \mod n)*(b \mod n))\mod n
a∗bmodn=((amodn)∗(bmodn))modn
举个例子:
2
15
m
o
d
107
=
(
2
14
∗
2
)
m
o
d
107
=
(
2
14
m
o
d
107
)
∗
(
2
m
o
d
107
)
m
o
d
107
=
.
.
.
2^{15}\mod 107=(2^{14}*2)\mod 107=(2^{14}\mod 107)*(2\mod107)\mod107=...
215mod107=(214∗2)mod107=(214mod107)∗(2mod107)mod107=...
因此我们就可以使用快速幂取模:
#define ll long long
ll quick_pow(ll a,ll b,ll n){
return a%n;
if(b&1) return quick_pow(a,b-1,n)*(a%n)%n;
int res=quick(a,b>>1,n);
return res*res%n;
}