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} \]
幫忙在評論區裏留下一些建設性言論幫助咱們共同進步吧!