如果我们使用循环而不是递归,或者反之,在两者都可以达到相同目的的算法中,性能是否会受到影响?检查给定的字符串是否是回文。 我见过许多程序员使用递归作为一种炫耀的手段,当一个简单的迭代算法可以满足要求。 编译器在决定使用什么时起着至关重要的作用吗?
当前回答
如果你只是在一个列表上迭代,那么当然,迭代出去。
其他几个答案提到了(深度优先)树遍历。这真的是一个很好的例子,因为这是对一个非常普通的数据结构所做的非常普通的事情。对于这个问题,递归是非常直观的。
点击这里查看“查找”方法: http://penguin.ewu.edu/cscd300/Topic/BSTintro/index.html
其他回答
使用Chrome 45.0.2454.85 m,递归似乎要快得多。
代码如下:
(function recursionVsForLoop(global) {
"use strict";
// Perf test
function perfTest() {}
perfTest.prototype.do = function(ns, fn) {
console.time(ns);
fn();
console.timeEnd(ns);
};
// Recursion method
(function recur() {
var count = 0;
global.recurFn = function recurFn(fn, cycles) {
fn();
count = count + 1;
if (count !== cycles) recurFn(fn, cycles);
};
})();
// Looped method
function loopFn(fn, cycles) {
for (var i = 0; i < cycles; i++) {
fn();
}
}
// Tests
var curTest = new perfTest(),
testsToRun = 100;
curTest.do('recursion', function() {
recurFn(function() {
console.log('a recur run.');
}, testsToRun);
});
curTest.do('loop', function() {
loopFn(function() {
console.log('a loop run.');
}, testsToRun);
});
})(window);
结果
//使用标准for循环运行100次
循环运行100x。 完成时间:7.683ms
//使用带有尾递归的函数递归方法运行100次
100x递归运行。 完成时间:4.841毫秒
在下面的截图中,当每次测试运行300次循环时,递归再次以更大的优势获胜
在许多情况下,由于缓存提高了性能,递归更快。例如,这是一个使用传统归并例程的归并排序的迭代版本。它将比递归实现运行得慢,因为缓存改进了性能。
迭代实现
public static void sort(Comparable[] a)
{
int N = a.length;
aux = new Comparable[N];
for (int sz = 1; sz < N; sz = sz+sz)
for (int lo = 0; lo < N-sz; lo += sz+sz)
merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}
递归实现
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
PS -这是Kevin Wayne教授(普林斯顿大学)在Coursera上的算法课程上讲的。
据我所知,Perl没有优化尾递归调用,但是您可以伪造它。
sub f{
my($l,$r) = @_;
if( $l >= $r ){
return $l;
} else {
# return f( $l+1, $r );
@_ = ( $l+1, $r );
goto &f;
}
}
第一次调用时,它将在堆栈上分配空间。然后它将改变它的参数,并重新启动子例程,而不向堆栈添加任何东西。因此,它会假装从未调用过自己,将其转变为一个迭代过程。
注意,没有“my @_;”或“local @_;”,如果你这样做,它将不再工作。
比较递归和迭代就像比较十字螺丝刀和一字螺丝刀。在大多数情况下,你可以拆卸任何一个平头的十字螺钉,但如果你使用专为该螺钉设计的螺丝刀,那就更容易了,对吧?
有些算法只是适合递归,因为它们的设计方式(斐波那契数列,遍历树状结构等)。递归使算法更简洁,更容易理解(因此可共享和可重用)。
此外,一些递归算法使用“惰性评估”,这使得它们比迭代算法更有效。这意味着它们只在需要的时候执行昂贵的计算,而不是每次循环运行时都执行。
这应该足够让你开始了。我也会给你找一些文章和例子。
链接1:Haskel vs PHP(递归vs迭代)
下面是一个程序员必须使用PHP处理大型数据集的示例。他展示了在Haskel中使用递归处理是多么容易,但由于PHP没有简单的方法来完成相同的方法,他被迫使用迭代来获得结果。
http://blog.webspecies.co.uk/2011-05-31/lazy-evaluation-with-php.html
链接2:掌握递归
递归的坏名声大多来自于命令式语言的高成本和低效率。本文的作者讨论了如何优化递归算法,使其更快、更有效。他还介绍了如何将传统循环转换为递归函数,以及使用尾部递归的好处。我认为他的结束语总结了我的一些要点:
递归编程为程序员提供了一种更好的组织方式 以一种既可维护又逻辑一致的方式编写代码。” https://developer.ibm.com/articles/l-recurs/
链接3:递归比循环快吗?(回答)
下面是一个与你的问题类似的stackoverflow问题的答案链接。作者指出,许多与递归或循环相关的基准测试都是特定于语言的。命令式语言通常使用循环更快,使用递归更慢,函数式语言反之亦然。我想从这个链接中得到的主要观点是,在语言不可知论/情境盲目的意义上回答这个问题是非常困难的。
递归比循环快吗?
通常情况下,人们会期望性能损失在另一个方向上。递归调用会导致构建额外的堆栈帧;对此的惩罚各不相同。此外,在一些语言中,如Python(更准确地说,是在某些语言的某些实现中……),对于递归指定的任务,您可能很容易遇到堆栈限制,例如在树状数据结构中查找最大值。在这些情况下,你应该坚持使用循环。
编写好的递归函数可以在一定程度上降低性能损失,前提是你有一个优化尾部递归的编译器,等等(还要再次检查,确保函数真的是尾部递归——这是许多人都会犯的错误之一)。
除了“边缘”情况(高性能计算、非常大的递归深度等)之外,最好采用最清楚地表达您的意图、设计良好且可维护的方法。仅在确定需求后进行优化。
推荐文章
- .NET反射的成本有多高?
- 有效的方法应用多个过滤器的熊猫数据框架或系列
- 在c#中检查字符串是否只包含数字的最快方法
- JavaScript变量声明在循环外还是循环内?
- 什么是ORM,它是如何工作的,我应该如何使用它?
- 我能在服务器端应用程序(PHP、Ruby、Python等)上读取URL的哈希部分吗?
- 多少个参数是太多?
- 为什么C语言这么快,为什么其他语言没有这么快或更快?
- String与StringBuilder
- 为什么引入无用的MOV指令会加速x86_64汇编中的紧循环?
- 哪些是遗传算法/遗传规划解决方案的好例子?
- 如何在SQL中有效地计数列值的发生?
- 使用curl在PHP中获取HTTP代码
- 确定PHP中是否存在数组键的更快更好的方法是什么?
- 如何在O(n)中找到长度为n的无序数组中的第k大元素?