斐波纳契数列不同实现比较

来源:互联网 发布:仿商城源码免费无授权 编辑:程序博客网 时间:2024/04/30 15:47

斐波纳契数列形如:0,1,2,3,5,8,13,....。该序列的通用方程是:Fib(n)=Fib(n-1)+Fib(n-2).
1.使用递归

unsigned RecursiveFib(unsigned n){if (n <= 1)return n;return RecursiveFib(n-1) + RecursiveFib(n-2);}


eg:RecursiveFib(0xFF); 信不信由你,这个简单函数的时间复杂度是指数级的,它非常低效。

2.使用循环

unsigned RecursiveFib(unsigned n){if (n <= 1)return n;unsigned f_2 = 0;unsigned f_1 = 1;unsigned f;for (unsigned i = 2; i <= n; i ++){f = f_1 + f_2;f_2 = f_1;f_1 = f;}return f;} 


eg:RecursiveFib(0xFFFFFF);解决了1中低效的问题。

3.使用模板函数

template<unsigned N> struct Fib{enum{Val = Fib<N-1>::Val + Fib<N-2>::Val};};template <> struct Fib<0> {enum{Val = 0};};template <> struct Fib<1> {enum{Val = 1};};#define RecursiveFib3(n) Fib<n>::Val 


eg:RecursiveFib(4);
关于此模板化版本需要注意以下几点:
.模板函数并不是真正的函数--它是叫作Val的枚举整数,在编译期递归生成。语句Val = Fib<N-1>::Val + Fib<N-2>::Val不常见,但是合法。
.Fib被定义为结构,以简化标记。在默认情况下结构数据是公用的,这也正是我们所需要的。
.模板参数N用于指定函数的输入。这种模板参数的使用并不常见,但完全可行。例数,std::bitset<N> 用N的数值作为它的模板参数来定义表示位域的位数。数字参数必须在编译期被获知,如果当i还是可变的变量时调用RecursiveFib(i),则会产生编译错误。
.要中止递归,需要正确地处理结束条件。对于斐波纳契数来说,结束条件就是当N为0和1时。在模板中处理基本情况的方法是使用模板特化。标记了template<>的,表示为模板特化。当N为0和1时,Val=N.

分析编译程序如何计算RecursiveFib(4):

=Fib<4>::Val=Fib<3>::Val + Fib<2>::Val=Fib<2>::Val + Fib<1>::Val + Fib<1>::Val + Fib<0>::Val=Fib<1>::Val + Fib<0>::Val + 1 + 1 + 0=1 + 0 + 1 + 1 + 0=3


由于所有的输入在编译期都确定了,所以编译程序可以将RecursiveFib(N)换算为常量。换句话来说,编译程序可以生成与以下完全等价的代码:

std::cout<<3;//std::cout<<RecursiveFib(4)

该方法可以成为你C++工具包中有用工具。你很可能会有指数级运行时间不能降为常数级运行时间的情况,通过使用模板无编程,就可以通过增加额外的编译时间来降低程序的执行时间。对于游戏来说,执行时间通常比编译时间更重要,所以这项技术也将非常有用。