算法与数据结构---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 }
来源:oschina
链接:https://my.oschina.net/u/4373992/blog/4307482