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

     
  • 相关阅读:
    AndoridSQLite数据库开发基础教程(9)
    AndoridSQLite数据库开发基础教程(8)
    AndoridSQLite数据库开发基础教程(7)
    AndoridSQLite数据库开发基础教程(6)
    AndoridSQLite数据库开发基础教程(5)
    当music-list向上滑动的时候,设置layer层,随其滚动,覆盖图片,往下滚动时候,图片随着展现出来
    开发song-list组件;
    封装一个音乐列表music-list基础组件,可以共用,哪个需要的时候就是哪个props相应的值
    获取并封装歌手歌曲的数据
    使用vuex保存singer每个歌星的基本信息
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/13066103.html
Copyright © 2020-2023  润新知