• Fibonacci数列


    Fibonacci数是组合数学中非常重要的一个数列,它的递推公式是:
      F(1)=F(2)=1
      F(n)=F(n-1)+F(n-2)
      当然,用这个公式来计算F(n)是非常慢的,当计算F(n)时需要从F(1)一直计算到F(n)。Fibonacci数列还满足一些其他的公式,如:
      F(a+b+1)=F(a+1)*F(b+1)+F(a)*F(b)
      利用这个公式,可以加速Fibonacci数的计算。我们考虑同时计算F(2n+1)和F(2n),则按照上面的公式:
      F(2n+1)=F(n+1)*F(n+1)+F(n)*F(n)
      F(2n)=F(n+1)*F(n)+F(n)*F(n-1)=F(n+1)*F(n)+F(n)*(F(n+1)-F(n))
      这样,F(2n+1)和F(2n)的计算变为了F(n+1)和F(n)的计算,即下标变为了原来的一半。重复利用这种方法,可以每次让下标变为原来的一半,总共需要大约log n次计算(以2为底)。
      当n较大时,后面的方法就比直接的递推要快得多,比如当n=1000000时,后面的方法大概需要20次计算,而直接递推的方法大概需要1000000次计算

    Fibonacci的矩阵快速幂算法:

    进一步,矩阵乘法满足结合律可以得出直接推导公式:
    代码:(codevs 1250 Fibonacci数列
    #include<iostream>
    #include<cstring>
    #define Size 5
    using namespace std;
    
    int n,q;
    
    struct Box{
        long long data[Size][Size];
        int x,y;
    };
    
    void cheng(Box& a,Box& b,Box& c){
        c.x=a.x; c.y=b.y;
        memset(c.data,0,sizeof(c.data));
        for(int i=1;i<=c.x;i++){
            for(int j=1;j<=c.y;j++){
                for(int k=1;k<=a.y;k++){
                    c.data[i][j]+=a.data[i][k]*b.data[k][j];
                    if(c.data[i][j]%=q);
                }
            }
        }
    }
    
    void copy(Box& a,Box& b){
        b.x=a.x; b.y=a.y;
        for(int i=1;i<=a.x;i++){
            for(int j=1;j<=a.y;j++){
                b.data[i][j]=a.data[i][j];
            }
        }
    }
    
    void out(Box a){
        for(int i=1;i<=a.x;i++){
            for(int j=1;j<=a.y;j++){
                cout<<a.data[i][j]<<' ';
            }
            cout<<endl;
        }
    }
    
    void mi(Box& a,int b,Box& ans){
        Box temp;
        bool flag=false;
        while(b>0){
            if(b&1){
                if(flag){
                    cheng(ans,a,temp);
                    copy(temp,ans);
                }else copy(a,ans),flag=true;
            }
            cheng(a,a,temp);
            copy(temp,a);
            b>>=1;
        }
    }
    
    int main(){
        int T; cin>>T;
        Box a,b,c,d; 
        c.x=2; c.y=1;
        c.data[1][1]=1; c.data[2][1]=1;
        while(T--){
            cin>>n>>q;
            n++;
            if(n==1||n==2){cout<<1<<endl;continue;}
            a.x=2; a.y=2;
            a.data[1][1]=1; a.data[1][2]=1; a.data[2][1]=1; a.data[2][2]=0;
            mi(a,n-2,b);
            cheng(b,c,d);
    //        cout<<"a:
    ";out(a);
    //        cout<<"b:
    ";out(b);
    //        cout<<"c:
    ";out(c);
    //        cout<<"d:
    ";out(d);
            cout<<d.data[1][1]%q<<endl;
        }
    }

    codevs 1574 广义斐波那契数列

    斐波那契数列的一个变化,基本算法是一样的。

    不过a要变成【p,q(endl)1,0】,c变成【a2,a1】。

  • 相关阅读:
    Java最常见的面试题:模块十一
    Java最常见的面试题:模块九和模块十
    Java最常见的面试题:模块八
    Java最常见的面试题:模块七
    【leetcode】跳跃游戏
    【leetcode】字母异位词分组
    【C++】STL各容器的实现,时间复杂度,适用情况分析
    【C++】如何使用GCC生成动态库和静态库
    【C++】C++中基类的析构函数为什么要用virtual虚析构函数?
    【leet-code】接雨水
  • 原文地址:https://www.cnblogs.com/FuTaimeng/p/5657724.html
Copyright © 2020-2023  润新知