快速幂
快速幂
常规幂运算:
计算a^n 需要O(n)次乘法,例如2^5 = 2 2 2 2 2, so overwhelming
当n很大时这种方法不可取
快速幂:
可以在O(log(n))的时间复杂度内完成计算
基本思想:二分法
如果n是偶数:a^n = (a^(n/2)) ^ 2;
如果是奇数:a^n = (a^(n-1)) * a;
算法实现
1 |
|
笔者觉得还是第二版更好理解,因为第一版还需要额外了解一点小知识点(课堂上必定会有提及,但是我摸鱼故没听见
迭代版补充:
- n&1为位运算,检查n是否为奇数,只检查 n 的二进制最后一位(二进制最后一位是1),如果指数是奇数,需要将当前的底数乘进结果中
n>>1等价于n/=2,使用位右移一位,相当于整除以2
- 计算过程举例
Q:计算3^13
初始:result=1, a=3, n=13 (二进制为1101)
第一轮:n=13(奇数)
n&1=1 -> result= 1 3 =3
a = 3 3 = 9
n = 13>>1 =6第二轮: n=6(偶数)
n&1=0 -> result不变
a = 9 * 9 = 81
n = 6>>1 = 3第三轮: n=3(奇数)
n&1=1 -> result = 3 81 = 243
a = 81 81 = 6561
n = 3>>1 = 1第四轮: n=1(奇数)
n&1=1 -> result =243 6561 = 1594323
a = 6561 6561
n= 1>>1 = 0;return 1594323
看完例子相信大家有种豁然开朗之感,就像英语背单词需要带入句子,如果不理解如何运行不妨举个例子试一试
带模运算的版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//其实就是一步一除模
using namespace std;
using ll = long long;
ll fast_mod(ll a,ll n,ll mod){
ll result=1;
a%=mod; //先取模防止a过大
while(n>0){
if(n&1){
result=(result*a)%mod;
}
a=(a*a)%mod;
n>>1;
}
return result;
}- 计算过程举例
上难度
我的目的终于达成,在讲完这么多前期准备工作过后,我们迎来终于要上场的问题
矩阵快速幂(以斐波那契数列为例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using namespace std;
using ll = long long;
using Matrix= vector<vector<ll>>;
Matrix Mul(const Matrix& A,const Matrix& B,ll mod){
int n=A.size(); //A的行数
int m=B[0].size(); //B的列数
int p=B.size(); //B的行数(A的列数)
//创建结果矩阵C,大小为n*m,初始化为0
Matrix C(n,vector<ll>(m,0));
//三重循环
for(int i=0;i<n;i++){ //C的每一行
for(int j=0;j<m;j++){ //C的每一列
for(int k=0;k<p;k++){ //累加A第i列和B第j列的点积(妙
C[i][j]=(C[i][j]+A[i][k]*B[k][j])%mod;
}
}
}
return C;
}
Matrix FastPow(Matrix base,ll n,ll mod){
int size=base.size(); //获取矩阵的维度,假设是方阵
//创建结果方阵,初始化为单位矩阵
Matrix result(size,vector<ll>(size,0));
for(int i=0;i<size;i++){
result[i][i]=1;
}
//核心循环:类似快速幂
while(n>0){
if(n&1){
// result*=base
result=Mul(result,base,mod);
}
//base*=base
base=Mul(base,base,mod);
n>>1;
}
return result;
}
ll fibo(ll n,ll mod){
if(n<=1) return n;
//核心:斐波那契的矩阵表示(妙啊
//| F(n+1) | | 1 1 | | F(n) |
//| | = | | * | |
//| F(n) | | 1 0 | | F(n-1) |
// 简化为
//| F(n+1) | | 1 1 |^n | F(1) |
//| | = | | * | |
//| F(n) | | 1 0 | | F(0) |
//通过矩阵联系起来了递归!!!
Matrix base={{1,1},{1,0}};
Matrix result=FastPow(base,n-1,mod);
return result[0][0];
}
快速幂这种基础算法,是构建更复杂算法(矩阵快速幂、数论运算)的基石。基础不牢,地动山摇。线代也要好好学,嗯。