算法导论-求(Fibonacci)斐波那契数列算法对比
目录
1、斐波那契数列(Fibonacci)介绍
2、朴素递归算法(Naive recursive algorithm)
3、朴素递归平方算法(Naive recursive squaring)
4 、自底向上算法(Bottom-up)
5、 递归平方算法(Recursive squaring)
6、完整代码(c++)
7、参考资料
内容
1、斐波那契数列(Fibonacci)介绍
Fibonacci数列应该也算是耳熟能详,它的递归定义如上图所示。
下面2-6分别说明求取Fibonacci数列的4种方法
2、朴素递归算法(Naive recursive algorithm)
在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。因此很多程序员对这道题的递归解法非常熟悉,看到题目就能写出如下的递归求解的代码
unsigned long long Fibonacci_naive_reu(int n)//朴素递归算法
{
int result[] = {,};
if (n<) //退出条件
return result[n]; return (Fibonacci_naive_reu(n-)+ Fibonacci_naive_reu(n-));//递归调用
}
其实这种想法很好,但该算法运行时间令人无法忍受,当计算n=50时,用时1200多秒
分析:该算法的时间复杂度T(n)=Θ(∂n),其中,∂ = (1 + 5^½) / 2,即黄金分割比率。可知,∂>1,随着n增加,时间指数快速增加,所以,平时很少采用!
3、朴素递归平方算法(Naive recursive squaring)
这个算法主要根据斐波那契数列的一条数学性质而来。该性质表明,斐波那契数列F(n)即为φ^n / 5^½向下取整。这样,问题的求解于是变成了一个求x的n次方的问题,在之前的文章里有讲解,链接请进。 所以算法的效率为Θ(lgn)。但是这个方法是不太靠谱的,主要是当n比较大时,由于硬件的限制计算机中的浮点运算得到的结果与真实值就产生误差了。实用价值也不大,只能用做理论分析。
4 、自底向上算法(Bottom-up)
考虑到2中的简单递归算法,为了求解F(n),需要同时递归求解F(n - 1)和F(n - 2),显然这样就做了大量的重复工作。采用自底向上的算法即可避免这样的冗余。要计算F(n),则依次计算F0,F1,F2。。。Fn,这时计算Fn只需要利用前两个结果即可,这样算法效率提高到了Θ(n)。n=50时,小于1毫秒。实用价值高。
算法代码:
/************************************************************************/
/* 自底向上算法 */
// 时间复杂度T(n)=o(n)
/************************************************************************/
unsigned long long Fibonacci_bottom_up(int n)//自底向上算法
{
int result[] = {, };
if(n < )
return result[n]; unsigned long long fibNMinusN_1 = ;
unsigned long long fibNMinusN_2 = ;
unsigned long long fibN = ;
for(int i = ; i <= n; ++ i) //从底到上逐次计算出数列值
{
fibN = fibNMinusN_1 + fibNMinusN_2; fibNMinusN_2 = fibNMinusN_1;
fibNMinusN_1 = fibN;
} return fibN;//返回数组值
}
5、 递归平方算法(Recursive squaring)
上面的算法已经能达到Θ(n)线性效率,然而下面的这个算法能使效率达到T(n) = Θ(lgn)。该算法是基于一个定理,定理以及证明过程如下图所示。这样,问题的求解即成为了矩阵的乘方问题,算法效率于是提高到了Θ(lgn)。
矩阵求幂方法同常数求幂方法,采用分治思想,可参见常数求幂方法,下面是矩阵求幂实现代码:
/************************************************************************/
/* 下面矩阵的n次幂,结果保存在 Result[2][2] */
// 矩阵的n次幂,采用分治法,与x的n 次方算法一致,时间复杂度T(n)=o(logn)
// 1 1
// 1 0
/************************************************************************/
void Matrix_power(int n,unsigned long long Result[][])//求矩阵幂
{
unsigned long long AResult[][];
unsigned long long zResult1[][];
unsigned long long zResult2[][]; AResult[][]=;AResult[][]=;AResult[][]=;AResult[][]=;
if (==n)
{
Result[][]=;Result[][]=;Result[][]=;Result[][]=;
}
else if (n%==)
{
Matrix_power(n/,zResult1);
MUL(zResult1,zResult1,Result);
}
else if (n% == )
{
Matrix_power((n-)/,zResult1);
MUL(zResult1,zResult1,zResult2);
MUL(zResult2,AResult,Result);
}
}
其中,两个矩阵相乘代码实现如下:
/************************************************************************/
/* 两个 矩阵相乘 、结果矩阵保存在 MatrixResult[2][2]中 */
/************************************************************************/
void MUL( unsigned long long MatrixA[][],unsigned long long MatrixB[][], unsigned long long MatrixResult[][] )//矩阵相乘
{
for (int i=;i< ;i++)
{
for (int j=;j< ;j++)
{
MatrixResult[i][j]=;
for (int k=;k< ;k++)
{
MatrixResult[i][j]=MatrixResult[i][j]+MatrixA[i][k]*MatrixB[k][j];
}
}
}
}
6、完整代码(c++)
Fibonacci.h 其中递归平方法(矩阵求幂法)有两种实现,一种是我自己编码实现,另外是网上copy的代码。
#ifndef FIBONACCI_HH
#define FIBONACCI_HH
struct Matrix2By2
{
Matrix2By2
(
unsigned long long m00 = ,
unsigned long long m01 = ,
unsigned long long m10 = ,
unsigned long long m11 =
)
:m_00(m00), m_01(m01), m_10(m10), m_11(m11)
{
} unsigned long long m_00;
unsigned long long m_01;
unsigned long long m_10;
unsigned long long m_11;
};
class Fibonacci
{
public:
unsigned long long Fibonacci_naive_reu(int n);//朴素递归算法
unsigned long long Fibonacci_bottom_up(int n);//自底向上算法
unsigned long long Fibonacci_recur_squar(int n);//递归平方算法
void MUL( unsigned long long MatrixA[][],unsigned long long MatrixB[][], unsigned long long MatrixResult[][] );
void Matrix_power(int n,unsigned long long result[][]); Matrix2By2 MatrixMultiply(const Matrix2By2& matrix1, const Matrix2By2& matrix2);
Matrix2By2 MatrixPower(unsigned int n);
unsigned long long Fibonacci_Solution3(unsigned int n); }; unsigned long long Fibonacci::Fibonacci_naive_reu(int n)//朴素递归算法
{
int result[] = {,};
if (n<) //退出条件
return result[n]; return (Fibonacci_naive_reu(n-)+ Fibonacci_naive_reu(n-));//递归调用
}
/************************************************************************/
/* 自底向上算法 */
// 时间复杂度T(n)=o(n)
/************************************************************************/
unsigned long long Fibonacci::Fibonacci_bottom_up(int n)//自底向上算法
{
int result[] = {, };
if(n < )
return result[n]; unsigned long long fibNMinusN_1 = ;
unsigned long long fibNMinusN_2 = ;
unsigned long long fibN = ;
for(int i = ; i <= n; ++ i) //从底到上逐次计算出数列值
{
fibN = fibNMinusN_1 + fibNMinusN_2; fibNMinusN_2 = fibNMinusN_1;
fibNMinusN_1 = fibN;
} return fibN;//返回数组值
}
/************************************************************************/
/* 两个 矩阵相乘 、结果矩阵保存在 MatrixResult[2][2]中 */
/************************************************************************/
void Fibonacci::MUL( unsigned long long MatrixA[][],unsigned long long MatrixB[][], unsigned long long MatrixResult[][] )//矩阵相乘
{
for (int i=;i< ;i++)
{
for (int j=;j< ;j++)
{
MatrixResult[i][j]=;
for (int k=;k< ;k++)
{
MatrixResult[i][j]=MatrixResult[i][j]+MatrixA[i][k]*MatrixB[k][j];
}
}
}
}
/************************************************************************/
/* 下面矩阵的n次幂,结果保存在 Result[2][2] */
// 矩阵的n次幂,采用分治法,与x的n 次方算法一致,时间复杂度T(n)=o(logn)
// 1 1
// 1 0
/************************************************************************/
void Fibonacci::Matrix_power(int n,unsigned long long Result[][])//求矩阵幂
{
unsigned long long AResult[][];
unsigned long long zResult1[][];
unsigned long long zResult2[][]; AResult[][]=;AResult[][]=;AResult[][]=;AResult[][]=;
if (==n)
{
Result[][]=;Result[][]=;Result[][]=;Result[][]=;
}
else if (n%==)
{
Matrix_power(n/,zResult1);
MUL(zResult1,zResult1,Result);
}
else if (n% == )
{
Matrix_power((n-)/,zResult1);
MUL(zResult1,zResult1,zResult2);
MUL(zResult2,AResult,Result);
}
}
unsigned long long Fibonacci::Fibonacci_recur_squar(int n)//递归平方算法
{
unsigned long long fib_result[][];
int result[] = {, };
if(n < )
return result[n];
Matrix_power(n,fib_result);
return fib_result[][];
} /************************************************************************/
/* 下面是网上copy的递归平方算法的代码 */
/************************************************************************/
///////////////////////////////////////////////////////////////////////
// Multiply two matrices
// Input: matrix1 - the first matrix
// matrix2 - the second matrix
//Output: the production of two matrices
///////////////////////////////////////////////////////////////////////
Matrix2By2 Fibonacci::MatrixMultiply
(
const Matrix2By2& matrix1,
const Matrix2By2& matrix2
)
{
return Matrix2By2(
matrix1.m_00 * matrix2.m_00 + matrix1.m_01 * matrix2.m_10,
matrix1.m_00 * matrix2.m_01 + matrix1.m_01 * matrix2.m_11,
matrix1.m_10 * matrix2.m_00 + matrix1.m_11 * matrix2.m_10,
matrix1.m_10 * matrix2.m_01 + matrix1.m_11 * matrix2.m_11);
} ///////////////////////////////////////////////////////////////////////
// The nth power of matrix
// 1 1
// 1 0
///////////////////////////////////////////////////////////////////////
Matrix2By2 Fibonacci::MatrixPower(unsigned int n)
{
Matrix2By2 matrix;
if(n == )
{
matrix = Matrix2By2(, , , );
}
else if(n % == )
{
matrix = MatrixPower(n / );
matrix = MatrixMultiply(matrix, matrix);
}
else if(n % == )
{
matrix = MatrixPower((n - ) / );
matrix = MatrixMultiply(matrix, matrix);
matrix = MatrixMultiply(matrix, Matrix2By2(, , , ));
} return matrix;
}
///////////////////////////////////////////////////////////////////////
// Calculate the nth item of Fibonacci Series using devide and conquer
///////////////////////////////////////////////////////////////////////
unsigned long long Fibonacci::Fibonacci_Solution3(unsigned int n)
{
int result[] = {, };
if(n < )
return result[n]; Matrix2By2 PowerNMinus2 = MatrixPower(n - );
return PowerNMinus2.m_00;
}
#endif
Fibonacci.h
Fibonacci.cpp (主函数)
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
#include "Fibonacci.h" int main()
{
Fibonacci fib;
clock_t startTime_For_Fibonacci_naive_reu ;
clock_t endTime_For_Fibonacci_naive_reu ;
clock_t startTime_For_Fibonacci_bottom_up ;
clock_t endTime_For_Fibonacci_bottom_up ;
clock_t startTime_For_Fibonacci_recur_squar1 ;
clock_t endTime_For_Fibonacci_recur_squar1 ;
clock_t startTime_For_Fibonacci_recur_squar2 ;
clock_t endTime_For_Fibonacci_recur_squar2 ; startTime_For_Fibonacci_naive_reu=clock();
cout<<fib.Fibonacci_naive_reu()<<endl;//朴素递归算法
endTime_For_Fibonacci_naive_reu=clock(); startTime_For_Fibonacci_bottom_up=clock();
cout<<fib.Fibonacci_bottom_up()<<endl;//自底向上算法
endTime_For_Fibonacci_bottom_up=clock(); startTime_For_Fibonacci_recur_squar1=clock();
cout<<fib.Fibonacci_recur_squar()<<endl;//我自己编写的递归平方算法
endTime_For_Fibonacci_recur_squar1=clock(); startTime_For_Fibonacci_recur_squar2=clock();
cout<<fib.Fibonacci_Solution3()<<endl;//网上copy的递归平方算法
endTime_For_Fibonacci_recur_squar2=clock(); cout<<"朴素递归算法用时: "<<(endTime_For_Fibonacci_naive_reu-startTime_For_Fibonacci_naive_reu)/(1.0*CLOCKS_PER_SEC)<<"秒"<<endl;
cout<<"自底向上算法用时: "<<(endTime_For_Fibonacci_bottom_up-startTime_For_Fibonacci_bottom_up)/(1.0*CLOCKS_PER_SEC)<<"秒"<<endl;
cout<<"我自己编写的递归平方算法用时: "<<(endTime_For_Fibonacci_recur_squar1-startTime_For_Fibonacci_recur_squar1)/(1.0*CLOCKS_PER_SEC)<<"秒"<<endl;
cout<<"网上copy的递归平方算法用时: "<<(endTime_For_Fibonacci_recur_squar2-startTime_For_Fibonacci_recur_squar2)/(1.0*CLOCKS_PER_SEC)<<"秒"<<endl;
system("Pause");
return ;
}
输出:
结:朴素递归算法用时太多,实用价值不大,自底向上算法效率为线性,较高,平时用较多,递归平方算法效率为对数级,且编程可实现,实用价值很大。并且经过测试,当n值变很大后,递归平方算法效率明显高于自底向上算法效率。
7、参考资料
【1】http://zhedahht.blog.163.com/blog/static/25411174200722991933440/
【2】http://blog.csdn.net/xyd0512/article/details/8220506
算法导论-求(Fibonacci)斐波那契数列算法对比的更多相关文章
- Python算法_三种斐波那契数列算法
斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为"兔子数列&qu ...
- 算法 递归 迭代 动态规划 斐波那契数列 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 递归算法之Fibonacci 斐波那契数列第n个数的求解
Fibonacci 斐波那契数列第n个数的求解,也可以用递归和非递归的形式实现,具体如下,dart语言实现. int fibonacci(int n) { if (n <= 0) throw S ...
- 《BI那点儿事》Microsoft 时序算法——验证神奇的斐波那契数列
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10 ...
- lintcode:Fibonacci 斐波纳契数列
题目: 斐波纳契数列 查找斐波纳契数列中第 N 个数. 所谓的斐波纳契数列是指: 前2个数是 0 和 1 . 第 i 个数是第 i-1 个数和第i-2 个数的和. 斐波纳契数列的前10个数字是: 0, ...
- 【算法】php实现斐波那契数列
斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21.这个数列从第3项开始,每一项都等于前两项之和. 根据这个定义,斐波那契数列的递推公式是:f(n)=f(n-1)+f(n ...
- 基于visual Studio2013解决算法导论之045斐波那契堆
题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...
- Fibonacci(斐波那契数列)的最佳实践方式(JavaScript)
1)低级版本 var fibonacci = function(n) { if (n == 0 || n == 1) { return n; } else { return fibonacci(n - ...
- fibonacci 斐波那契数列
1.小兔子繁殖问题 (有该问题的详细来由介绍) 2.台阶问题 题目:一个人上台阶可以一次上一个或者两个,问这个人上n层的台阶,一共有多少种走法. 递归的思路设计模型: i(台阶阶数) ...
随机推荐
- python3,判断,循环练习1
1.使用while循环输出1 2 3 4 5 6 8 9 10 i = 1 while i <= 10: if i == 7: i += 1 print(end=' ') continue pr ...
- JavaScript获取和操作html的元素
#转载请留言联系 1.获取元素 JavaScript的用途就是为了实现用户交互和网页的大部分动画.所以JavaScript常常需要操作html中的元素.要先操作就要先获取过来.JS有几种途径获取元素, ...
- 【C++】默认构造函数
参考文献: 1.黄邦勇帅 2.http://www.cnblogs.com/graphics/archive/2012/10/02/2710340.html 3.http://blog.csdn.ne ...
- PHP文件环境搭建—EcShop环境搭建
1 rpm -qa|grep yum|xargs rpm -e --nodeps 2 ls 3 rpm -ivh python-iniparse-0.3.1-2.1.el6.noar ...
- 怎么将string list 转成有特殊字符分开字符串
https://stackoverflow.com/questions/4021851/join-string-list-elements-with-a-delimiter-in-one-step Y ...
- docker 安装持久化mysql
https://hub.docker.com 拉取mysql镜像docker pull mysql:5.5运行mysql镜像并持久化到本地docker run -v /var/own/mysqldat ...
- DockerFile的编写和注意的一些知识点
CMD,RUN,ENTRYPOINT之类的差别. VOLUMN和-V之间的差别. EXPOSE和-P的对应等. 今天上午写了一个脚本,可以传参数进IMAGE,让启动的CONTAINER具有不同的行为. ...
- 计蒜客 30999.Sum-筛无平方因数的数 (ACM-ICPC 2018 南京赛区网络预赛 J)
J. Sum 26.87% 1000ms 512000K A square-free integer is an integer which is indivisible by any squar ...
- 前端学习blog
有一些js写的小游戏,很有趣 http://oldj.net
- ZCMU Problem G: 素数对(数论,素数筛法)
#include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #inc ...