使用尾递归计算Fibonacci数列
2007-12-19 22:54
176 查看
在过程式,面向对象编程中我们使用递归解决问题的机会不多.但是使用递归方式解决问题是一种比较直观而且简洁的方式,不过编译器对递归没有特别的优化.所以我们很容易写出效率不高的递归程序.而所谓尾递归就是在递归的时候进行计算.下面我以Fibonacci数列为例来说明普通递归和尾递归的不同.
普通递归:
public long Fib_Common(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Fib_Common(n - 1) + Fib_Common(n - 2);
}
这个实现简单明了就是执行速度太慢了,因为编译器是以如下方式进行计算的(例如计算Fib(6)):
Fib(6) = Fib(5) + Fib(4);
= Fib(4) + Fib(3) + Fib(3) + Fib(2);
= Fib(3) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
= Fib(2) + Fib(1) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
= 8
从上面的递归展开式可以看出Fib(4),Fib(3)都被计算了2次,而且递归函数以2的指数增长。所以当计算到30时就变得非常慢。
尾递归:
public long Fib_Tail(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Tail(n, 1, 1, 3);
}
private long Tail(int n, long b1, long b2, int begin)
{
if (n == begin)
{
return b1 + b2;
}
else
return Tail(n, b2, b1 + b2, begin + 1);
}
再来看看Fib_Tail(3)的计算过程
Fib_tail(6) = Tail(6, 1, 2, 3)
= Tail(6, 2, 3, 4)
= Tail(6, 3, 5, 5)
= 8
这样计算过程都是在每次进入递归函数时计算的(尾部),所以是一个线性增长。只要编译器允许我们可以计算Fib_tail(100)都非常迅速.
正常循环:
public long Fib_Loop(int n)
{
long b1 = 1, b2 = 1, temp, begin = 3;
for (; begin <= n; ++begin)
{
temp = b1;
b1 = b2;
b2 = temp + b2;
}
return b2;
}
尾递归一般都是在函数式编程中出现,而且FP语言的编译器有优化,实际上还是尽量转化成循环形式.不过用递归的方式挺锻炼思维的.
普通递归:
public long Fib_Common(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Fib_Common(n - 1) + Fib_Common(n - 2);
}
这个实现简单明了就是执行速度太慢了,因为编译器是以如下方式进行计算的(例如计算Fib(6)):
Fib(6) = Fib(5) + Fib(4);
= Fib(4) + Fib(3) + Fib(3) + Fib(2);
= Fib(3) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
= Fib(2) + Fib(1) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
= 8
从上面的递归展开式可以看出Fib(4),Fib(3)都被计算了2次,而且递归函数以2的指数增长。所以当计算到30时就变得非常慢。
尾递归:
public long Fib_Tail(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Tail(n, 1, 1, 3);
}
private long Tail(int n, long b1, long b2, int begin)
{
if (n == begin)
{
return b1 + b2;
}
else
return Tail(n, b2, b1 + b2, begin + 1);
}
再来看看Fib_Tail(3)的计算过程
Fib_tail(6) = Tail(6, 1, 2, 3)
= Tail(6, 2, 3, 4)
= Tail(6, 3, 5, 5)
= 8
这样计算过程都是在每次进入递归函数时计算的(尾部),所以是一个线性增长。只要编译器允许我们可以计算Fib_tail(100)都非常迅速.
正常循环:
public long Fib_Loop(int n)
{
long b1 = 1, b2 = 1, temp, begin = 3;
for (; begin <= n; ++begin)
{
temp = b1;
b1 = b2;
b2 = temp + b2;
}
return b2;
}
尾递归一般都是在函数式编程中出现,而且FP语言的编译器有优化,实际上还是尽量转化成循环形式.不过用递归的方式挺锻炼思维的.
相关文章推荐
- 使用尾递归计算阶乘
- 使用NSAttributedString来计算行高注意事项
- 使用BigDecimal进行精确计算工具类
- js使用FileReader和Google的md5.js计算文件的MD5值
- 优化ImageView,在使用ListView或GridView的时候,每次更新图片的时候,在显示新的图片和旧的图片一样大小的时候,不需要去重新计算大小
- 使用开源Grid平台-GridGain实现网格计算
- 不使用sizeof, 计算int的位数
- 只使用指向二叉树的根的一个指针T,计算T中节点的个数,T中树叶的片数,T中满节点的个数
- 使用开源Grid平台-GridGain实现网格计算
- 使用calc()计算元素宽度
- spark【例子】同类合并、计算(主要使用groupByKey)
- 使用console进行性能测试和计算代码运行时间
- 使用测试工具时候如何计算设置并发数?
- 创建一个学生类,按照要求生成各种方法,然后使用数学方法计算某一个元素的规定结果
- 使用 Linux 和 Hadoop 进行分布式计算
- 一个用于每一天JavaScript示例-使用缓存计算(memoization)为了提高应用程序性能
- 使用PHP similar text计算两个字符串相似度
- 创建一个三角形类并且使用成员函数计算三角形的周长和面积《2》
- 使用perl计算日期
- 使用并行计算大幅提升递归算法效率