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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章