O(logN)求斐波那契數列第N項:動態規劃、矩陣分治

logN求Fibonacci數列第N項

  • 斐波那契數列通項公式 F(i)=F(i1)+F(i2)F(i)=F(i-1)+F(i-2)
  • 以下介紹兩種複雜度爲O(logN)O(logN)的算法
  • 兩種方法思想類似

1. 動態規劃

  • 當我們嘗試將F(i1)F(i-1)F(i2)F(i-2)進行繼續拆解時(始終保持兩項),發現兩項的係數始終是斐波那契數列中的某一相鄰兩項
  • 因此,F(i)一定可以可以由4個項組成
    • F(i)=F(ia)F(ix)+F(ib)F(i(x1))F(i)=F(i-a)F(i-x)+F(i-b)F(i-(x-1))
    • a=0a=0時則爲F(i)=F(2)F(i1)+F(1)F(i2)F(i)=F(2)F(i-1)+F(1)F(i-2)
    • 且a,b,x具有一定關係

思路

a,b,x有多種可能
此時需要算4個小項才能合併到1個大項
若這4個項中有相同的項,則可以簡化計算
此外,越大的項,計算的複雜度越大,反之越小

因此我們可以想到取 i/2i/2 附近的項

  • 通過遞推得(也可以用較小的ii找找規律)
    • ii爲奇數時,F(i)=F2(i/2+1)+F2(i/2)F(i)=F^2(i/2+1)+F^2(i/2)
    • ii爲偶數時,F(i)=F(i/2+1)F(i/2)+F(i/2)F(i/21)F(i)=F(i/2+1)*F(i/2)+F(i/2)*F(i/2-1)

即我們將一個遞推項分成2或3項分別計算後再合併

  • 可以分析複雜度爲O(logN)O(logN)
  • :在實際程序實現中,不能直接return F(i/2+1)*F(i/2)+F(i/2)*F(i/2-1),這樣會造成計算冗餘
  • 應該先保存下結果,計算後return

DP代碼

int Fibonacci(int x) // logN
{
    if (x == 1 || x == 2) return 1;
    if (x % 2 != 0)
    {
        int F1 = Fibonacci(x / 2 + 1), F2 = Fibonacci(x / 2);
        return F1 * F1 + F2 * F2;
    }
    else
    {
        int F1 = Fibonacci(x / 2 + 1), F2 = Fibonacci(x / 2), F3 = Fibonacci(x / 2 - 1);
        return F1 * F2 + F2 * F3;
    }
}

2. 矩陣分治

  • 結合矩陣的知識,我們可以知道斐波那契數列符合
  • 於是隨着等號右邊中的斐波那契項的項數ii降低,該矩陣就不斷左乘

因此可以基於矩陣結合律計算若干項矩陣乘積,再左乘F(1)andF(2)F(1) and F(2)

  • 設該矩陣爲A
  • An=An/2An/2A^n=A^{n/2}*A^{n/2}
  • 其中An/2A^{n/2}只需計算一項
  • 依次類推

這就是快速冪的思想

快速冪講解

代碼

int[2][2] Matrix(int x)
{
    int a[2][2] = { { 1, 1 }, { 0, 1 } };
    if (x == 1) return a;
    int b[2][2] = Matrix(x / 2);
    if (x % 2 == 0) return b * b; // 省略矩陣乘法
    return b * b * a;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章