算法导论-求(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(台阶阶数) ...
随机推荐
- MySQL 进阶(待发布)
视图 存储过程 触发器 基本函数
- BZOJ 4522: [Cqoi2016]密钥破解
http://www.lydsy.com/JudgeOnline/problem.php?id=4522 题目:给你RSA密钥的公钥和密文,求私钥和原文,其中\(N=pq\le 2^{62}\),p和 ...
- 设计模式之笔记--简单工厂模式(Simple Factory)
简单工厂模式(Simple Factory) 类图 描述 简单工厂: 一个抽象产品类,可以派生多个具体产品类: 一个具体工厂类: 工厂只能创建一个具体产品. 应用场景 汽车接口 public inte ...
- linux环境下的GUN make学习笔记(一)
第一章:概述 1.1:make概述 在linux环境下使用make工具能够比较容易的构建一个属于自己的工程,整个工程的编译只需要一个命令就可以完成编译.连接以至于最后的执行.不过我们需要投入一些时间去 ...
- sql ---- count 误区
select count(字段名) from table group by 字段; 分组后的内容统计. count统计的字段是 不包括空的数据.
- mysql绿色版配置
MySQL 绿色版安装配置教程 一.下载,这里使用绿色免安装版 1.网上搜索mysql绿色免安装版下载即可.提供官网地址 http://dev.mysql.com/downloads/mysql--- ...
- 任务侦听器(Task listener)
任务侦听器:用于在任务相关的事件发生时执行一段java逻辑或者是表达式 <userTask id="myTask" name="My Task" > ...
- Redis数据类型的常用API以及使用场景
一.通用命令 1.keys 遍历出所有的key 一般不在生产环境使用 2.dbsize key的总数 3.exists key 4.del key 删除指定key-value 5.expire k ...
- hdu5737
首先思考一个朴素的做法 将b[]维护成一个线段树套有序表,每次修改a[]用线段树+lazy tag 并在线段树的子区间上在有序表中二分更新这段区间中a[i]>=b[i]的值,复杂度O(nlog^ ...
- 训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)
layout: post title: 训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分) author: "luowentaoaa" catalog: tr ...