2.9 Fibonacci数列

来源:互联网 发布:mysql 5.7 json 函数 编辑:程序博客网 时间:2024/06/05 19:28

1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述

这里写图片描述

3. 问题分析

这个问题, 也是一个经典的问题, 但是通常, 我们看到的解法大概就是递归思路, 以及循环的思路两种, 这里给出了一个巧妙的解法, 利用矩阵相乘的性质, 来求解fibonacci 数列
解法一 : 常规的递归思路, 递归退出条件为n 为0 或者1
解法二 : 因为fibonacci的递归思路, 中实际上存在很多的重复计算, 所以这里在在常规的递归思路, 上面加上了缓存
解法三 : 利用尾递归优化了求解fibonacci, 实质将其转换为了循环
解法四 : 循环求解fibonacci数列
解法五 : 假设存在一个矩阵A使得 [f(n+1), f(n) ] = [f(n), f(n-1) ] * A, 利用fibonacci数列的性质[f(n+1) = f(n) + f(n-1) ]作为条件 解之得 A : [[1, 1], [1, 0] ]

然后递归下去得到 如下结论 : [f(n+1), f(n) ] = [f(n), f(n-1) ] * A = [f(n-1), f(n-2) * A ] * A = [f(n-1), f(n-2) ] * A^2 = … = [f(1), f(0) ] * A^n
所以, 如果我们想求[f(n+1), f(n) ], 我们只需要求出A^n, 即可, 然后乘上[f(1), f(0) ]([1, 1]), 即为所求

编程之美分析原图 :
这里写图片描述

4. 代码

/** * file name : Test15Fibonacci.java * created at : 10:43:54 AM May 23, 2015 * created by 970655147 */package com.hx.test03;public class Test16Fibonacci {    // Fibonacci数列    public static void main(String []args) {            int n = 23;            int res = -1;            res = fibonacci01(n);            Log.log(res);            res = fibonacci02(n);            Log.log(res);            res = fibonacci03(n, 1, 1);            Log.log(res);            res = fibonacci04(n);            Log.log(res);            res = fibonacci05(n);            Log.log(res);            Log.horizon();    }    // 递归获取fibonacci数列    public static int fibonacci01(int n) {        if(n == 0 || n == 1) {            return 1;        }        return fibonacci01(n - 2) + fibonacci01(n - 1);    }    // 通过加入了缓存 获取fibonacci数列    static Map<Integer, Integer> fibonacciMap = new HashMap<Integer, Integer>();    public static int fibonacci02(int n) {        if(n == 0 || n == 1) {            return 1;        }        int tmp01 = -1, tmp02 = -1;        if(fibonacciMap.containsKey(n - 1)) {            tmp01 = fibonacciMap.get(n - 1);        } else {            tmp01 = fibonacci02(n - 1);            fibonacciMap.put(n - 1, tmp01);        }        if(fibonacciMap.containsKey(n - 2)) {            tmp02 = fibonacciMap.get(n - 2);        } else {            tmp02 = fibonacci02(n - 2);            fibonacciMap.put(n - 2, tmp02);        }        return tmp01 + tmp02;    }    // 递归获取fibonacci数列, 尾递归优化    public static int fibonacci03(int n, int res01, int res02) {        if(n == 0 || n == 1) {            return res02;        }        return fibonacci03(n-1, res02, res01+res02);    }    // 通过累加获取fibonacci数列    public static int fibonacci04(int n) {        int i = 0, tmp01 = 1, tmp02 = 1;        for(i=1; i<n; i+=2) {            tmp01 = tmp01 + tmp02;            tmp02 = tmp01 + tmp02;        }        if(n == i) {            return tmp02;        } else {            return tmp01;        }    }    // 获取A矩阵[ [1, 1], [1, 0] ] 的n-1阶方幂    // 然后在和[f1, f0] 做乘法运算    // 得到的Tuple的第一个元素即为f(n)    public static int fibonacci05(int n) {        Matrix n1Matrix = getAMatrix(n - 1);        Tuple fibonacci01 = Tuple.getFibonacci01();        Tuple res = fibonacci01.mul(n1Matrix);        return res.getFn();    }    // 获取A矩阵[ [1, 1], [1, 0] ] 的n阶方幂    // 例如 42 = 32 + 8 + 2  所以matrix(42) = matrix(1) * matrix(32) * matrix(8) * matrix(2)    // 然后返回结果     static Map<Integer, Matrix> matrixCache = new HashMap<Integer, Matrix>();    public static Matrix getAMatrix(int n) {        Matrix res = Matrix.getUnitMatrix();        while(n > 0) {            int higestOneBit = Integer.highestOneBit(n);            if(matrixCache.containsKey(higestOneBit)) {                res = res.mul(matrixCache.get(higestOneBit));            } else {                Matrix tmp = getIntegerMatrix(higestOneBit);                matrixCache.put(higestOneBit, tmp);                res = res.mul(tmp);            }            n -= higestOneBit;        }        return res;    }    // A^(x+y) = A^x * A^y    // 如果n为 1  则返回A矩阵[ [1, 1], [1, 0] ]    // 否则 A^n = A^(n/2) * A^(n/2)    // 获取2的幂次方的 A^n, 矩阵, n必需为2的幂        private static Matrix getIntegerMatrix(int n) {        if(n == 1) {            matrixCache.put(1, Matrix.getAMatrix());            return Matrix.getAMatrix();        }        int other = n >> 1;        Matrix tmp01 = null, tmp02 = null;        if(matrixCache.containsKey(other)) {            tmp02 = matrixCache.get(other);        } else {            tmp02 = getIntegerMatrix(other);            matrixCache.put(other, tmp02);        }        return tmp02.mul(tmp02);    }    // 一个int元组    // fibonacci01表示( fibonacci(1), fibonacci(0) )    static class Tuple {        // [f(1), f(0)]        private static Tuple fibonacci01 = new Tuple(new int[]{ 1, 1 });         // 存放当前元组的数据        int[] data;        // 初始化        public Tuple() {        }        public Tuple(int[] data) {            this.data = data;        }        // 获取[f(1), f(0)]        public static Tuple getFibonacci01() {            return fibonacci01;        }        // 当前元组与matrix的乘积  结果为一个元组        public Tuple mul(Matrix matrix) {            int[] tmp = new int[data.length];            for(int col=0; col<matrix.matrix[0].length; col++) {                int sum = 0;                for(int row=0; row<matrix.matrix.length; row++) {                    sum += data[row] * matrix.matrix[row][col];                }                tmp[col] = sum;            }            return new Tuple(tmp);        }        // getter         // f(n) & f(n-1)        public int getFn() {            return data[0];        }        public int getFnSub1() {            return data[1];        }        // 打印当前元组        public void print() {            for(int i=0; i<data.length; i++) {                Log.logWithoutLn(data[i] + " ");            }            Log.enter();        }    }    // 矩阵表示    static class  Matrix {        // A_Matrix表示A矩阵  fibonacci可以通过A矩阵来计算        // UNIT_MATRIX表示单位矩阵        // DEFAULT_SIDE_LEN为默认数组长度        private static Matrix A_Matrix = new Matrix(new int[][]{ {1, 1}, {1, 0} });        private static Matrix UNIT_MATRIX = new Matrix(new int[][] { {1, 0}, {0, 1} });        private static int DEFAULT_SIDE_LEN = 2;        // 存储当前矩阵的数据        int[][] matrix;        // 初始化        public Matrix() {            matrix = new int[DEFAULT_SIDE_LEN][DEFAULT_SIDE_LEN];        }        public Matrix(int[][] matrix) {            this.matrix = matrix;        }        // setter & getter        public static Matrix getAMatrix() {            return A_Matrix;        }        public static Matrix getUnitMatrix() {            return UNIT_MATRIX;        }        // 加法        public Matrix add(Matrix other) {            Matrix tmp = new Matrix();            for(int row=0; row<matrix.length; row++) {                for(int col=0; col<matrix[0].length; col++) {                    tmp.matrix[row][col] = matrix[row][col] + other.matrix[row][col];                }            }            return tmp;        }        // 减法        public Matrix sub(Matrix other) {            Matrix tmp = new Matrix();            for(int row=0; row<matrix.length; row++) {                for(int col=0; col<matrix[0].length; col++) {                    tmp.matrix[row][col] = matrix[row][col] - other.matrix[row][col];                }            }            return tmp;        }        // 乘法        public Matrix mul(Matrix other) {            if(matrix.length != other.matrix[0].length) {                throw new RuntimeException("this two matrix can't multiply...");            }            Matrix tmp = new Matrix();            for(int row=0; row<matrix.length; row++) {                for(int col=0; col<matrix[0].length; col++) {                    int sum = 0;                    for(int i=0; i<matrix.length; i++) {                        sum += matrix[row][i] * other.matrix[i][col];                    }                    tmp.matrix[row][col] = sum;                }            }            return tmp;        }        // 打印信息        public void print() {            for(int row=0; row<matrix.length; row++) {                for(int col=0; col<matrix[0].length; col++) {                    Log.logWithoutLn(matrix[row][col] + " ");                }                Log.enter();            }        }    }}

5. 运行结果

这里写图片描述

6. 总结

前几种思路, 都比较常规, 最后一种思路是非常妙的, 利用了线性代数中矩阵相乘的性质, 并缓存A矩阵的2的幂次方的矩阵的值, 大大的降低了求解A^(n-1)的时间复杂度, 从而保证了计算fibonacci 数的效率

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

0 0
原创粉丝点击