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, 所以请大家指出!
- 2.9 Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- Fibonacci数列
- fibonacci数列
- IBOutlet 以及 IBAction 意义
- 支付宝 iOS SDK 官方下载页面
- 全新编程模式---站立编程--独创编程方式,可能会流行
- 强连通分量 模板
- 优化 Go 中的 map 并发存取
- 2.9 Fibonacci数列
- iOS视图控制器的跳转方法
- 线段树的两种查询方式
- 分词器之NLPIR加密文件在哪
- IK分词加入标点符号
- 斯特林数 组合数
- hdu5399(2015多校9)--Too Simple
- 总线设备驱动模型——设备篇
- HDU 5399