题目:斐波那契数列,又称黄金分割数列(F(n+1)/F(n)的极限是1:1.618,即黄金分割率),指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……。在数学上,斐波纳契数列以如下被以递归的方法定义:

              F(0)=0F(1)=1F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)

  递归实现——自上而下

  

  在很多C语言教科书中讲到递归函数的时候,都会用Fibonacci作为例子。因此很多程序员对这道题的递归解法非常熟悉,看到题目就能写出如下递归求解的代码:

long Fibonacci(long n)                          //递归算法
{
if(n<=) return n; //终止递归的条件
else return Fibonacci(n-) + Fibonacci(n-);//递归步骤
}

  

  但是,教科书上反复用这个题目来讲解递归函数,并不能说明递归解法最适合这道题目。我们以求解F(10)作为例子来分析递归求解的过程。要求得F(10),需要求得F(9)和F(8)。同样,要求得F(9),要先求得F(8)和F(7)……我们用下面的树形结构来表示这种依赖关系

         F(10)
                         /             \
                    F(9)          F(8)
                   /     \           /    \
               F(8)    F(7)   F(7)   F(6)
              /    \      /    \ 
           F(7)  F(6) F(6)  F(5)

  我们不难发现在这棵树中有很多结点会重复的,而且重复的结点数会随着n的增大而急剧增加。这意味这计算量会随着n的增大而急剧增大。例如,在递归计算F(10)时,F(3)的值被计算了21次。而在递归计算F(30),这个调用的次数是骇人的317811次!这些个计算实际上只有一次是必要的,其余的纯属浪费!

  事实上这个递归算法的时间复杂度是指数级Ω(φn),φ=1.618(1:1.618=0.618称为黄金分割率)。

  迭代算法——自底向上

  下面的程序使用一个简单循环迭代来代替递归,这个非递归的形式不如上文给出的递归简单,也不太符合Fibonacci的递归定义,但是,它的运行速度提高了特别多!

  迭代算法的源码如下:

// 计算斐波那契数列的非递归算法(迭代)
long Fibonacci(long n)
{
if(n<=) return n; // Fib(0)或Fib(1)的情况
long FibCurrent, FibTwoBack = , FibOneBack = ; // 用数组保存程序更简洁,但不能明显的看出迭代的思想
for(int i= ; i<=n ; i++) // n≥2的情况
{
FibCurrent = FibOneBack + FibTwoBack; // 计算Fib(i)=Fib(i-1)+Fib(i-2)
/* 下面的保存顺序不能对调 */
FibTwoBack = FibOneBack; // 保存Fib(i-1)作为下趟的Fib(i-2)
FibOneBack = FibCurrent; // 保存Fib(i)作为下趟的Fib(i-1) }
return FibCurrent;
}

  显然,这个算法的时间复杂度为O(n),相比于前面指数级的递归算法,有了质的飞跃。

  事实上,这还不是最快的算法。还有一种时间复杂度是O(logn)的方法

  转化为特征矩阵乘方——分治策略 + 矩阵快速幂

  由数学归纳法易证:

          

  问题转化为求,继而就求出了F(n)。

  对于乘方问题我们利用分治策略优化有 

  运行时间递归式:T(n) = T(n/2) + θ(1) (同二分搜索一样)    用主方法接得T(n) = θ(logn)

  利用对特征矩阵乘方优化的方法可以得到最小的运行时间复杂度O(logn),代码如下:

class Matrix    // 自定义2×2矩阵类
{
public:
unsigned int a11, a12, a21, a22; // 矩阵元素
Matrix(int a, int b, int c, int d) :a11(a), a12(b), a21(c), a22(d) {}// 构造函数
Matrix operator*(const Matrix &other) // 重载矩阵的乘法
{
Matrix result(, , , );
result.a11 = a11*other.a11 + a12*other.a21;
result.a12 = a11*other.a12 + a12*other.a22;
result.a21 = a21*other.a11 + a22*other.a21;
result.a22 = a21*other.a12 + a22*other.a22;
return result;
}
}; Matrix MatrixPow(const Matrix &A, unsigned int n)// 计算矩阵A的n次方(分治策略,此处自底向上迭代)
{
Matrix result(, , , ); // 单位矩阵
Matrix tmp = A;
while (n)
{
if (n & ) // &为按位"与"运算,如果n为奇数
result = result * tmp;// 单乘一次矩阵
tmp = tmp * tmp;
n = n >> ; // n右移一位,相当于n/2(向下取整)
}
return result;
} unsigned int Fibonacci(int n)
{
if (n <= )
return n;
Matrix A(, , , ); // 特征矩阵
Matrix result = MatrixPow(A, n); // 计算矩阵A的n次方
return result.a12; // Fn即为结果矩阵中第一行第二例上的元素
}

    参考资料: 《MIT算法导论公开课》第三集——分治法

          《编程之美》P163

          《剑指offer》P73

斐波那契(Fibonacci)数列的几种计算机解法的更多相关文章

  1. 斐波那契(Fibonacci)数列的七种实现方法

    废话不多说,直接上代码 #include "stdio.h" #include "queue" #include "math.h" usin ...

  2. 如何用Python输出一个斐波那契Fibonacci数列

    a,b = 0, 1 while b<100: print (b), a, b = b, a+b

  3. 斐波那契 (Fibonacci)数列

    尾递归会将本次方法的结果计算出来,直接传递给下个方法.效率很快. 一般的递归,在本次方法结果还没出来的时候,就调用了下次的递归, 而程序就要将部分的结果保存在内存中,直到后面的方法结束,再返回来计算. ...

  4. 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)

    2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...

  5. ACM/ICPC 之 数论-斐波拉契●卢卡斯数列(HNNUOJ 11589)

    看到这个标题,貌似很高大上的样子= =,其实这个也是大家熟悉的东西,先给大家科普一下斐波拉契数列. 斐波拉契数列 又称黄金分割数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.34.… ...

  6. 递归函数练习:输出菲波拉契(Fibonacci)数列的前N项数据

    /*====================================================================== 著名的菲波拉契(Fibonacci)数列,其第一项为0 ...

  7. [洛谷P3938]:斐波那契(fibonacci)(数学)

    题目传送门 题目描述 小$C$养了一些很可爱的兔子.有一天,小$C$突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子 ...

  8. HZOJ 斐波那契(fibonacci)

    先说一个规律: 如图将每个月出生的兔子的编号写出来,可以发现一只兔子在哪一列他的父亲就是谁. 每列的首项可以通过菲波那契求得. 然后你就可以像我一样通过这个规律打表每个点的父亲,预处理出倍增数组,倍增 ...

  9. 【模拟8.03】斐波那契(fibonacci) (规律题)

    就是找规律,发现每个父亲和孩子的差值都是距儿子最大的fibonacc 也是可证的 f[i]表示当前月的兔子总数 f[i]=f[i-1]+f[i-2](f[i-2]是新生的,f[i-1]是旧有的) 然后 ...

随机推荐

  1. 图像和滚动 、 编程规范和Xcode(一)

    1 在界面上以各种模式显示图片 1.1 问题 在ios开发中经常需要展示图片以满足需求和美化界面,本案例将学习如何以代码的方式使用UIImageView视图控件来展示图片,如图-1所示: 图-1 1. ...

  2. spark streaming 实现接收网络传输数据进行WordCount功能

    package iie.udps.example.operator.spark; import scala.Tuple2; import org.apache.spark.SparkConf; imp ...

  3. 七 JavaBean

    一 什么是 JavaBean? JavaBean 是一个遵循特定写法的Java类,它是基于 Java 的组件模型,由属性.方法和事件3部分组成. 二 JavaBean 的特点: 1. 这个Java类必 ...

  4. 四 主要的几种 Web 服务器

    一 Microsoft IIS 1. 仅支持 Windows 操作系统,用于 .Net 平台网站的部署和运行. 2. IIS 是一种 Web 服务组件,包括括 Web 服务器.FTP 服务器.NNTP ...

  5. soapUI 在多个测试套件 testsuite 里,多个testcase里传值如何实现

        1.首先 要添加一个全局 自定义变量 Custom Properties   2.用transfer property 将取来的值 放入到变量 getToken 里   3.在另一个testc ...

  6. docker容器互联

    link方式 http://blog.csdn.net/halcyonbaby/article/details/42112325 通过link方式创建容器,然后我们可以使用被link容器的别名进行访问 ...

  7. 学霸网站之NABCD

    1.需求(Need) 随着社会的发展,互联网产业得到了飞速的发展,如今,互联网在各个领域都取得了至关重要的作用,随之而来的数量庞大的数据也让我们每个人在学习生活中倍感压力,如何才能在这些数据中找到自己 ...

  8. POJ 1195 Mobile phones(二维树状数组)

                                                                  Mobile phones Time Limit: 5000MS   Mem ...

  9. ed编辑器使用

    evilxr@IdeaPad:/tmp$ ed aa.c 0 a enter another words hello nice www.evilxr.com . w aa.c 46 q a 表示添加内 ...

  10. 布局转换:文档流->绝对定位

    布局转换:文档流->绝对定位(详见妙味JS高级教程,运动课程第6课20分钟起)比如一个DIV中有三张图片并排,个数不确定的布局.需要鼠标移上去图片从中心放大,只使用float:left布局在放大 ...