递推公式就是形如斐波那契数列那样,每一项都由前面几项运算求得
下面以斐波那契数列为例讲解递推公式的几种求解方法
斐波那契数列形式如下:
学习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):
由上可知,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.公式求法
斐波那契数列的通项公式为:
由于本人数学一般,无法自己推导,下图为百度的一种较为简单的方法:
由于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) x2≡5(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−((1−x+p)%p∗inv(2))n]
4.矩阵快速幂
对于一般的递推公式: ① F(n) = a*F(n-1) + b*F(n-2)
我们可以得到 ② F(n-1) = F(n-1) + 0*F(n-2)
根据线性代数知识,联立①和②,我们求得:
其中的常数矩阵称为递推式的特征矩阵
依次将等号右边的第二个式子代入,可以得到:
③:
④:
联立③和④,可以得到最终的等式矩阵:
由于有的递推式常数项没有给出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;
}