本文介绍了斐波那契数列的三种C++实现并详细地分析了时间复杂度。

斐波那契数列定义: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.  
  6. long long Fibonacci1(int n)
  7. {
  8. if (n < )
  9. {
  10. return ;
  11. }
  12. if (n == || n == )
  13. {
  14. return ;
  15. }
  16. return Fibonacci1(n - ) + Fibonacci1(n - );
  17. }
  18. int main()
  19. {
  20. char cont = 'y';
  21. int n = ;
  22. long long f = ;
  23. cout << "Enter an integer:" << endl;
  24. while (cin >> n)
  25. {
  26. f = Fibonacci1(n);
  27. cout << "Fibonacci1(" << n << ") = " << f << endl;
  28. cout << "Continue(y or n)?" << endl;
  29. cin >> cont;
  30. if (cont == 'y' || cont == 'Y')
  31. {
  32. cout << "Enter an integer:" << endl;
  33. }
  34. else
  35. break;
  36. }
  37. return ;
  38. }

时间复杂度:

  设计算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.  
  6. long long Fibonacci2(int n)
  7. {
  8. long long temp1 = ;
  9. long long temp2 = ;
  10. long long result = ;
  11.  
  12. if (n < )
  13. {
  14. return ;
  15. }
  16. if (n == || n == )
  17. {
  18. return ;
  19. }
  20. for (int i = ; i <= n; i++)
  21. {
  22. result = temp1 + temp2;
  23. temp1 = temp2;
  24. temp2 = result;
  25. }
  26. return result;
  27. }
  28.  
  29. int main()
  30. {
  31. char cont = 'y';
  32. int n = ;
  33. long long f = ;
  34. cout << "Enter an integer:" << endl;
  35. while (cin >> n)
  36. {
  37. f = Fibonacci2(n);
  38. cout << "Fibonacci2(" << n << ") = " << f << endl;
  39. cout << "Continue(y or n)?" << endl;
  40. cin >> cont;
  41. if (cont == 'y' || cont == 'Y')
  42. {
  43. cout << "Enter an integer:" << endl;
  44. }
  45. else
  46. break;
  47. }
  48. return ;
  49. }

时间复杂度:

  通过顺序计算求得第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  
  3. A4平方得A8                                       0
  4. result *= A8,A8平方得A16  
  5. A16平方得A32                                    0
  6. result *= A32,A32平方得A64  
  7. result *= A64,A64平方得A128  

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

时间复杂度:

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

总结:

三种方法的运行时间比较

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

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

方法3秒杀方法2!

感悟

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

斐波那契数列的三种C++实现及时间复杂度分析的更多相关文章

  1. 斐波那契数列的5种python实现写法

    斐波那契数列的5种python写法       斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖 ...

  2. Python中斐波那契数列的四种写法

    在这些时候,我可以附和着笑,项目经理是决不责备的.而且项目经理见了孔乙己,也每每这样问他,引人发笑.孔乙己自己知道不能和他们谈天,便只好向新人说话.有一回对我说道,“你学过数据结构吗?”我略略点一点头 ...

  3. 实现斐波拉契数列的四种方式python代码

    斐波那契数列 1. 斐波拉契数列简介 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引 ...

  4. 斐波那契数列 的两种实现方式(Java)

    import java.util.Scanner; /* 斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 如果设F(n)为该数列的第n ...

  5. 方法输出C++输出斐波那契数列的几种方法

    PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘.目前又不当COO,还是得用心记代码哦! 定义: 斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, ...

  6. Fibonacci series(斐波纳契数列)的几种常见实现方式

    费波那契数列的定义: 费波那契数列(意大利语:Successione di Fibonacci),又译费波拿契数.斐波那契数列.斐波那契数列.黄金切割数列. 在数学上,费波那契数列是以递归的方法来定义 ...

  7. C++输出斐波那契数列的几种方法

    定义: 斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 这个数列从第三项开始,每一项都等于前两项之和. 以输出斐波那 ...

  8. JS写斐波那契数列的几种方法

    斐波那契数,指的是这样一个数列:1.1.2.3.5.8.13.21.……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字 ...

  9. JS实现斐波那契数列的几种方法

    斐波那契数列指的是这样一个数列:1.1.2.3.5.8.13.21.34.…… 前两项为1,从第三项起,每一项等于前两项的和,即F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n& ...

随机推荐

  1. Veritas NetBackup™ 状态码"十大"常见报错状态码

    我在刚开始学习Netbackup的时候,没少走弯路.经常会遇到各种稀奇古怪的 error 信息,遇到报错会很慌张,急需一个解决问题的办法.跟无头苍蝇一样,会不加思索地把错误粘到百度上,希望赶紧查找一下 ...

  2. php图像处理插件imagick安装(仅适用于86位,php5.4非安全环境-16px)

    phpImageMagick-6.7.7-5-Q16-windows-dll(加测试代码,经测试,仅适用于86位,php5.4安全环境-16px) 下载地址:http://pan.baidu.com/ ...

  3. python 删除空白

    Python能够找出字符串开头和末尾多余的空白.要确保字符串末尾没有空白,可使用方法rstrip() . >>> favorite_language = 'python ' > ...

  4. keyframes 放大缩小动画

    本次项目中动画放大缩小代码小结 .fix .phone{ -moz-animation: myfirst 1s infinite; -webkit-animation: myfirst 1s infi ...

  5. this指向问题(1)

    在JS中,this一般有四种绑定的方式,但是在确定到底是哪种绑定之前必须先找到函数的调用位置.接下来先介绍其中的三种: 1.默认绑定 其实所谓的默认绑定就是函数直接调用(前面没有什么东西来点它),在默 ...

  6. hdu_2837_Calculation(欧拉函数,快速幂求指数循环节)

    Assume that f(0) = 1 and 0^0=1. f(n) = (n%10)^f(n/10) for all n bigger than zero. Please calculate f ...

  7. matlab2018a安装后帮助文档打不开解决方法

    安装matlab2018a破解版后,帮助文档提示需要许可证问题(破解版没有可用许可证): 解决方法是把文档设置为离线即可(预设---->帮助---->安装在本地---->小窗口)

  8. pycharm界面美化,个人喜欢

    进入file-setting选项 界面设置主要是在appearance和editor里面.appearance主要是整个pycharm的主题设置,比如文件管理窗口的颜色,其实就是软件本身的主题设置.我 ...

  9. mybatis的坑——不报错,就是不能committing,数据存不进数据库

    测试的时候会报空指针异常,在项目跑的时候会停止执行程序,不会出现异常. 经过一星期的排查与测试,最终找到错误,把mapper文件的映射属性名写错了. property属性名要与接收类的属性名保持一致. ...

  10. 手机丢了怎么办?MZ给你来支招

    1致电运营商挂失手机 2致电银行冻结手机网银 3手机绑定支付宝的拨95188挂失 4微信用户登录110.qq.com冻结账号 5修改微博.微信.QQ等密码 6到手机运营商处补手机卡. 一定要记住啊!手 ...