Fibonacci数列几种不同的编程实现方法
2015-09-14 15:30
337 查看
Fibonacci数列:http://baike.baidu.com/link?url=MSUG79bXOIuyMT8SkGsXdh3mGxYhlun_srlBZVFwcgrNPcL8VulHhdDOClNwJzNJ0W4rlpdyxeiOi9kTBoORt_
描写了动物滋生数量、植物花序改变等大措施则。作为一个经典的数学识题,Fibonacci数列常作为例子展目前途序设计、数据构造与算法等多个相干学科中。
下面容易地分析一下常见的Fibonacci数列求解算法。
1、递归法。大多数教材在解说递归算法时总迷恋以Fibonacci数列为例,这是因为我们能够直观地从定义公式的第三行看出Fibonacci数列的递归性。其C++告终如下:
递归算法与定义公式极其合乎,草带会意,但计算过程存在许多重复的计算,工夫混杂度到达了O(2^n),利用的内存空间也随着函数调用栈的增长而增长。这显明难受于实用的过程。
2、表驱动的递归法。这里不提单纯的表驱动法,因为对于项数未知的Fibonacci数列开启***的空间来换取工夫未免不划算且不负责。我们只是为了肃清递归法中许多重复的计算,能够将曾经计算过的其中值存入一个表,已备后续利用:
当n小于保留的表长时,由于每个其中值只计算顺次,工夫混杂度降为O(n)。但随着n的增大,要想坚持O(n)的工夫混杂度,就定然放大保留的表长,这就构成了存储空间的浪费。
3、迭代法。求Fibonacci数列第n项时固然要用到前面两项的值,但它们仅作为临时计算的其中值,不作为收获输出,因而无保留的必需,全面能够改换成迭代法求解:
迭代法的工夫混杂度为O(n),利用的内存空间也不会动态递升。个人感受Fibonacci数列更轻便作为迭代法而非递归法的典例展目前教材上。
下面给出两种数学性较强的算法。琢磨到表白的简明性,用Matlab告终:
4、迁移矩阵法。此措施等闲见于线性代数中的Markov过程示例。Fibonacci数列第n项与第n-1项能够穿越迁移矩阵的n-1次迭代求出:
代码如下:
此算法的工夫混杂度相当于计算矩阵乘方的工夫混杂度。在计算2阶矩阵n次方时,万一直接按矩阵乘法定义式展开,不加尤其优化,那工夫混杂度为O(n)。
5、通项公式法。Fibonacci数列的通项公式如下(验证略):
利用通项公式能够获得Fibonacci数列的任何项:
该措施的工夫混杂度传神为O(1),但我们还该当琢磨乘方计算的工夫花费。不加尤其优化时,用乘法告终n次乘方的工夫混杂度为O(n)。琢磨到浮点数计算效率和精度问题,此措施在计算机告终时不及迁移矩阵法。
下面再给出两种对计算机告终有尤其含义,但同时有定然局限性的告终措施(只是告终措施,不能称为新的算法)。其中利用了一些C++编程技巧,对利用其它语言告终也有定然的参看价值:
6、模板元编程法。等闲我们在C++中利用模板,仅限于类模板与函数模板。事实上C++扶持模板元编程,理论上能够在编译时厉行任何计算(甚至包括抉择、循环、递归等构造)。代码如下:
我们用一个构造体作为模板的载体,用一个枚举值保留计算收获。其中第一个模板为大约递归过程(利用递归算法是为打听释的轻便,全面能够用其它算***换,以加速编译过程),后两个模板为n=0、1时的模板特化。穿越#define语句将模板调用简写成相仿函数调用的措施。过程在编译时计算所需的Fibonacci数列项,将收获作为常量嵌入编译好的过程。运行时直接利用收获,工夫混杂度恳挚地变成了O(1)。但这一措施最大的局限即便只能对常量嵌入,过程中揭示诸如计算Fib(i++)的情形则无能为力。尽管如此,这比在代码中手工计算并写入所需的值要直观准确,比穿越单纯的表驱动法“空间换时间”要得体迅捷。
7、函数对象法。此措施重要用于C++ STL编程的通用算法方面,其厉行行动也有别于以上其它措施:
这个函数类对象的行动能够会意为一个“Fibonacci数列发生器”,其测验性调用如下,过程将顺次打印
函数对象具有与函数指针相仿的行动,同时又能保留切身的一些属性,因而常用于STL通用算法编程。但针对个体的Fibonacci数列项求值,灵便性不及等闲的措施。
描写了动物滋生数量、植物花序改变等大措施则。作为一个经典的数学识题,Fibonacci数列常作为例子展目前途序设计、数据构造与算法等多个相干学科中。
下面容易地分析一下常见的Fibonacci数列求解算法。
1、递归法。大多数教材在解说递归算法时总迷恋以Fibonacci数列为例,这是因为我们能够直观地从定义公式的第三行看出Fibonacci数列的递归性。其C++告终如下:
unsigned long Fib(int n) { if (n <= 1) { return n; } else { return Fib(n - 1) + Fib(n - 2); } }
递归算法与定义公式极其合乎,草带会意,但计算过程存在许多重复的计算,工夫混杂度到达了O(2^n),利用的内存空间也随着函数调用栈的增长而增长。这显明难受于实用的过程。
2、表驱动的递归法。这里不提单纯的表驱动法,因为对于项数未知的Fibonacci数列开启***的空间来换取工夫未免不划算且不负责。我们只是为了肃清递归法中许多重复的计算,能够将曾经计算过的其中值存入一个表,已备后续利用:
#define MAX_LOG 20 static unsigned long Logs[MAX_LOG] = {0}; unsigned long Fib(int n) { if (n <= 1) { return n; }
else if (n < MAX_LOG && Logs != 0) { return Logs ; }
else { Logs = Fib(n - 1) + Fib(n - 2); return Logs ; } }
当n小于保留的表长时,由于每个其中值只计算顺次,工夫混杂度降为O(n)。但随着n的增大,要想坚持O(n)的工夫混杂度,就定然放大保留的表长,这就构成了存储空间的浪费。
3、迭代法。求Fibonacci数列第n项时固然要用到前面两项的值,但它们仅作为临时计算的其中值,不作为收获输出,因而无保留的必需,全面能够改换成迭代法求解:
unsigned long Fib(int n) { int i; unsigned long a = 0,tiffany b = 1, c; if (n <= 1) { return n; }
else { for (i = 2; i <= n; i++) { c = a + b; a = b; b = c; } return c; } }
迭代法的工夫混杂度为O(n),利用的内存空间也不会动态递升。个人感受Fibonacci数列更轻便作为迭代法而非递归法的典例展目前教材上。
下面给出两种数学性较强的算法。琢磨到表白的简明性,用Matlab告终:
4、迁移矩阵法。此措施等闲见于线性代数中的Markov过程示例。Fibonacci数列第n项与第n-1项能够穿越迁移矩阵的n-1次迭代求出:
代码如下:
function y = Fib(n) first = [1; 0]; trans = [1 1; 1 0]; last = trans ^ (n - 1) * first; y = last(1, 1); end
此算法的工夫混杂度相当于计算矩阵乘方的工夫混杂度。在计算2阶矩阵n次方时,万一直接按矩阵乘法定义式展开,不加尤其优化,那工夫混杂度为O(n)。
5、通项公式法。Fibonacci数列的通项公式如下(验证略):
利用通项公式能够获得Fibonacci数列的任何项:
function y = Fib(n) sr5 = sqrt(5); y = uint32((((1 + sr5) / 2) ^ n - ((1 - sr5) / 2) ^ n) / sr5); end
该措施的工夫混杂度传神为O(1),但我们还该当琢磨乘方计算的工夫花费。不加尤其优化时,用乘法告终n次乘方的工夫混杂度为O(n)。琢磨到浮点数计算效率和精度问题,此措施在计算机告终时不及迁移矩阵法。
下面再给出两种对计算机告终有尤其含义,但同时有定然局限性的告终措施(只是告终措施,不能称为新的算法)。其中利用了一些C++编程技巧,对利用其它语言告终也有定然的参看价值:
6、模板元编程法。等闲我们在C++中利用模板,仅限于类模板与函数模板。事实上C++扶持模板元编程,理论上能够在编译时厉行任何计算(甚至包括抉择、循环、递归等构造)。代码如下:
#define Fib(N) FibT<N>::Val template<int n> struct FibT { enum { Val = FibT<n - 1>::Val + FibT<n - 2>::Val }; }; template<> struct FibT<0> { enum { Val = 0 }; }; template<> struct FibT<1> { enum { Val = 1 }; };
我们用一个构造体作为模板的载体,用一个枚举值保留计算收获。其中第一个模板为大约递归过程(利用递归算法是为打听释的轻便,全面能够用其它算***换,以加速编译过程),后两个模板为n=0、1时的模板特化。穿越#define语句将模板调用简写成相仿函数调用的措施。过程在编译时计算所需的Fibonacci数列项,将收获作为常量嵌入编译好的过程。运行时直接利用收获,工夫混杂度恳挚地变成了O(1)。但这一措施最大的局限即便只能对常量嵌入,过程中揭示诸如计算Fib(i++)的情形则无能为力。尽管如此,这比在代码中手工计算并写入所需的值要直观准确,比穿越单纯的表驱动法“空间换时间”要得体迅捷。
7、函数对象法。此措施重要用于C++ STL编程的通用算法方面,其厉行行动也有别于以上其它措施:
class Fib { public: Fib() : a(0), b(1), n(0) { } unsigned long operator()() { if (n <= 1) { n++; return n - 1; }
else { int c; c = a + b; a = b; b = c; return c; } } private: int a, b, n; };
这个函数类对象的行动能够会意为一个“Fibonacci数列发生器”,其测验性调用如下,过程将顺次打印
void test(int i) { Fib fib; do { cout << fib() << endl; } while (i--); }
函数对象具有与函数指针相仿的行动,同时又能保留切身的一些属性,因而常用于STL通用算法编程。但针对个体的Fibonacci数列项求值,灵便性不及等闲的措施。
相关文章推荐
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#数据结构揭秘一
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 数据结构之Treap详解
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总