分享

斐波那契数列3种解法(朴素递归、动态规划、数学归纳)及算法分析

 dinghj 2018-12-02

本文来自网易公开课的<算法导论>第3讲分治法。让我对分治法的使用有了一个新的认识斐波那契数列,又称黄金分割数列,F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)

       下面我将使用Java(是的,又是Java,不过我觉得没什么问题,算法嘛,重在思想)来分别实现这三种方法。后来视频看到一半,发现使用朴素递归方法求解该问题时,有许多重复的子问题,那么这就符合动态规划的基本思想了,可以将采用自底向上的求解顺序,保存子问题的结果。这样做的算法时间是:theta(n)

       1.朴素递归:自定向向下求解问题,导致了大量的重复求解

       2.动态规划:准确的讲应该是一种自底向上的"动态规划"思想解法。

       3.数学归纳法(线性代数矩阵连乘公式)

       设Fn表示第n个斐波那契数,那么有定理:

       证明:当n=1,F0=0,F1=1,F2=2,即:

       那么假设定理成立,将n-1带入可得表达式:

       即:

       因为等式

       恒成立,于是假设成立。

       这样就将斐波那契数列转换成了n乘方问题了,那么n乘方问题的算法时间为什么不是,而是呢?这里可以使用分治法求解n乘方问题,可将n个数连乘分成左右相等的两部分(偶数d的话n/2和n/2,奇数的话(n-1)/2和(n-1)/2)。就有:

这样n个数连乘,只需划分次即可。

       代码如下:

  1. package com.wly.algorithmproblem;
  2. /**
  3. * 解斐波拉契数列问题,使用三种方法:朴素递归解法、自底向上的动态规划思想解法、线性代数矩阵连乘公式解法
  4. * @author wly
  5. * @date 2013-11-28
  6. *
  7. */
  8. public class FibonacciSequence {
  9. private static int TESTCASE = 43;
  10. private static int[][] matrixUnit = {{1,1},{1,0}};
  11. public static void main(String[] args) {
  12. System.out.println("测试规模:" + TESTCASE);
  13. //---朴素递归解斐波那契数列问题测试
  14. long startT = System.currentTimeMillis();
  15. System.out.println("朴素递归:" + simpleRecurrence(TESTCASE));
  16. System.out.println("朴素递归用时:" + (System.currentTimeMillis()-startT));
  17. //---自底向上(动态规划)解斐波那契数列问题测试
  18. startT = System.currentTimeMillis();
  19. System.out.println("自底向上(动态规划):" + downToTopReslove(TESTCASE));
  20. System.out.println("自底向上(动态规划)用时:" + (System.currentTimeMillis()-startT));
  21. //---线性代数矩阵解斐波那契数列问题测试
  22. int[][] mResult = {{1,1},{1,0}};
  23. startT = System.currentTimeMillis();
  24. int n = 1;
  25. while(n<TESTCASE) {
  26. mResult = matrixMutiple(mResult, matrixUnit);
  27. n ++;
  28. }
  29. System.out.println("线性代数矩阵公式:" + mResult[0][1]);
  30. System.out.println("线性代数矩阵公式用时:" + (System.currentTimeMillis()-startT));
  31. //分治法求m的n连乘测试
  32. System.out.println("分治法求2的23连乘:" + pow(2, 23));
  33. //两矩阵相乘方法测试
  34. /*
  35. int[][] matrix1 = {{2,3,4},{1,2,3}};
  36. int[][] matrix2 = {{2,4},{3,5},{4,6}};
  37. int[][] result = new int[matrix1.length][matrix2[0].length];
  38. int[][] resultS = matrixMutiple(matrix1,matrix2,result);
  39. System.out.println();
  40. */
  41. }
  42. /**
  43. * 朴素递归
  44. * @param n
  45. * @return 第n个斐波那契数
  46. */
  47. public static int simpleRecurrence(int n) {
  48. if(n == 0) {
  49. return 0;
  50. }
  51. if(n == 1 || n == 2) {
  52. return 1;
  53. }
  54. return simpleRecurrence(n-1) + simpleRecurrence(n-2);
  55. }
  56. /**
  57. * 自底向上包含"动态规划"思想的解法
  58. * @param n
  59. * @return 第n个斐波那契数
  60. */
  61. public static int downToTopReslove(int n) {
  62. if(n == 0) {
  63. return 0;
  64. } else if(n == 1 || n == 2) {
  65. return 1;
  66. } else {
  67. int[] fibonacciArray = new int[n+1]; //fibonacciArray[i]表示第i个斐波那契数
  68. fibonacciArray[0] = 0;
  69. fibonacciArray[1] = 1;
  70. fibonacciArray[2] = 1;
  71. for(int i=3;i<=n;i++) { //注意由于fibonacciArray[0]表示第0个元素,这里是i<=n,而不是i<n
  72. fibonacciArray[i] = fibonacciArray[i-1] + fibonacciArray[i-2];
  73. }
  74. return fibonacciArray[fibonacciArray.length-1];
  75. }
  76. }
  77. /**
  78. * 分治法求解factor的n次方
  79. * @param factor 基数
  80. * @param n 次方数
  81. * @return
  82. */
  83. public static long pow(long factor,int n) {
  84. if(n == 0) {
  85. return 1;
  86. } else if(n == 1){
  87. return factor;
  88. } else {
  89. if(n % 2 == 1) { //乘法数为奇数
  90. return pow(factor,(n-1)/2) * pow(factor, (n-1)/2) * factor;
  91. } else { //乘方数为偶数
  92. return pow(factor, n/2) * pow(factor, n/2);
  93. }
  94. }
  95. }
  96. /**
  97. * 两矩阵相乘
  98. * @param matrix1
  99. * @param matrix2
  100. * @return
  101. */
  102. public static int[][] matrixMutiple(int[][] matrix1,int[][] matrix2) {
  103. int[][] result = new int[matrix1.length][matrix2[0].length];
  104. for(int i=0;i<matrix1.length;i++) {
  105. for(int j=0;j<matrix2[i].length;j++) {
  106. int temp = 0;
  107. for(int k=0;k<matrix1[0].length;k++) {
  108. temp = matrix1[i][k] * matrix2[k][j] + temp;
  109. }
  110. result[i][j] = temp;
  111. }
  112. }
  113. return result;
  114. }
  115. }
       运行结果:
  1. 测试规模:43
  2. 朴素递归:433494437
  3. 朴素递归用时:1669
  4. 自底向上(动态规划):433494437
  5. 自底向上(动态规划)用时:0
  6. 线性代数矩阵公式:433494437
  7. 线性代数矩阵公式用时:1
  8. 分治法求2的23连乘:8388608

       这里有一点需要说明的是,代码中已经包含了m的n次方的代码。本来想讲它结合到第三种线性代数矩阵连乘的解法中的,但是后来发现那样的话,代码显得很乱,于是就只是简单的使用while来连乘了n次实现。总的来说还是实现了三种不同的解斐波拉契数列的方法,希望能够大家一点参考

       O啦~~~

       转载请保留出处:

       谢谢!!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多