算法导论-求(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(台阶阶数) ...
随机推荐
- linux下检测可用串口并使用minicom打开(改进版)
之前使用的方式是这样的 http://www.cnblogs.com/zqb-all/p/7073612.html 这两天看到minicom的参数,可以直接使用 -D 指定设备,于是修改成了这样 建立 ...
- golang写一个简单的爬虫
package main import( "fmt" "io/ioutil" "net/http" ) func gethtml(url s ...
- 算法题之Leetcode分糖果
题目: There are N children standing in a line. Each child is assigned a rating value. You are giving c ...
- H5智能表单
表单 keygen元素的作用是提供一种验证用户的可靠方法 datalist 元素规定输入域的选项列表 output元素用于不同类型的输出,比如计算或脚本输出 智能表单 input 新增type值 em ...
- 时间戳 JS PHP MYSQL
Unix 时间戳 Unix timestamp ('1970-01-01 00:00:00' GMT 之后的秒数) JS var strtime = '2014-04-23 18:55:49:123 ...
- 【SQL】全关系操作
1.消除重复 - DISTINCT SQL语句中默认的是,重复的元祖可以多次的显示.如果希望消除重复,需要DISTINCT关键字. 注:消除重复需要排序,所以代价高.在需要高效率时要谨慎. SELEC ...
- UVALive - 5798
Jupiter Atacks! /** 题意:B,P,L,N,分别表示进制,mod,数组的个数,操作数 做法:树状数组 欧几里得 每个数加入到数组Tree的数是 B^(L-i) 用树状数组进行维护前缀 ...
- Selenium2+python自动化31-生成测试报告【转载】
前言 最近小伙伴们总有一些测试报告的问题,网上的一些资料生成报告的方法,我试了都不行,完全生成不了,不知道他们是怎么生成的,同样的代码,有待研究. 今天小编写一下可以生成测试报告的方法.个人觉得也是最 ...
- Chrome崩溃卡死
Chrome崩溃卡死主要是Flash崩溃导致,解决办法:1,chrome://plugins,停用Chrome自带的Flash插件:2,停用GPU加速.chrome://flags,停用对所有网页执行 ...
- 使用 gulp 压缩图片
请务必理解如下章节后阅读此章节: 安装 Node 和 gulp 使用 gulp 压缩 JS 压缩 图片文件可降低文件大小,提高图片加载速度. 找到规律转换为 gulp 代码 规律 找到 images/ ...