递推数列算法研究

递推数列算法研究

引言

在研究中,我们可能会遇到需要根据某些已知条件来求解某个数列的通项公式的问题,本文就此类问题展开探究,寻求其解法。

线性递推方程的解

对于常系数线性递推方程

F n b 1 F n 1 b 2 F n 2 b k F n k = 0 ( 0 < k < n )

有如下解法:

先求方程对应的特征方程

q k b 1 q k 1 b 2 q k 2 b k = 0

k 个互不相同的根 q 1 , q 2 , , q k ,那么相应的常系数线性递推方程的通解为
F n = c 1 q 1 n + c 2 q 2 n + + c k q k n

例1

求斐波那契数列 F n = F n 1 + F n 2 , F 0 = F 1 = 1 的通项公式

线性递推方程 F n = F n 1 + F n 2 的特征方程为

q 2 q 1 = 0

特征方程有两个实根
q 1 = 1 + 5 2 , q 2 = 1 5 2

因此线性递推方程的通解为
F n = c 1 ( 1 + 5 2 ) n + c 2 ( 1 5 2 ) n

F 0 = F 1 = 1 代入上式求得
c 1 = 5 5 , c 2 = 5 5

因此斐波那契数列的通项公式为
F n = 5 5 [ ( 1 + 5 2 ) n ( 1 5 2 ) n ]

非线性递推方程的解

对于常系数非线性递推方程

F n b 1 F n 1 b 2 F n 2 b k F n k = s ( n ) ( 0 < k < n )

有如下解法:

先求对应的线性递推方程

F n b 1 F n 1 b 2 F n 2 b k F n k = 0 ( 0 < k < n )

的解
F n = c 1 q 1 n + c 2 q 2 n + + c k q k n

解法如前文所述

再求原方程的一个特解 f n ,那么原方程的通解为

F n = c 1 q 1 n + c 2 q 2 n + + c k q k n + f n

对于 s ( n ) 过于复杂的情况,很难求得原方程的一个特解,因此这种方法有局限性。

例2

求递推方程 F n = F n 1 + F n 2 + 1 , F 0 = F 1 = 1 的通项公式

易求方程

F n = F n 1 + F n 2 + 1

的一个特解为
F n = 1

又由例1得,方程
F n = F n 1 + F n 2

的通解为
F n = c 1 ( 1 + 5 2 ) n + c 2 ( 1 5 2 ) n

故原方程的通解为
F n = c 1 ( 1 + 5 2 ) n + c 2 ( 1 5 2 ) n 1

F 0 = F 1 = 1 代入上式求得
c 1 = 1 + 5 5 , c 2 = 1 5 5

因此 F n 的通项公式为
F n = ( 1 + 5 5 ) ( 1 + 5 2 ) n + ( 1 5 5 ) ( 1 5 2 ) n 1

数列递推方程的求法

一个数列 F n 满足某个线性递推方程,如果这个递推方程是已知的,那么根据前文所述的方法,即可求得其通项公式,但很多情况下,我们并不能得到数列的递推方程,下面介绍一种求递推方程的方法。

假设

F n b 1 F n 1 b 2 F n 2 b k F n k = 0 ( 0 < k < n )

如果根据某些已知条件求得了数列 F n 的前 2 k 项,那么就得到方程组
{ b k F 0 + b k 1 F 1 + + b 1 F k 1 = F k b k F 1 + b k 1 F 2 + + b 1 F k = F k + 1 b k F k 1 + b k 2 F 1 + + b 1 F 2 k 2 = F 2 k 1

利用高斯消元法就可以求出 b 1 , b 2 , , b k ,也就得到了数列的递推方程。

实际上,不仅是满足线性递推方程的数列,对于 s ( n ) 为常数的非线性递推方程也可用上述方法求解。

例3

已知

F n = { 1 , n 0 ( m o d   3 ) 6 , n 1 ( m o d   3 ) 3 , n 2 ( m o d   3 )

F n 的递推方程。

假设 F n 满足递推方程

F n = b 1 F n 1 + b 2 F n 2 + b 3

那么
{ b 1 F 1 + b 2 F 0 + b 3 = F 2 b 1 F 2 + b 2 F 1 + b 3 = F 3 b 1 F 3 + b 2 F 2 + b 3 = F 4

F 0 = 1 , F 1 = 6 , F 2 = 3 , F 3 = 1 , F 4 = 6 代入方程组中求得
{ b 1 = 1 b 2 = 1 b 3 = 10

那么
F n = F n 1 F n 2 + 10

根据数学归纳法可以证明 F n 的递推方程恰是如此,此处略。

正如读者所见,这个方法需要先猜测后证明,猜测的过程计算量可能会很大,那么,何不将这个繁琐的过程交给计算机来完成呢?下面给出了上述算法的C++代码,能够求解线性递推数列的递推方程,也能求解通项公式为多项式的数列的递推方程,以及两者的线性组合,但只支持整数系数,内置矩阵快速幂,能够快速求出数列第n项。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for(int i=a;i<n;i++)
namespace linear
{
    ll mo=1000000007;
    vector<ll> v;
    double a[105][105],del;
    int k;
    struct matrix
    {
        int n;
        ll a[50][50];
        matrix operator * (const matrix & b)const
        {
            matrix c;
            c.n=n;
            rep(i,0,n)rep(j,0,n)c.a[i][j]=0;
            rep(i,0,n)rep(j,0,n)rep(k,0,n)
            c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]%mo)%mo;
            return c;
        }
    }A;
    bool solve(int n)
    {
        rep(i,1,n+1)
        {
            int t=i;
            rep(j,i+1,n+1)if(fabs(a[j][i])>fabs(a[t][i]))t=j;
            if(fabs(del=a[t][i])<1e-6)return false;
            rep(j,i,n+2)swap(a[i][j],a[t][j]);
            rep(j,i,n+2)a[i][j]/=del;
            rep(t,1,n+1)if(t!=i)
            {
                del=a[t][i];
                rep(j,i,n+2)a[t][j]-=a[i][j]*del;
            }
        }
        return true;
    }
    void build(vector<ll> V)
    {
        v=V;
        int n=(v.size()-1)/2;
        k=n;
        while(1)
        {
            rep(i,0,k+1)
            {
                rep(j,0,k)a[i+1][j+1]=v[n-1+i-j];
                a[i+1][k+1]=1;
                a[i+1][k+2]=v[n+i];
            }
            if(solve(n+1))break;
            n--;k--;
        }
        A.n=k+1;
        rep(i,0,A.n)rep(j,0,A.n)A.a[i][j]=0;
        rep(i,0,A.n)A.a[i][0]=(int)round(a[i+1][A.n+1]);
        rep(i,0,A.n-2)A.a[i][i+1]=1;
        A.a[A.n-1][A.n-1]=1;
    }
    void formula()
    {
        printf("f(n) =");
        rep(i,0,A.n-1)printf(" (%lld)*f(n-%d) +",A.a[i][0],i+1);
        printf(" (%lld)\n",A.a[A.n-1][0]);
    }
    ll cal(ll n)
    {
        if(n<v.size())return v[n];
        n=n-k+1;
        matrix B,T=A;
        B.n=A.n;
        rep(i,0,B.n)rep(j,0,B.n)B.a[i][j]=i==j?1:0;
        while(n)
        {
            if(n&1)B=B*T;
            n>>=1;
            T=T*T;
        }
        ll ans=0;
        rep(i,0,B.n-1)ans=(ans+v[B.n-2-i]*B.a[i][0]%mo)%mo;
        ans=(ans+B.a[B.n-1][0])%mo;
        while(ans<0)ans+=mo;
        return ans;
    }
}
int main()
{
    vector<ll> V={1,6,3,1,6,3,1,6,3,1,6,3};
    linear::build(V);
    linear::formula();
    ll n;
    while(~scanf("%lld",&n))
    {
        printf("%lld\n",linear::cal(n));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39515621/article/details/80585417