Go&Java算法之斐波那契数列

232 阅读1分钟

这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战

斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1

F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

 

示例 1:

输入:n = 2

输出:1

示例 2:

输入:n = 5

输出:5  

提示:

0 <= n <= 100

题解

算法一:动态规划(Java)

斐波那契数的边界条件是 F(0)=0 和 F(1)=1。当 n>1 时,每一项的和都等于前两项的和,因此有如下递推关系:

F(n)=F(n-1)+F(n-2)

由于斐波那契数存在递推关系,因此可以使用动态规划求解。动态规划的状态转移方程即为上述递推关系,边界条件为 F(0) 和 F(1)。

根据状态转移方程和边界条件,可以得到时间复杂度和空间复杂度都是 O(n) 的实现。由于 F(n) 只和 F(n-1) 与 F(n-2) 有关,因此可以使用「滚动数组思想」把空间复杂度优化成 O(1)。

class Solution {

    public int fib(int n) {
        if(n <= 1) {
            return n;
        }
        int a = 0;
        int b = 1;
        int sum = 0;
        for(int i = 2; i <= n; i++) {
            sum = (a + b) % 1000000007;
            a = b;
            b = sum;
        }
        return sum;
    }
}

时间复杂度:O(N)

空间复杂度:O(1)

算法一:动态规划(Go)

思路同上

func fib(n int) int {
    const mod int = 1e9 + 7
    if n < 2 {
        return n
    }
    p, q, r := 0, 0, 1
    for i := 2; i <= n; i++ {
        p = q
        q = r
        r = (p + q) % mod
    }
    return r
}

时间复杂度:O(N)

空间复杂度:O(1)

算法二:矩阵快速幂(Java)

对于本题,某个 f(n)依赖于 f(n - 1)和 f(n - 2,将其依赖的状态存成列向量:

[f(n1)f(n2)]\begin{bmatrix} f(n - 1)\\ f(n - 2) \end{bmatrix}

目标值 f(n)f(n) 所在矩阵为:

[f(n)f(n1)]\begin{bmatrix} f(n)\\ f(n - 1) \end{bmatrix}

根据矩阵乘法,不难发现:

[f(n)f(n1)]=[1110][f(n1)f(n2)]\begin{bmatrix} f(n)\\ f(n - 1) \end{bmatrix} = \begin{bmatrix} 1& 1\\ 1& 0 \end{bmatrix} * \begin{bmatrix} f(n - 1)\\ f(n - 2) \end{bmatrix}

我们令:

mat = [1110]\begin{bmatrix} 1& 1\\ 1& 0 \end{bmatrix}

起始时,我们只有 [f(1)f(0)]\begin{bmatrix} f(1)\\ f(0) \end{bmatrix},根据递推式得:

[f(n)f(n1)]=matmat...mat[f(1)f(0)]\begin{bmatrix} f(n)\\ f(n - 1) \end{bmatrix} = mat * mat * ... * mat * \begin{bmatrix} f(1)\\ f(0) \end{bmatrix}

再根据矩阵乘法具有「结合律」,最终可得:

[f(n)f(n1)]=matn1[f(1)f(0)]\begin{bmatrix} f(n)\\ f(n - 1) \end{bmatrix} = mat^{n - 1} * \begin{bmatrix} f(1)\\ f(0) \end{bmatrix}

计算 mat^{n - 1}可以套用「快速幂」进行求解。

class Solution {
    static final int MOD = 1000000007;

    public int fib(int n) {
        if (n < 2) {
            return n;
        }
        int[][] q = {{1, 1}, {1, 0}};
        int[][] res = pow(q, n - 1);
        return res[0][0];
    }

    public int[][] pow(int[][] a, int n) {
        int[][] ret = {{1, 0}, {0, 1}};
        while (n > 0) {
            if ((n & 1) == 1) {
                ret = multiply(ret, a);
            }
            n >>= 1;
            a = multiply(a, a);
        }
        return ret;
    }

    public int[][] multiply(int[][] a, int[][] b) {
        int[][] c = new int[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                c[i][j] = (int) (((long) a[i][0] * b[0][j] + (long) a[i][1] * b[1][j]) % MOD);
            }
        }
        return c;
    }
}