递推公式&斐波那契数列的四种求法

什么是递推公式

递推公式就是形如斐波那契数列那样,每一项都由前面几项运算求得

下面以斐波那契数列为例讲解递推公式的几种求解方法

斐波那契数列

斐波那契数列形式如下:

递推公式&斐波那契数列的四种求法_第1张图片

学习C语言后我们都知道斐波那契数列的递归求法,很好理解:

1.递归求法

时间复杂度O(2n)

int Fibonacci(int n){
    return n>2?Fibonacci(n-1)+Fibonacci(n-2):1;
}

根据上面的递归方法来看,由F(n)我们递归到F(n-1)和F(n-2),接下来要求F(n-1)时,我们又递归到F(n-2)和F(n-3);要求F(n-2)我们又需要递归到F(n-3)和F(n-4):

递推公式&斐波那契数列的四种求法_第2张图片

由上可知,F(n-2)、F(n-3)、F(n-4)…我们都求了两遍,这也是我们浪费时间的地方,稍微有点算法知识的都知道我们可以存起来每次求的值,办法有两种,一个是数组预处理,另一个是一次循环求

2.线性求法

数组预处理:

由于数组大小最多开到2e7因此此方法有一定的局限性

const int N=?;
int F[N];
int num; //num为我们需要求的最大的斐波那契数,num
void Fibonacci(){
    F[1]=F[2]=1;
    for(int i=3;i<=num;i++){
        F[i]=F[i-1]+F[i-2];
    } 
}

循环求:

通过中间变量保存F(n-1)和F(n-2),由于每次ans都要更新,所以我们必须将ans初始值设为F(2),然后令x=F(0)=0,y=F(1),每次先更新x再更新ans再更新y,这样即可得出答案,见下面表格分析:

n \ 3 4 5 6
x 0 1 2 3 5
ans 1 2 3 5 8
y 1 1 2 3 5

代码:

int Fibonacci(int n){
    if(n==1 || n==2) return 1;
    int x=0,y=1,ans=1;
    for(int i=3;i<=n;i++){
        x=ans;
        ans=ans+y;
        y=x;
    }
    return ans;
}

3.公式求法

斐波那契数列的通项公式为:

在这里插入图片描述

由于本人数学一般,无法自己推导,下图为百度的一种较为简单的方法:

递推公式&斐波那契数列的四种求法_第3张图片

由于n增大时涉及大量浮点运算,会导致精度损失,因此会出现错误答案,故无法通过浮点数计算斐波那契数列

int Fibonacci(int n){
    return (pow((1 + sqrt(5)) / 2, n) - pow((1 - sqrt(5)) / 2, n)) / sqrt(5);
}

但是也不是不可以做,只是需要用到很多知识

因为取模的数一般都是质数假设为 1 e 9 + 9 1e9+9 1e9+9,那么我们能求出 x 2 ≡ 5 ( m o d    1 e 9 + 9 ) x^2 \equiv 5(mod~~1e9+9) x25(mod  1e9+9)是有解的,解出的两个解为 616991993 616991993 616991993 383008016 383008016 383008016,也就是在这个取模的意义下 5 \sqrt{5} 5 和这两个解是相等的,那么我们就能直接通过公式计算 F ( n ) F(n) F(n)了,设二次剩余方程的解存在且为 x x x,那么有:

F ( n ) = i n v ( x ) [ ( ( 1 + x ) ∗ i n v ( 2 ) % p ) n − ( ( 1 − x + p ) % p ∗ i n v ( 2 ) ) n ] F(n)=inv(x)[((1+x)*inv(2)\%p)^n-((1-x+p)\%p*inv(2))^n] F(n)=inv(x)[((1+x)inv(2)%p)n((1x+p)%pinv(2))n]

4.矩阵快速幂

对于一般的递推公式: ① F(n) = a*F(n-1) + b*F(n-2)

我们可以得到 ② F(n-1) = F(n-1) + 0*F(n-2)

根据线性代数知识,联立①和②,我们求得:

递推公式&斐波那契数列的四种求法_第4张图片

其中的常数矩阵称为递推式的特征矩阵

依次将等号右边的第二个式子代入,可以得到:

③:

递推公式&斐波那契数列的四种求法_第5张图片

④:

递推公式&斐波那契数列的四种求法_第6张图片

联立③和④,可以得到最终的等式矩阵:

递推公式&斐波那契数列的四种求法_第7张图片

由于有的递推式常数项没有给出F(0)和F(-1),但我们由F(2)=F(1)+F(0)和F(1)=F(0)+F(-1)可以求出

下面的代码以斐波那契数列的递推矩阵为例:

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int Mod=10007;
int n;
struct Matrix{
    ll matrix[105][105];
};

Matrix mul(Matrix a,Matrix b){
    Matrix res;
    memset(res.matrix,0,sizeof res.matrix);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    for(int k=1;k<=n;k++){  //对于新矩阵的(i,j)位置,即前行乘后列
        res.matrix[i][j]+=(a.matrix[i][k]*b.matrix[k][j])%Mod;
        res.matrix[i][j]%=Mod;
    }
    return res;
}

Matrix quick_pow(Matrix mx,ll x){
    Matrix ans;   //快速幂我们初始化为1,那么这里就是矩阵的单位“1”,即单位矩阵
    memset(ans.matrix,0,sizeof ans.matrix);
    for(int i=1;i<=n;i++) ans.matrix[i][i]=1;
    while(x){
        if(x&1) ans=mul(ans,mx);
        mx=mul(mx,mx);
        x>>=1;
    }
    return ans;
}

int main()
{
    int q;
    n=2;
    Matrix m;  //特征矩阵及初始化
    m.matrix[1][1]=1;
    m.matrix[1][2]=1;
    m.matrix[2][1]=1;
    m.matrix[2][2]=0;
    cin>>q;
    m=quick_pow(m,q);
    cout<<m.matrix[2][1]<<endl;
    return 0;
}

你可能感兴趣的:(数论)