在次線性時間內計算線性遞歸數列

date: 2019-09-13ios

簡介

標題看着很高端,其實就是在\(O(\log_2 N)\)內計算出線性遞歸數列的某一項(好像什麼都沒有解釋清楚啊)c++

斐波那契數列的快速計算

咱們先來看一個題目:算法

題目描述:優化

​ 給你一個數字\(n\),你須要輸出斐波那契數列的第n項。spa

注意:第一項和第二項都爲\(1\)code

題目輸入:遞歸

​ 第一行一個整數\(n\),保證$1 \leq n \leq 1e18 $。ci

題目輸出:數學

​ 輸出斐波那契數列的第n項,對\(1e9+7\)取模。it

這道題目小數據範圍內很簡單(一道水題),可是當數據範圍擴展到\(1 \leq n \leq 1e18\)時,一切都不同了。你須要一個\(O(N \log_2 N)\)的算法來解決它。這裏咱們須要用到矩陣運算。

遞推式

斐波那契數列的遞推式這個你們應該都會寫吧。。。

算了我仍是來寫一下好了:
\[ f_{i}=f_{i-1}+f_{i-2}\\ f_{0}=1\\ f_{1}=1 \]

轉化爲矩陣計算

這裏咱們把計算\(f_0\)\(f_1\)包含在一個2行1列的矩陣中:
\[ \begin{pmatrix} f_{0}\\ f_{1} \end{pmatrix} \]
以後咱們能夠得出以下的式子:
\[ \begin{pmatrix} 0&1\\ 1&1 \end{pmatrix} \begin{pmatrix} f_{0}\\ f_{1} \end{pmatrix} = \begin{pmatrix} f_{1}\\ f_{2} \end{pmatrix} \]
以後咱們又能夠得出以下的式子:
\[ \begin{pmatrix} 0&1\\ 1&1 \end{pmatrix} ( \begin{pmatrix} 0&1\\ 1&1 \end{pmatrix} \begin{pmatrix} f_{0}\\ f_{1} \end{pmatrix} ) = \begin{pmatrix} f_{2}\\ f_{3} \end{pmatrix} \]
因爲矩陣的結合律,咱們又能夠寫成:
\[ \begin{pmatrix} 0&1\\ 1&1 \end{pmatrix}^2 \begin{pmatrix} f_{0}\\ f_{1} \end{pmatrix} = \begin{pmatrix} f_{2}\\ f_{3} \end{pmatrix} \]
以後一步步遞推,就得到了:
\[ \begin{pmatrix} 0&1\\ 1&1 \end{pmatrix}^n \begin{pmatrix} f_{0}\\ f_{1} \end{pmatrix} = \begin{pmatrix} f_{n}\\ f_{n+1} \end{pmatrix} \]
鏘鏘~咱們就得到了一個複雜度大頭是矩陣冪運算的遞推式了。衆所周知,因爲結合律,冪運算能夠優化到\(O(N\log_2 N)\),這是能夠接受的。

代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll mod=1000000007;

vector<vector<ll> > mul(const vector<vector<ll> > & a,const vector<vector<ll> > & b){
    vector<vector<ll> > res(2,vector<ll>(2,0));
    res[0][0]=(a[0][0]*b[0][0]+a[0][1]*b[1][0])%mod;
    res[0][1]=(a[0][0]*b[0][1]+a[0][1]*b[1][1])%mod;
    res[1][0]=(a[1][0]*b[0][0]+a[1][1]*b[1][0])%mod;
    res[1][1]=(a[1][0]*b[0][1]+a[1][1]*b[1][1])%mod;
    return res;
}

vector<vector<ll> > pow(vector<vector<ll> > a,ll n){
    vector<vector<ll> > res(2,vector<ll>(2,0));
    res[0][0]=1;
    res[1][1]=1;
    while(n>0){
        if(n&1){
            res=mul(res,a);
        }
        a=mul(a,a);
        n>>=1;
    }
    return res;
}

ll n;

main(){

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin>>n;
    vector<vector<ll> > a(2,vector<ll>(2,1));
    a[0][0]=0;
    a=pow(a,n);
    cout<<a[0][1]<<endl;

    return 0;
}

擴展至通用

遞推式

直接上數學式:
\[ f_{i+M}=a_0f_{i}+a_1f_{i+1}+\cdots+a_{M-2}f_{i+M-2}+a_{M-1}f_{i+M-1} \]

矩陣計算

首先,咱們把這個數學式定義爲一個\(M\)\(1\)列的矩陣:
\[ \begin{pmatrix} f_i\\ f_{i+1}\\ f_{i+2}\\ \vdots\\ f_{i+M-2}\\ f_{i+M-1} \end{pmatrix} \]
以後,遞推式就能夠這麼寫了:(\(a_k\)表示\(f_{i+M}\)加上了\(a_k f_{i+k}\)
\[ \begin{pmatrix} 0 & 1 & 0 & \cdots & 0 & 0\\ 0 & 0 & 1 & \cdots & 0 & 0\\ 0 & 0 & 0 & \cdots & 0 & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots\\ 0 & 0 & 0 & \cdots & 0 & 1\\ a_0 & a_1 & a_2 & \cdots & a_{M-2} & a_{M-1} \end{pmatrix} \begin{pmatrix} f_i\\ f_{i+1}\\ f_{i+2}\\ \vdots\\ f_{i+M-2}\\ f_{i+M-1} \end{pmatrix} = \begin{pmatrix} f_{i+1}\\ f_{i+2}\\ f_{i+3}\\ \vdots\\ f_{i+M-1}\\ f_{i+M} \end{pmatrix} \]

因爲結合律,遞推式能夠寫成:
\[ \begin{pmatrix} 0 & 1 & 0 & \cdots & 0 & 0\\ 0 & 0 & 1 & \cdots & 0 & 0\\ 0 & 0 & 0 & \cdots & 0 & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots\\ 0 & 0 & 0 & \cdots & 0 & 1\\ a_0 & a_1 & a_2 & \cdots & a_{M-2} & a_{M-1} \end{pmatrix}^n \begin{pmatrix} f_0\\ f_{1}\\ f_{2}\\ \vdots\\ f_{M-2}\\ f_{M-1} \end{pmatrix} = \begin{pmatrix} f_{n}\\ f_{n+1}\\ f_{n+2}\\ \vdots\\ f_{n+M-2}\\ f_{n+M-1} \end{pmatrix} \]

謝謝觀看

幫忙在評論區裏留下一些建設性言論幫助咱們共同進步吧!

相關文章
相關標籤/搜索