动态规划:斐波那契数列问题

292 阅读2分钟

斐波那契数列问题

给定整数N,返回斐波那契数列的第N项。类似地,

  • 给定整数N,代表台阶数,一次可以跨2个或者1个台阶,返回有多少种走法
  • 假设农场中成熟的母牛每年只会生1头小母牛,并且永远不会死。第一年农场有1只成熟的母牛,从第二年开始,母牛开始生小母牛。每只小母牛3年之后成熟又可以生小母牛。给定整数N,求出N年后牛的数量。

上述问题可以优化为O(logN)O(logN)的实现。

LeetCode 509 斐波那契数 斐波那契数 (通常用 F(n) 表示)形成的序列称为斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给定 n ,请计算 F(n) 该问题可以通过以下种方式来实现:

    1. 递归实现,时间复杂度为O(2N)O(2^N)
int fib(int n) {
    if (n <= 1) return n;
    if (n == 2) return 1;
    return fib(n-1)+fib(n-2);
}

我们在计算F(N)F(N)的时候前面已经计算过F(n1)F(n-1)可以通过记忆F(n1)F(n-1)来降低计算的复杂度。

    1. 动态规划。时间复杂度为O(N)O(N),空间复杂度为O(N)O(N)
int fib(int n) {    
    vector<int> dp = vector<int>(n+2);
    dp[0] = 0, dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2];
    }
    return dp[n];
}

f[n]f[n]只依赖f[n1]f[n-1]f[n2]f[n-2]可以进行空间压缩,来降低空间复杂度。

    1. 动态规划+滚动数组,时间复杂度为O(N)O(N),空间复杂度为O(1)O(1)
int fib(int n) {
    if (n <= 1) return n;

    int pre = 1, prepre = 0;
    int cur = 1;
    for (int i = 2; i <= n; i++) {
        cur = pre + prepre;
        prepre = pre;
        pre = cur;
    }
    return cur;
}
    1. 矩阵快速幂,时间复杂度为O(log2N)O(log_{2}N),空间复杂度为O(1)O(1)
[F(N)F(N1)]=[F(N1)+F(N2)F(N1)]=[1110][F(N1)F(N2)]可以推出:[F(N)F(N1)]=[1110]N1[F(1)F(0)]\begin{array}{l} \begin{bmatrix} F(N)\\ F(N-1) \end{bmatrix} = \begin{bmatrix} F(N-1) + F(N-2) \\ F(N-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} F(N-1) \\ F(N-2) \end{bmatrix} \\ \\ 可以推出:\\ \begin{bmatrix} F(N)\\ F(N-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{N-1} \begin{bmatrix} F(1)\\ F(0) \end{bmatrix} \end{array}

快速幂算法可以快速计算MNM^NO(log2N)O(log_{2}N)的时间复杂度内计算得到结果。 快速幂算法源码较长,可以参考实现Github源码

    1. 通项公式
斐波那契数F(n)是齐次线性递推,根据递推方程F(N)=F(N1)+F(N2),可得到特征方程x2=x+1求得x1=1+52,x1=152,设通解为F(n)=c1x1n+c2x2n初始值F(0)=0,F(1)=1,代入计算得到c1=15,c2=15由此得:F(n)=15[(1+52)n(152)n]\begin{array}{l} 斐波那契数F(n)是齐次线性递推,根据递推方程F(N) = F(N-1) + F(N-2),可得到特征方程\\ x^2=x+1 \\ 求得x_1=\frac{1+\sqrt{5}}{2},x_1=\frac{1-\sqrt{5}}{2},设通解为 F(n)=c_1x_1^n+c_2x_2^n\\ 初始值F(0)=0,F(1)=1,代入计算得到c_1=\frac{1}{\sqrt{5}},c_2=-\frac{1}{\sqrt{5}}\\ 由此得:F(n)=\frac{1}{\sqrt{5}} \left[\left( \frac{1+\sqrt{5}}{2}\right)^n - \left( \frac{1-\sqrt{5}}{2}\right)^n \right ] \end{array}
int fib(int n) {
    double sqrt5 = sqrt(5);
    double fibN = pow((1 + sqrt5) / 2, n) - pow((1 - sqrt5) / 2, n);
    return round(fibN / sqrt5);
}

类似扩展

    1. 爬楼梯问题

LeetCode 70:爬楼梯问题 设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。 这就是一个斐波那契额数列问题,状态转移方程:F(n)=F(n1)+F(n2)F(n)=F(n-1)+F(n-2)

快速幂算法递归公式

[F(N)F(N1)]=[F(N1)+F(N2)F(N1)]=[1110][F(N1)F(N2)]可以推出:[F(N)F(N1)]=[1110]N1[F(1)F(0)]\begin{array}{l} \begin{bmatrix} F(N)\\ F(N-1) \end{bmatrix} = \begin{bmatrix} F(N-1) + F(N-2) \\ F(N-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} F(N-1) \\ F(N-2) \end{bmatrix} \\ \\ 可以推出:\\ \begin{bmatrix} F(N)\\ F(N-1) \end{bmatrix} = \begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix}^{N-1} \begin{bmatrix} F(1)\\ F(0) \end{bmatrix} \end{array}

实现github源码

    1. 第N个泰波那契数

LeetCode1137 第 N 个泰波那契数 泰波那契序列 Tn 定义如下: T0=0, T1=1, T2=1, 且在n>= 0 的条件下Tn+3=Tn+Tn+1+Tn+2。给你整数n,请返回第n个泰波那契数Tn的值

快速幂算法递归公式

[TnTn1Tn2]=[Tn3+Tn2+Tn1Tn1Tn2]=[111100010][Tn1Tn2Tn3]从而,可以推出:[TnTn1Tn2]=([111100010])n2[T2T1T0]其中:T0=0,T1=1,T2=1\begin{array}{l} \begin{bmatrix} T_n\\ T_{n-1}\\ T_{n-2} \end{bmatrix} = \begin{bmatrix} T_{n-3} + T_{n-2} + T_{n-1}\\ T_{n-1}\\ T_{n-2} \end{bmatrix} = \begin{bmatrix} 1 & 1 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} T_{n-1} \\ T_{n-2} \\ T_{n-3} \end{bmatrix} \\ \\ 从而,可以推出:\\ \begin{bmatrix} T_n\\ T_{n-1}\\ T_{n-2} \end{bmatrix} = \left (\begin{bmatrix} 1 & 1 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix} \right )^{n-2}\begin{bmatrix} T_2\\ T_{1}\\ T_{0} \end{bmatrix} \\ \\ 其中:T_0=0, T_1=1, T_2=1 \end{array}

动态规划和快速幂等实现github源码

参考资料

  • 《程序员代码面试指南:IT名企算法与数据结构题目最优解(第2版)》