斐波那契数列的三种C++实现及时间复杂度分析
本文介绍了斐波那契数列的三种C++实现并详细地分析了时间复杂度。
斐波那契数列定义:F(1)=1, F(2)=1, F(n)=F(n-1) + F(n-2) (n>2)
如何计算斐波那契数 F(n) 及时间复杂度 T(n) 呢?
我参考了一些资料总结了以下3种方法:递归法、顺序法和矩阵乘法,并给出了基于C++的简单代码实现和时间复杂度分析。
如有不当,欢迎指正。
方法1:递归法
实现:
#include <stdio.h>
#include <iostream> using namespace std; long long Fibonacci1(int n)
{
if (n < )
{
return ;
}
if (n == || n == )
{
return ;
}
return Fibonacci1(n - ) + Fibonacci1(n - );
}
int main()
{
char cont = 'y';
int n = ;
long long f = ;
cout << "Enter an integer:" << endl;
while (cin >> n)
{
f = Fibonacci1(n);
cout << "Fibonacci1(" << n << ") = " << f << endl;
cout << "Continue(y or n)?" << endl;
cin >> cont;
if (cont == 'y' || cont == 'Y')
{
cout << "Enter an integer:" << endl;
}
else
break;
}
return ;
}
时间复杂度:
设计算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:顺序法(从左到右依次求)
实现:
#include <stdio.h>
#include <iostream> using namespace std; long long Fibonacci2(int n)
{
long long temp1 = ;
long long temp2 = ;
long long result = ; if (n < )
{
return ;
}
if (n == || n == )
{
return ;
}
for (int i = ; i <= n; i++)
{
result = temp1 + temp2;
temp1 = temp2;
temp2 = result;
}
return result;
} int main()
{
char cont = 'y';
int n = ;
long long f = ;
cout << "Enter an integer:" << endl;
while (cin >> n)
{
f = Fibonacci2(n);
cout << "Fibonacci2(" << n << ") = " << f << endl;
cout << "Continue(y or n)?" << endl;
cin >> cont;
if (cont == 'y' || cont == 'Y')
{
cout << "Enter an integer:" << endl;
}
else
break;
}
return ;
}
时间复杂度:
通过顺序计算求得第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,
计算过程:
- result = E (单位矩阵),A平方得A2 0
 - result *= A2,A2平方得A4
 - A4平方得A8 0
 - result *= A8,A8平方得A16
 - A16平方得A32 0
 - result *= A32,A32平方得A64
 - result *= A64,A64平方得A128
 
即106的二进制形式有多少位,就要调用平方运算几次,这个次数p其实满足:2p-1 ≤ n < 2p,即p ≈ log2(n),这样就将方法2中复杂度为O(n)的计算降到了O(log2(n))。
#include <stdio.h>
#include <iostream> using namespace std; enum Status { kValid = , kInvalid };
int g_nStatus = kValid; class Matrix
{
public:
Matrix(int rows, int cols); // 构建一个全零矩阵
int GetRows() const; // 返回矩阵行数
int GetCols() const; // 返回矩阵列数
long long **p; // 指针数组
private:
int rows_; // 矩阵行数
int cols_; // 矩阵列数
}; Matrix::Matrix(int rows, int cols) : rows_(rows), cols_(cols)
{
if (rows < || cols_ < )
{
cout << "Matrix error!\n";
g_nStatus = kInvalid;
return;
}
// 分配空间
p = new long long *[rows_];
for (int i = ; i < rows_; i++)
{
p[i] = new long long[cols_];
}
// 初始化
for (int i = ; i < rows_; i++)
{
for (int j = ; j < cols; j++)
{
p[i][j] = ;
}
}
}
int Matrix::GetRows() const
{
return rows_;
}
int Matrix::GetCols() const
{
return cols_;
} // 矩阵乘法运算
Matrix MatrixMultiply(Matrix& m1, Matrix& m2)
{
Matrix result(m1.GetRows(), m2.GetCols());
if (m1.GetCols() == m2.GetRows())
{
for (int i = ; i < result.GetRows(); i++)
{
for (int j = ; j < result.GetCols(); j++)
{
for (int k = ; k < m1.GetCols(); k++)
{
result.p[i][j] += m1.p[i][k] * m2.p[k][j];
}
}
}
g_nStatus = kValid;
}
return result;
} // 矩阵幂运算
Matrix MatrixPowder(Matrix& m, int p)
{
g_nStatus = kInvalid;
Matrix result(m.GetRows(), m.GetCols());
if (m.GetRows() == m.GetCols())
{
for (int i = ; i < result.GetRows(); i++)
{
result.p[i][i] = ;
}
for (; p != ; p >>= )
{
if ((p & ) != )
{
result = MatrixMultiply(result, m);
}
m = MatrixMultiply(m, m);
}
}
return result;
} long long Fibonacci3(int n)
{
if (n < )
{
return ;
}
if (n == || n == )
{
return ;
}
Matrix m1(, );
m1.p[][] = ;
m1.p[][] = ;
m1.p[][] = ;
m1.p[][] = ; Matrix result = MatrixPowder(m1, n - );
if (g_nStatus == kInvalid)
{
cout << "Matrix error!\n";
return ;
}
return (result.p[][] + result.p[][]);
} int main()
{
char cont = 'y';
int n = ;
long long f = ;
cout << "Enter an integer:" << endl;
while (cin >> n)
{
f = Fibonacci3(n);
cout << "Fibonacci(" << n << ") = " << f << endl;
cout << "Continue(y or n)?" << endl;
cin >> cont;
if (cont == 'y' || cont == 'Y')
{
cout << "Enter an integer:" << endl;
}
else
break;
}
return ;
}
时间复杂度:
时间复杂度等于求矩阵n次方的复杂度,即O(log2n)。
总结:
三种方法的运行时间比较

可以明显感受到方法1的呈指数增长的时间复杂度。
方法1太耗时,下面再比较方法2和方法3:

方法3秒杀方法2!
感悟
写完这篇随笔,深深体会到了算法的神奇。完成基本需求也许不难,又快又好的完成就需要有算法的功底啦。
斐波那契数列的三种C++实现及时间复杂度分析的更多相关文章
- 斐波那契数列的5种python实现写法
		
斐波那契数列的5种python写法 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖 ...
 - Python中斐波那契数列的四种写法
		
在这些时候,我可以附和着笑,项目经理是决不责备的.而且项目经理见了孔乙己,也每每这样问他,引人发笑.孔乙己自己知道不能和他们谈天,便只好向新人说话.有一回对我说道,“你学过数据结构吗?”我略略点一点头 ...
 - 实现斐波拉契数列的四种方式python代码
		
斐波那契数列 1. 斐波拉契数列简介 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引 ...
 - 斐波那契数列 的两种实现方式(Java)
		
import java.util.Scanner; /* 斐波那契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 如果设F(n)为该数列的第n ...
 - 方法输出C++输出斐波那契数列的几种方法
		
PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘.目前又不当COO,还是得用心记代码哦! 定义: 斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, ...
 - Fibonacci series(斐波纳契数列)的几种常见实现方式
		
费波那契数列的定义: 费波那契数列(意大利语:Successione di Fibonacci),又译费波拿契数.斐波那契数列.斐波那契数列.黄金切割数列. 在数学上,费波那契数列是以递归的方法来定义 ...
 - C++输出斐波那契数列的几种方法
		
定义: 斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... 这个数列从第三项开始,每一项都等于前两项之和. 以输出斐波那 ...
 - JS写斐波那契数列的几种方法
		
斐波那契数,指的是这样一个数列:1.1.2.3.5.8.13.21.……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字 ...
 - 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& ...
 
随机推荐
- ssd论文解读
			
https://www.sohu.com/a/168738025_717210 https://www.cnblogs.com/lillylin/p/6207292.html https://blog ...
 - CUDA内存拷贝
			
原文链接1.cudaMemcpy()<--> cudaMalloc() //线性内存拷贝 1 //线性内存拷贝 2 cudaMalloc((void**)&dev_A, data ...
 - Win10预览版激活信息
			
微软在10月2日零点正式公开了Win10预览版的下载地址,这个时间大家应该逐步开始安装工作了,因此提出下面两个问题的用户特别多,IT之家再稍作告知一下.1.Win10预览版安装密钥是什么?答:NKJF ...
 - pycharm中常用设置
			
当安装时检查版本过低 首先 pip --help 进入帮助,找到 复制,然后 pip install --disable-pip-version-check 要安装的包 这样就会跳过版本检测. 在py ...
 - django中的auth模块以及分页器
			
1.auth模块 auth模块是Django提供的标准权限管理系统,可以提供用户身份认证,和权限管理 auth可以和admin模块配合使用, 快速建立网站的管理系统 在INSTALLED_APPS中添 ...
 - Linux自带mariadb卸载
			
MySQL安装过程中报错: dpkg: regarding mysql-community-server_5.6.39-1debian9_i386.deb containing mysql-commu ...
 - 浅谈PHP中的数组和JS中的数组
			
最近在做前后端对接的时候,遇到一个问题,前端要求返回的数据格式是左边的,但是我通过json_encode返回到的数据格式是右边的 注意:数据格式从"[]"(数组)变成了&quo ...
 - JAVA / MySql 编程—— 第一章 数据库的设计
			
1. 数据库设计:将数据库中的数据实体及这些数据实体之间的关系进行规划和结构化的过程: 良好的数据库设计: 节省数据的存储空间 能够保证数据的完整性 方便进行数据库应用系统的开发 糟糕 ...
 - 一个简单的WPF MVVM实例【转载】
			
引用地址:http://blog.csdn.net/yl2isoft/article/details/20838149 1 新建WPF 应用程序WPFMVVMExample 程序结构如下图所示. 2 ...
 - 为 vsftpd 启动 vsftpd:500 OOPS: SSL: cannot load RSA&nb
			
博主在配置ftp服务器的时候 自己生成的CA,然后认证自己的私钥文件 一切配置都是没有问题的,然后重新启动服务器 service vsftpd restart 出现以下错误 为 vsftpd 启 ...