大斐波拉契数列及斐波拉契前缀和(m阶前缀和)求解

前言:

1、本文主要介绍了如何对斐波拉契数列的高项进行求解,省略了对于朴素求解算法的详细介绍。

2、本文对于斐波拉契数列的前缀和以及多次求前缀和的方法进行一定的分析,得出了转移矩阵的一般形式。

3、编写公式开始是在AxMath进行的书写,写完以后发现好像不能直接将原文本导出(只有LaTex文本文件),故下面只是将导出的图片进行了展示,带来不变请谅解。

4、最后进行了代码的实现。

 

(一)斐波拉契数列求解

                                   

(二)斐波拉契数列前缀和

                     

 

(三)斐波拉契数列m阶前缀和

                      

 

(四)代码实现

1、斐波拉契数列的求解:

/**
    支持1e18范围内的斐波拉契数列值的求解
    MOD为取模以后的结果
    这里模数较大,需要用到模拟乘法或者O(1)快速乘
    但是O(1)快速乘可能会有精度误差(当模数较大)
    当MOD*2会爆long long时这里的乘法MULL会出错,需要重写
**/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ll long long
#define MOD 1000000000000000000                    //模数1e18
#define FOR(i,a,b) for(int i=a;i<b;i++)
using namespace std;
inline ll ksc(ll x,ll y,ll mod){                   //O(1)快速乘?
    return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}
inline ll mul_mod_ll(ll a,ll b,ll mod){            //O(1)快速乘?
    ll d=(ll)floor(a*(double)b/mod+0.5);
    ll ret=a*b-d*mod;
    if(ret<0)ret+=mod;
    return ret;
}
ll MUll(ll x,ll y,ll mod){                         //O(log)快速乘?
    ll ans=0;
    while(y){
        if(y&1)ans=(ans+x)%mod;
        y>>=1;x=(x<<1)%mod;
    }
    return ans;
}
void MUL(ll A[2][2],ll B[2][2],ll mod){           //矩阵乘法
    ll C[2][2]={0,0,0,0};
    FOR(i,0,2)FOR(j,0,2)FOR(k,0,2)
    C[i][j]=(C[i][j]+MUll(A[i][k],B[k][j],mod))%mod;
    memcpy(A,C,sizeof C);
}
ll fib(ll a,ll b,ll n,ll mod){                    //快速幂
    ll X[2][2]={1,1,1,0};
    ll E[2][2]={1,0,0,1};
    while(n){
        if(n&1)MUL(E,X,mod);
        n>>=1;MUL(X,X,mod);
    }
    return b*E[0][0]+a*E[0][1];
}

int main(){
    ll n;
    ll a=1,b=1;                            //f(0)、f(1)
    while(cin>>n){
        printf("%lld\n",n<0?-1:n==0?a:n==1?b:fib(a,b,n-1,MOD));
    }
    return 0;
}

 

2、斐波拉契数列m阶前缀和:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define MOD 1000000009                      //注意模数的大小
#define ll long long
using namespace std;
ll E[110][110],e[110][110];
ll X[110][110],x[110][110],l[110],a,b,c,d,t;
void init(){                                //初始化单位矩阵
    for(int i=0;i<105;i++){
        e[i][i]=1;
        for(int j=0;j<=i;j++)x[i][j]=1;
    }
    x[0][0]=0;x[0][1]=1;
}
void MUL(ll m1[110][110],ll m2[110][110]){  //矩阵乘法
    ll m3[110][110];
    memset(m3,0,sizeof m3);
    for(int i=0;i<=c;i++)
        for(int j=0;j<=c;j++)
            for(int k=0;k<=c;k++)
                m3[i][j]=(m3[i][j]+m1[i][k]*m2[k][j])%MOD;
    memcpy(m1,m3,sizeof m3);
}
ll ksm(ll n){                               //快速幂
    memcpy(E,e,sizeof e);
    memcpy(X,x,sizeof x);
    while(n){
        if(n&1)MUL(E,X);
        n>>=1;MUL(X,X);
    }
    ll ans=0;
    for(int i=c;i>=0;i--)
        ans=(ans+l[i]*E[c][i])%MOD;
    return ans;
}
int main(){
//    freopen("in.txt","r",stdin);
    init();
    scanf("%d",&t);
    while(t--){

        scanf("%d%d%d%d",&a,&b,&c,&d);       //a,b分别为f(0),f(1)
                                             //c,d表示c阶的前d项和
        if(d==0){printf("%I64d\n",a);}       //第一列全为a,d从0开始
        else{
            l[0]=a%MOD;c++;                  //初始化第二列
            for(int i=1;i<=c;i++)
                l[i]=((ll)(i-1)*a+b)%MOD;
            printf("%I64d\n",ksm(d-1));      //快速幂--返回c阶的S(d)值
                                             //c==0时返回的就是斐波拉契数列的f(d)值
        }
    }
    return 0;
}

 

(五)总结

1、对于上述介绍的方法复杂度都是O(m^3logn)的,当m较大时性能将变得极差,这里主要解决的是在m较小时对于高项的单次(或少数次)求解。

2、对于上述编写的代码可能会有一定的错误,若有纰漏,烦请斧正。

3、实际上对于m阶前缀和的求解,我们将f(0)=a,f(1)=b的值分开加,可以得到最终a与b的系数的规律,可以在更短的时间内求得(万能的学长)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值