算法与数据结构---6.8、斐波那契数列-矩阵快速幂
一、总结
一句话总结:
斐波那契数列的矩阵快速幂的解法,也就是将递推表达式化成矩阵的幂操作和乘法操作,利用快速幂,可以得到O(logn)的解法
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int mod=1000000007; 5 6 //定义矩阵对应的结构体 7 struct Matrix{ 8 int row,column; 9 long long v[3][3]; 10 Matrix(){ 11 memset(v,0,sizeof(v)); 12 } 13 }; 14 15 //矩阵乘法 16 Matrix multiply(Matrix a,Matrix b){ 17 Matrix ans; 18 ans.row=a.row; 19 ans.column=b.column; 20 //具体来做矩阵乘法 21 for(int i=1;i<=a.row;i++){ 22 for(int j=1;j<=b.column;j++){ 23 for(int k=1;k<=a.column;k++){ 24 ans.v[i][j]+=(a.v[i][k]*b.v[k][j])%mod; 25 ans.v[i][j]%=mod; 26 } 27 } 28 } 29 return ans; 30 } 31 32 33 //矩阵的快速幂 34 Matrix pow(Matrix a,long long n){ 35 Matrix ans,base=a; 36 ans.row=2;ans.column=2; 37 ans.v[1][1]=ans.v[2][2]=1; 38 while(n){ 39 if(n%2==1) ans=multiply(ans,base); 40 base=multiply(base,base); 41 n/=2; 42 } 43 return ans; 44 } 45 46 47 int main(){ 48 long long n; 49 cin>>n; 50 Matrix ans,base,last; 51 //初始化base矩阵 52 base.row=2;base.column=2; 53 base.v[1][1]=base.v[1][2]=base.v[2][1]=1; 54 //初始化last矩阵 55 last.row=2;last.column=1; 56 last.v[1][1]=last.v[2][1]=1; 57 if(n==1||n==2){ 58 cout<<1<<endl; 59 }else{ 60 ans=pow(base,n-2); 61 ans=multiply(ans,last); 62 cout<<ans.v[1][1]<<endl; 63 } 64 65 return 0; 66 }
1、斐波那契数列-矩阵快速幂前置知识:矩阵乘法?
矩阵阵法就是按照矩阵相乘的规律,一步步来做的,也就是拿矩阵a的每一行乘以矩阵b的每一列,并且把矩阵a的每一行里面的每一个元素都和矩阵b里面每一列的每一个元素都一一相乘
1 Matrix multiply(Matrix a,Matrix b){ 2 Matrix ans; 3 ans.row=a.row; 4 ans.column=b.column; 5 //遍历矩阵a的每一行 6 for(int i=1;i<=a.row;i++){ 7 //遍历矩阵b的每一列 8 for(int j=1;j<=b.column;j++){ 9 //把矩阵a的每一行里面的每一个元素都和矩阵b里面每一列的每一个元素都一一相乘 10 for(int k=1;k<=a.column;k++){ 11 ans.v[i][j]+=a.v[i][k]*b.v[k][j]; 12 } 13 } 14 } 15 return ans; 16 }
2、斐波那契数列-矩阵快速幂前置知识:快速幂?
比如在求a^11的时候,快速幂就是利用11的二进制1011,也即11=2º×1+2¹×1+2²×0+2³×1=1+2+8,将a^11转化为a^1*a^2*a^8,从而用O(logn)的时间复杂度求解
#include <iostream> using namespace std; int pow(int a,int n){ int ans=1,base=a; while(n){ if(n%2==1) ans=ans*base; base=base*base; n=n/2; } return ans; } int main(){ cout<<pow(2,22)<<endl; return 0; }
二、斐波那契数列
博客对应课程的视频位置:6.8、斐波那契数列-矩阵快速幂
https://www.fanrenyi.com/video/27/282
1、前置知识:矩阵乘法
1 /* 2 3 矩阵的乘法在算法中有很多应用, 4 比如直接考矩阵的乘法,比如用矩阵优化递推表达式等等 5 6 7 矩阵a*矩阵b 要满足矩阵a的列等于矩阵b的行 8 最后乘出来的矩阵的行为矩阵a的行 9 列为矩阵b的列 10 11 总结: 12 矩阵阵法就是按照矩阵相乘的规律,一步步来做的 13 也就是拿矩阵a的每一行乘以矩阵b的每一列, 14 并且把矩阵a的每一行里面的每一个元素都和矩阵b里面每一列的每一个元素都一一相乘 15 16 17 矩阵a 18 1 2 3 19 4 5 6 20 21 矩阵b 22 1 2 23 3 4 24 5 6 25 26 27 1*1+2*3+3*5 28 29 */ 30 31 #include <iostream> 32 #include <cstring> 33 using namespace std; 34 35 struct Matrix{ 36 int row,column; 37 int v[5][5]; 38 Matrix(){ 39 memset(v,0,sizeof(v)); 40 } 41 }; 42 43 Matrix multiply(Matrix a,Matrix b){ 44 Matrix ans; 45 ans.row=a.row; 46 ans.column=b.column; 47 //遍历矩阵a的每一行 48 for(int i=1;i<=a.row;i++){ 49 //遍历矩阵b的每一列 50 for(int j=1;j<=b.column;j++){ 51 //把矩阵a的每一行里面的每一个元素都和矩阵b里面每一列的每一个元素都一一相乘 52 for(int k=1;k<=a.column;k++){ 53 ans.v[i][j]+=a.v[i][k]*b.v[k][j]; 54 } 55 } 56 } 57 return ans; 58 } 59 60 int main(){ 61 Matrix a,b,ans; 62 a.row=2;a.column=3; 63 b.row=3;b.column=2; 64 65 a.v[1][1]=1;a.v[1][2]=2;a.v[1][3]=3; 66 a.v[2][1]=4;a.v[2][2]=5;a.v[2][3]=6; 67 68 b.v[1][1]=1;b.v[1][2]=2; 69 b.v[2][1]=3;b.v[2][2]=4; 70 b.v[3][1]=5;b.v[3][2]=6; 71 72 ans=multiply(a,b); 73 74 cout<<ans.v[1][1]<<endl; 75 76 return 0; 77 }
2、前置知识:快速幂
1 /* 2 3 快速幂: 4 5 首先,快速幂的目的就是做到快速求幂, 6 7 假设我们要求a^n,那么其实n是可以拆成二进制的, 8 例如当n==11时 9 11的二进制是1011, 10 11 =2º×1+2¹×1+2²×0+2³×1=1+2+8, 11 所以 12 a^11=a^1*a^2*a^8 13 原来算11次,现在只需要算三次 14 15 具体怎么算呢: 16 我们可以用一个变量base来在每次循环的时候记录a^i, 17 最开始base是a 18 然后每次循环让base=base*base 19 那么base的值 20 a-->a^2-->a^4-->a^8-->a^16-->a^32....... 21 然后根据11的二进制,1011, 22 取位为1时候的base值即可, 23 也就是取a,a^2,a^8 24 25 由此可以得到代码: 26 27 28 */ 29 30 #include <iostream> 31 using namespace std; 32 33 int pow(int a,int n){ 34 int ans=1,base=a; 35 while(n){ 36 if(n%2==1) ans=ans*base; 37 base=base*base; 38 n=n/2; 39 } 40 return ans; 41 } 42 43 int main(){ 44 cout<<pow(2,22)<<endl; 45 return 0; 46 }
3、矩阵快速幂
我们来看这样一个式子
可以一路传递到出口f(2)、f(1)
所以我们可以求出
这样得到的结果就是
所以我们在这个结果取到f(n)就是所求的解
而求幂可以采用快速幂解法把
时间复杂度降为logn
1 /* 2 3 4 注意点: 5 1、 6 读入n的时候不能用int,因为1<=n<2^63 7 8 2、 9 注意结构体中的数组要用 long long类型,因为涉及到矩阵的乘法, 10 涉及到两个数相乘,所以int mod 1000000007之后,两个数相乘,还是会超int 11 12 3、 13 因为读入的n是long long类型,所以函数传递参数的时候,也要记得别用成int了 14 15 16 */ 17 #include <iostream> 18 #include <cstring> 19 using namespace std; 20 const int mod=1000000007; 21 22 //定义矩阵对应的结构体 23 struct Matrix{ 24 int row,column; 25 long long v[3][3]; 26 Matrix(){ 27 memset(v,0,sizeof(v)); 28 } 29 }; 30 31 //矩阵乘法 32 Matrix multiply(Matrix a,Matrix b){ 33 Matrix ans; 34 ans.row=a.row; 35 ans.column=b.column; 36 //具体来做矩阵乘法 37 for(int i=1;i<=a.row;i++){ 38 for(int j=1;j<=b.column;j++){ 39 for(int k=1;k<=a.column;k++){ 40 ans.v[i][j]+=(a.v[i][k]*b.v[k][j])%mod; 41 ans.v[i][j]%=mod; 42 } 43 } 44 } 45 return ans; 46 } 47 48 49 //矩阵的快速幂 50 Matrix pow(Matrix a,long long n){ 51 Matrix ans,base=a; 52 ans.row=2;ans.column=2; 53 ans.v[1][1]=ans.v[2][2]=1; 54 while(n){ 55 if(n%2==1) ans=multiply(ans,base); 56 base=multiply(base,base); 57 n/=2; 58 } 59 return ans; 60 } 61 62 63 int main(){ 64 long long n; 65 cin>>n; 66 Matrix ans,base,last; 67 //初始化base矩阵 68 base.row=2;base.column=2; 69 base.v[1][1]=base.v[1][2]=base.v[2][1]=1; 70 //初始化last矩阵 71 last.row=2;last.column=1; 72 last.v[1][1]=last.v[2][1]=1; 73 if(n==1||n==2){ 74 cout<<1<<endl; 75 }else{ 76 ans=pow(base,n-2); 77 ans=multiply(ans,last); 78 cout<<ans.v[1][1]<<endl; 79 } 80 81 return 0; 82 }