算法与数据结构---6.8、斐波那契数列-矩阵快速幂

算法与数据结构---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 }

 
原文地址:https://www.cnblogs.com/Renyi-Fan/p/13066103.html