4.1求斐波拉契数列的第N项(O(logN))

版权声明:本文采用 知识共享署名4.0 国际许可协议进行许可。 https://blog.csdn.net/youmian6868/article/details/85103156
题目

给定整数N,返回斐波拉契数列的第N项。

O(2^N)的方法:
/**
 * 暴力递归(O(2^N))
 *
 * @param n 给定整数
 * @return 斐波拉契数列第n项
 */
public int f1(int n) {
    if (n < 1) {
        return 0;
    }
    if (n == 1 || n == 2) {
        return 1;
    }
    return f1(n - 1) + f1(n - 2);
}
O(N)的方法:
/**
 * 顺序计算(O(N))
 *
 * @param n 给定整数
 * @return 斐波拉契数列第n项
 */
public int f2(int n) {
    if (n < 1) {
        return 0;
    }
    if (n == 1 || n == 2) {
        return 1;
    }

    int res = 1;
    int pre = 1;
    int tmp = 0;
    for (int i = 3; i <= n; i++) {
        tmp = res;
        res += pre;
        pre = tmp;
    }
    return res;
}
O(logN)的方法:

如果递归式严格遵循F(N)=F(N-1)+F(N-2),对于求第N项的值,有矩阵乘法的方式可以将时间复杂度降至O(logN)。因为上式是一个二阶递推数列,所以一定可以用矩阵乘法的形式表示,其中状态矩阵为2*2的矩阵。代入数列前四项,可求得状态矩阵为{{1,1},{1,0}}。

所以问题就变成了如何用最快方法求一个矩阵的N次方的问题,而求矩阵N次方的问题类似求一个整数N次方,是一个能够在O(logN)时间内解决的问题。

先给出求一个整数N次方的O(logN)方法:

/**
 * 求整数的N次方(O(logN))
 *
 * 例如我们想求12^75的值,快速解法如下:
 * 1. 75的二进制数形式为1001011
 * 2. 12^75=12^64∗12^8∗12^2∗12^1
 * 具体求解的时候,我们先计算12^1,然后根据12^1求12^2,再根据12^2求12^4,以此类推,最后求12^64。
 * 即75的二进制数形式总共为多少位,我们就要在原基础上平方几次。这样,就将复杂度为O(N)的计算降到了O(logN)。
 *
 * @param base     底数
 * @param exponent 指数
 * @return 结果
 */
public int power(int base, int exponent) {
    int tmp = base;
    int result = 1;

    for (; exponent != 0; exponent >>= 1) {
        // 只有当最低位为1时,结果才乘上现在的值
        if ((exponent & 1) != 0) {
            result *= tmp;
        }
        tmp *= tmp; //每移位一次,幂方计算一次
    }

    return result;
}

类似地可以给出用矩阵乘法实现求解斐波拉契数列第N项的O(logN)方法:

    /**
     * 两个矩阵相乘
     *
     * @param m1 矩阵1
     * @param m2 矩阵2
     * @return 相乘结果
     */
    public int[][] muliMatrix(int[][] m1, int[][] m2) {
        int[][] res = new int[m1.length][m2[0].length];

        for (int i = 0; i < m1.length; i++) {
            for (int j = 0; j < m2[0].length; j++) {
                for (int k = 0; k < m2.length; k++) {
                    res[i][j] += m1[i][k] * m2[k][j];
                }
            }
        }

        return res;
    }

    /**
     * 求矩阵m的p次方(类似求整数的N次方)
     *
     * @param m 矩阵m
     * @param p 指数
     * @return 结果
     */
    public int[][] matrixPower(int[][] m, int p) {
        int[][] res = new int[m.length][m[0].length];

        //先把res置为单位矩阵,相当于整数中的1
        for (int i = 0; i < res.length; i++) {
            res[i][i] = 1;
        }

        int[][] tmp = m;
        for (; p != 0; p >>= 1) {
            if ((p & 1) != 0) {
                res = muliMatrix(res, tmp);
            }
            tmp = muliMatrix(tmp, tmp);
        }

        return res;
    }

    /**
     * 用矩阵乘法(O(logN))
     *
     * @param n 给定整数
     * @return 斐波拉契数列第n项
     */
    public int f3(int n) {
        if (n < 1) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return 1;
        }

        int[][] base = {{1, 1}, {1, 0}};
        int[][] res = matrixPower(base, n - 2);

        return res[0][0] + res[1][0];
    }
}

猜你喜欢

转载自blog.csdn.net/youmian6868/article/details/85103156