经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
斐波那契数列的三种C++实现及时间复杂度分析
来源:cnblogs  作者:GarrettLu  时间:2018/12/11 9:11:37  对本文有异议

斐波那契数列定义:F(1)=1, F(2)=1, F(n)=F(n-1) + F(n-2) (n>2)

如何计算斐波那契数 F(n) 及时间复杂度 T(n) 呢?

我参考了一些资料总结了以下3种方法:递归法、顺序法和矩阵乘法,并给出了基于C++的简单代码实现和时间复杂度分析。

如有不当,欢迎指正。

方法1:递归法

实现:

  1. #include <stdio.h>
  2. #include <iostream>
  3.  
  4. using namespace std;
  5. long long Fibonacci1(int n)
  6. {
  7. if (n < 1)
  8. {
  9. return 0;
  10. }
  11. if (n == 1 || n == 2)
  12. {
  13. return 1;
  14. }
  15. return Fibonacci1(n - 1) + Fibonacci1(n - 2);
  16. }
  17. int main()
  18. {
  19. char cont = 'y';
  20. int n = 0;
  21. long long f = 0;
  22. cout << "Enter an integer:" << endl;
  23. while (cin >> n)
  24. {
  25. f = Fibonacci1(n);
  26. cout << "Fibonacci1(" << n << ") = " << f << endl;
  27. cout << "Continue(y or n)?" << endl;
  28. cin >> cont;
  29. if (cont == 'y' || cont == 'Y')
  30. {
  31. cout << "Enter an integer:" << endl;
  32. }
  33. else
  34. break;
  35. }
  36. return 0;
  37. }
View Code

时间复杂度:

  设计算F(n)时调用递归的次数为call(n),T(n) = O(call(n))。

  1. 数学归纳法(没兴趣的可以直接看下面的方法2)

  当n = 1, 2时,F(n) = 1,call(n) = 1,T(n) = O(1)

  当n > 2时,F(n) = F(n-1) + F(n-2),call(n) = call(n-1) + call(n-2) + 1(执行加法运算)。

  n = 3时,call(3) = call(2) + call(1) + 1 = 3

  n = 4时,call(4) = call(3) + call(2) + 1 = 5

  n = 5时,call(5) = call(4) +call(3) + 1 = 9

  ……

  注意到:F(3) = 2,call(3) = 3

      F(4) = 3,call(4) = 5

      F(5) = 5,call(5) = 9

  由此猜测call(n) = 2 * F(n) - 1,下面用数学归纳法证明:

  当n = 3时,等式成立。

  假设n = k(k ≥ 3) 等式成立,即有:

  call(k) = 2 * F(k) - 1

  当n = k + 1时,

  F(k+1) = F(k) + F(k-1)

  call(k+1) = call(k) + call(k-1) + 1 = 2 * F(k) - 1 + 2 * F(k-1) -1 + 1 = 2 * F(k+1) - 1

  所以,当n = k + 1时,等式也成立。

  综上,call(n) = 2 * F(n) - 1 对 n ≥ 2都成立

  所以,计算F(n)的时间复杂度T(n) = O(2 * F(n) - 1) = O(F(n)) ,

          

T(n)近似为O(2n)

  F(n)的计算可以参考“斐波那契数列”的百度百科:https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97

  2. 二叉树法

  观察以下二叉树:

                f(5)

                 /    \

              f(4)   f(3)

             /  \   /  \

              f(3)  f(2) f(2)   f(1)

           /   \

          f(2)  f(1)

  call(n)可以转化为求二叉树所有节点的个数。n = 5时,二叉树有4层,第一层有1个,第二层2个,第三层4个,第四层有2个。应用等比数列求和有call(n) = (1-2n-2)/(1-2) + 2 = 2n-2 + 1。T(n) = O(call(n)) = O(2n-2 + 1) = O(1/4 * 2n + 1) = O(2n)

方法2:顺序法(从左到右依次求)

实现:

  1. #include <stdio.h>
  2. #include <iostream>
  3.  
  4. using namespace std;
  5. long long Fibonacci2(int n)
  6. {
  7. long long temp1 = 1;
  8. long long temp2 = 1;
  9. long long result = 0;
  10. if (n < 1)
  11. {
  12. return 0;
  13. }
  14. if (n == 1 || n == 2)
  15. {
  16. return 1;
  17. }
  18. for (int i = 3; i <= n; i++)
  19. {
  20. result = temp1 + temp2;
  21. temp1 = temp2;
  22. temp2 = result;
  23. }
  24. return result;
  25. }
  26. int main()
  27. {
  28. char cont = 'y';
  29. int n = 0;
  30. long long f = 0;
  31. cout << "Enter an integer:" << endl;
  32. while (cin >> n)
  33. {
  34. f = Fibonacci2(n);
  35. cout << "Fibonacci2(" << n << ") = " << f << endl;
  36. cout << "Continue(y or n)?" << endl;
  37. cin >> cont;
  38. if (cont == 'y' || cont == 'Y')
  39. {
  40. cout << "Enter an integer:" << endl;
  41. }
  42. else
  43. break;
  44. }
  45. return 0;
  46. }
View Code

时间复杂度:

   通过顺序计算求得第n项,时间复杂度T(n) = O(n)

方法3:矩阵乘法

实现:

  F(n) = F(n-1) + F(n-2)是一个二阶递推数列,可以用矩阵乘法的形式表示为:

  [F(n), F(n-1)] = [F(n-1), F(n-2)] * [a, b; c, d]

  带入n = 3, 4 可求出a = b = c = 1, d = 0。

  递推可进一步得到[F(n), F(n-1)] = [F(2), F(1)] * [a, b; c, d]n-2 = [1, 1] * [1, 1; 1, 0]n-2,F(n)便迎刃而解。

  简单介绍一下矩阵幂运算的实现过程。

   矩阵幂运算:

  以矩阵A的106次方为例,(106)10  = (1101010)2 = 21 + 23 + 25 + 26 = 2 + 8 + 32 + 64,所以,

      A106 = A2 * A8 * A32 * A64

  计算过程:

  1. result = E (单位矩阵),A平方得A2     0
  2. result *= A2,A2平方得A4               1
  3. A4平方得A8                                       0
  4. result *= A8,A8平方得A16             1
  5. A16平方得A32                                    0
  6. result *= A32,A32平方得A64          1
  7. result *= A64,A64平方得A128        1

  即106的二进制形式有多少位,就要调用平方运算几次,这个次数p其实满足:2p-1 ≤ n < 2p,即p ≈ log2(n),这样就将方法2中复杂度为O(n)的计算降到了O(log2(n))。

  1. #include <stdio.h>
  2. #include <iostream>
  3.  
  4. using namespace std;
  5. enum Status { kValid = 0, kInvalid };
  6. int g_nStatus = kValid;
  7. class Matrix
  8. {
  9. public:
  10. Matrix(int rows, int cols); // 构建一个全零矩阵
  11. int GetRows() const; // 返回矩阵行数
  12. int GetCols() const; // 返回矩阵列数
  13. long long **p; // 指针数组
  14. private:
  15. int rows_; // 矩阵行数
  16. int cols_; // 矩阵列数
  17. };
  18. Matrix::Matrix(int rows, int cols) : rows_(rows), cols_(cols)
  19. {
  20. if (rows < 0 || cols_ < 0)
  21. {
  22. cout << "Matrix error!\n";
  23. g_nStatus = kInvalid;
  24. return;
  25. }
  26. // 分配空间
  27. p = new long long *[rows_];
  28. for (int i = 0; i < rows_; i++)
  29. {
  30. p[i] = new long long[cols_];
  31. }
  32. // 初始化
  33. for (int i = 0; i < rows_; i++)
  34. {
  35. for (int j = 0; j < cols; j++)
  36. {
  37. p[i][j] = 0;
  38. }
  39. }
  40. }
  41. int Matrix::GetRows() const
  42. {
  43. return rows_;
  44. }
  45. int Matrix::GetCols() const
  46. {
  47. return cols_;
  48. }
  49. // 矩阵乘法运算
  50. Matrix MatrixMultiply(Matrix& m1, Matrix& m2)
  51. {
  52. Matrix result(m1.GetRows(), m2.GetCols());
  53. if (m1.GetCols() == m2.GetRows())
  54. {
  55. for (int i = 0; i < result.GetRows(); i++)
  56. {
  57. for (int j = 0; j < result.GetCols(); j++)
  58. {
  59. for (int k = 0; k < m1.GetCols(); k++)
  60. {
  61. result.p[i][j] += m1.p[i][k] * m2.p[k][j];
  62. }
  63. }
  64. }
  65. g_nStatus = kValid;
  66. }
  67. return result;
  68. }
  69. // 矩阵幂运算
  70. Matrix MatrixPowder(Matrix& m, int p)
  71. {
  72. g_nStatus = kInvalid;
  73. Matrix result(m.GetRows(), m.GetCols());
  74. if (m.GetRows() == m.GetCols())
  75. {
  76. for (int i = 0; i < result.GetRows(); i++)
  77. {
  78. result.p[i][i] = 1;
  79. }
  80. for (; p != 0; p >>= 1)
  81. {
  82. if ((p & 1) != 0)
  83. {
  84. result = MatrixMultiply(result, m);
  85. }
  86. m = MatrixMultiply(m, m);
  87. }
  88. }
  89. return result;
  90. }
  91. long long Fibonacci3(int n)
  92. {
  93. if (n < 1)
  94. {
  95. return 0;
  96. }
  97. if (n == 1 || n == 2)
  98. {
  99. return 1;
  100. }
  101. Matrix m1(2, 2);
  102. m1.p[0][0] = 1;
  103. m1.p[0][1] = 1;
  104. m1.p[1][0] = 1;
  105. m1.p[1][1] = 0;
  106. Matrix result = MatrixPowder(m1, n - 2);
  107. if (g_nStatus == kInvalid)
  108. {
  109. cout << "Matrix error!\n";
  110. return 0;
  111. }
  112. return (result.p[0][0] + result.p[1][0]);
  113. }
  114. int main()
  115. {
  116. char cont = 'y';
  117. int n = 0;
  118. long long f = 0;
  119. cout << "Enter an integer:" << endl;
  120. while (cin >> n)
  121. {
  122. f = Fibonacci3(n);
  123. cout << "Fibonacci(" << n << ") = " << f << endl;
  124. cout << "Continue(y or n)?" << endl;
  125. cin >> cont;
  126. if (cont == 'y' || cont == 'Y')
  127. {
  128. cout << "Enter an integer:" << endl;
  129. }
  130. else
  131. break;
  132. }
  133. return 0;
  134. }
View Code

时间复杂度:

   时间复杂度等于求矩阵n次方的复杂度,即O(log2n)

总结: 

三种方法的运行时间比较

可以明显感受到方法1的呈指数增长的时间复杂度。

方法1太耗时,下面再比较方法2和方法3:

方法3秒杀方法2!

感悟

  写完这篇随笔,深深体会到了算法的神奇。完成基本需求也许不难,又快又好的完成就需要有算法的功底啦。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号