最简单易懂快速幂算法(fastpow)
看到其他文章写得太过于复杂,固然整理了一下思路,整理写了一篇简单易懂的快速幂算法讲解文章。
快速幂算法能够将O(n)降低到O(log n),快速幂所使用的二进制数拆分思想适用于矩阵的幂运算,线段树,树状数组等。
模板题:P1226 【模板】快速幂 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
同时为了减小运算过程中数字的大小,我们需要使用到
模运算的乘法性质
(
a
∗
b
)
%
p
=
(
(
a
%
p
)
∗
(
b
%
p
)
)
%
p
(a * b) \% p = ((a \% p) * (b \% p)) \% p
(a∗b)%p=((a%p)∗(b%p))%p
这是模运算的一个基本性质,称为模运算的乘法性质。它表明,对于任意三个整数 a、b 和 p,它们的乘法对 p 取模的结果等于它们分别对 p 取模后的乘积再对 p 取模的结果。
这个性质的应用能够确保了每次乘法操作后结果在取模运算后不会溢出(缩小数字)。
应用前:
for _ in range(n):
result *= a
大O(n)
应用后:
result=1
def power mod(a,n,p):
in range(n):
for result=(result*a)%p
return result
范围缩小了,没有超出数据范围,但是速度还是太慢了
所以此时我们需要用上快速幂算法
假设我们要计算的是3^237
237拆分成二进制为11101101
利用二进制转十进制的性质,我们可以得知:
237
=
1
∗
2
7
+
1
∗
2
6
+
1
∗
2
5
+
0
∗
2
4
+
1
∗
2
3
+
1
∗
2
2
+
0
∗
2
1
+
1
∗
2
0
237 = 1*2^7 + 1*2^6 + 1*2^5 + 0*2^4 + 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0
237=1∗27+1∗26+1∗25+0∗24+1∗23+1∗22+0∗21+1∗20
再将二次幂展开:
237
=
1
∗
128
+
1
∗
64
+
1
∗
32
+
0
∗
16
+
1
∗
8
+
1
∗
4
+
0
∗
2
+
1
∗
1
237 = 1*128 + 1*64 + 1*32 + 0*16 + 1*8 + 1*4 + 0*2 + 1*1
237=1∗128+1∗64+1∗32+0∗16+1∗8+1∗4+0∗2+1∗1
然后我们计算3^237的结果展开:
3
237
=
(
3
128
)
1
∗
(
3
64
)
1
∗
(
3
32
)
1
∗
(
3
16
)
0
∗
(
3
8
)
1
∗
(
3
4
)
1
∗
(
3
2
)
0
∗
(
3
1
)
1
3^{237} = (3^{128})^1 * (3^{64})^1 * (3^{32})^1 * (3^{16})^0 * (3^{8})^1 * (3^{4})^1 * (3^{2})^0 * (3^{1})^1
3237=(3128)1∗(364)1∗(332)1∗(316)0∗(38)1∗(34)1∗(32)0∗(31)1
然后通过观察,我们发现前者是后者的两倍,就比如:
(
3
128
)
=
(
3
64
)
∗
(
3
64
)
(3^{128}) = (3^{64}) * (3^{64})
(3128)=(364)∗(364)
也就是最终我们只需要进行十几次计算就能够计算出结果了,而不需要大O(n)次。
所以应用上快速幂之后,写法是:
result=1
while(n>0):
if n%2 == 1: #取模操作是为了得到幂中的二进制的最低位
res = res * a
a = a*a #每次将a翻倍
n //=n #除2
n(2) | n&1 | a | result |
---|---|---|---|
11101101 | 1 | 3 1 3^1 31 | 3 1 3^1 31 |
1110110 | 0 | 3 2 = 3 1 2 3^2 = 3^{1^2} 32=312 | 3 1 3^1 31 |
111011 | 1 | 3 4 = 3 2 2 3^4 = 3^{2^2} 34=322 | 3 1 ∗ 3 4 3^1 * 3^4 31∗34 |
11101 | 1 | 3 8 = 3 4 2 3^8 = 3^{4^2} 38=342 | 3 1 ∗ 3 4 ∗ 3 8 3^1 * 3^4 * 3^8 31∗34∗38 |
1110 | 0 | 3 1 6 = 3 8 2 3^16 = 3^{8^2} 316=382 | 3 1 ∗ 3 4 ∗ 3 8 3^1 * 3^4 * 3^8 31∗34∗38 |
111 | 1 | 3 3 2 = 3 1 6 2 3^32 = 3^{16^2} 332=3162 | 3 1 ∗ 3 4 ∗ 3 8 ∗ 3 32 3^1 * 3^4 * 3^8 * 3^{32} 31∗34∗38∗332 |
11 | 1 | 3 6 4 = 3 3 2 2 3^64 = 3^{32^2} 364=3322 | 3 1 ∗ 3 4 ∗ 3 8 ∗ 3 32 ∗ 3 64 3^1 * 3^4 * 3^8 * 3^{32}* 3^{64} 31∗34∗38∗332∗364 |
1 | 1 | 3 128 = 3 6 4 2 3^{128} = 3^{64^2} 3128=3642 | 3 1 ∗ 3 4 ∗ 3 8 ∗ 3 32 ∗ 3 64 ∗ 3 128 3^1 * 3^4 * 3^8 * 3^{32} * 3^{64} * 3^{128} 31∗34∗38∗332∗364∗3128 |
然后加上模运算减少数字大小:
def fast_pow_mode(a,n,p):
result=1
while(n>0):
if n%2 == 1:
res = (res * a)%p
a = (a*a)%p
n >>=1 #位运算跟除2一样的
C++的写法:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a,b,p,result=1;
cin>>a>>b>>p;
while(b>0){
if(b%2 == 1){
result = (result*a)%p;
}
a = (a*a)%p;
b = b/2;
}
result = result%p;
cout<<result<<endl;
return 0;
};