Performance 递归还是迭代?

Performance 递归还是迭代?,performance,algorithm,language-agnostic,recursion,Performance,Algorithm,Language Agnostic,Recursion,如果我们在算法中使用循环而不是递归,那么性能会受到影响吗?如果在算法中使用循环而不是递归,那么性能会受到影响吗?检查给定的字符串是否是回文。 我见过许多程序员使用递归作为一种手段来炫耀一个简单的迭代算法何时能满足要求。 编译器在决定使用什么方面起着至关重要的作用吗?这取决于语言。在Java中,应该使用循环。函数式语言优化递归。使用递归,每次“迭代”都会产生函数调用的成本,而对于循环,通常只需要增加/减少一次。因此,如果循环的代码并不比递归解决方案的代码复杂多少,那么循环通常会优于递归 我认为在(

如果我们在算法中使用循环而不是递归,那么性能会受到影响吗?如果在算法中使用循环而不是递归,那么性能会受到影响吗?检查给定的字符串是否是回文。 我见过许多程序员使用递归作为一种手段来炫耀一个简单的迭代算法何时能满足要求。
编译器在决定使用什么方面起着至关重要的作用吗?

这取决于语言。在Java中,应该使用循环。函数式语言优化递归。

使用递归,每次“迭代”都会产生函数调用的成本,而对于循环,通常只需要增加/减少一次。因此,如果循环的代码并不比递归解决方案的代码复杂多少,那么循环通常会优于递归

我认为在(非尾部)递归中,每次调用函数时分配一个新堆栈等都会对性能造成影响(当然取决于语言)。

递归的内存开销更大,因为每个递归调用通常都需要将内存地址推送到堆栈中,以便以后程序可以返回到该点


尽管如此,在很多情况下,递归比循环更自然、可读性更强,就像处理树一样。在这些情况下,我建议坚持递归。

通常,人们会认为性能的损失在另一个方向。递归调用可能导致构造额外的堆栈帧;对此的惩罚各不相同。此外,在某些语言(如Python)(更准确地说,在某些语言的某些实现中…)中,对于可能递归指定的任务,例如查找树数据结构中的最大值,可以很容易地遇到堆栈限制。在这些情况下,您确实希望使用循环

编写好的递归函数可以在一定程度上降低性能损失,假设您有一个优化尾部递归的编译器,等等(还要仔细检查以确保函数确实是尾部递归的——这是许多人会犯错误的事情之一)

除了“边缘”情况(高性能计算、非常大的递归深度等),最好采用最清楚地表达意图、设计良好且可维护的方法。仅在确定需求后进行优化。

这取决于“递归深度”。 这取决于函数调用开销对总执行时间的影响程度

例如,以递归方式计算经典阶乘非常低效,因为: -数据溢出的风险 -堆栈溢出的风险 -函数调用开销占执行时间的80%


在国际象棋游戏中开发一个用于位置分析的最小-最大算法,该算法将分析随后的N个动作,可以在“分析深度”上递归实现(正如我所做的^ ^ ^)

递归可能会更昂贵,这取决于递归函数是否是(最后一行是递归调用)。尾部递归应该被编译器识别并优化为其迭代对应项(同时保持代码中简洁、清晰的实现)


我会以最有意义的方式编写算法,对于那些需要在几个月或几年内维护代码的可怜虫(无论是你自己还是其他人)来说,这是最清晰的。如果您遇到性能问题,那么请分析您的代码,然后,只有这样,您才能通过转向迭代实现来进行优化。您可能需要查看和。

在使用递归时,您的性能会下降,因为在任何语言中调用方法都意味着大量的准备工作:调用代码发布返回地址、调用参数、一些其他上下文信息(如处理器寄存器)可能会保存在某个位置,在返回时,被调用的方法发布一个返回值,然后调用方检索该值,并且之前保存的任何上下文信息都将被恢复。迭代方法和递归方法之间的性能差异在于这些操作所需的时间


从实现的角度来看,当处理调用上下文所需的时间与执行方法所需的时间相当时,您确实开始注意到差异。如果递归方法的执行时间比调用上下文管理部分的执行时间长,那么请使用递归方法,因为代码通常更可读、更容易理解,并且不会注意到性能损失。否则,出于效率的考虑,需要进行迭代

我认为java中的尾部递归目前还没有得到优化。有关LtU和相关链接的详细信息将贯穿整个讨论过程。它可能是即将发布的第7版中的一个功能,但显然,当与堆栈检查结合使用时,它会出现某些困难,因为某些帧会丢失。自Java2以来,堆栈检查已被用于实现其细粒度安全模型


循环可以为您的程序实现性能增益。递归可能会为程序员带来性能增益。选择在你的情况下哪个更重要

迈克是对的。尾部递归不是由Java编译器或JVM优化的。您总是会遇到如下情况的堆栈溢出:

int count(int i) {
  return i >= 100000000 ? i : count(i+1);
}

据我所知,Perl不会优化尾部递归调用,但您可以伪造它

sub f{
  my($l,$r) = @_;

  if( $l >= $r ){
    return $l;
  } else {

    # return f( $l+1, $r );

    @_ = ( $l+1, $r );
    goto &f;

  }
}
第一次调用时,它将在堆栈上分配空间。然后,它将更改其参数,并重新启动子例程,而不向堆栈中添加任何内容。因此,它将假装从未调用过自己,将其更改为一个迭代过程


请注意,没有“
my@;
”或“
local@;
”,如果您这样做了,它将不再工作。

递归和迭代取决于您想要实现的业务逻辑,尽管在大多数情况下它可以在中使用
data Tree a = Branch (Tree a) (Tree a)
            | Leaf a
            deriving (Eq)
example :: Tree Int
example = Branch (Leaf 1) 
                 (Branch (Leaf 2) 
                         (Leaf 3))
addOne :: Tree Int -> Tree Int
addOne (Branch a b) = Branch (addOne a) (addOne b)
addOne (Leaf a)     = Leaf (a + 1)
instance Functor Tree where fmap f (Leaf a)     = Leaf (f a)
                            fmap f (Branch a b) = Branch (fmap f a) (fmap f b)
addOne' = fmap (+1)
addOne'' = cata go where
           go (Leaf a) = Leaf (a + 1)
           go (Branch a b) = Branch a b
(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);
iterative + optimisation flag -O3 -> 2.79.. sec
recursive + optimisation flag -O3 -> 1.32.. sec

iterative + No-optimisation flag  -> 2.83.. sec
recursive + No-optimisation flag  -> 4.15.. sec
    //key-task, value-list of tasks the key task depends on
    //"adjacency map":
    Map<Integer, List<Integer>> tasksMap = new HashMap<>();
    tasksMap.put(0, new ArrayList<>());
    tasksMap.put(1, new ArrayList<>());

    List<Integer> t2 = new ArrayList<>();
    t2.add(0);
    t2.add(1);
    tasksMap.put(2, t2);

    List<Integer> t3 = new ArrayList<>();
    t3.add(2);
    t3.add(10);
    tasksMap.put(3, t3);

    List<Integer> t4 = new ArrayList<>();
    t4.add(3);
    tasksMap.put(4, t4);

    List<Integer> t5 = new ArrayList<>();
    t5.add(3);
    tasksMap.put(5, t5);

    tasksMap.put(6, new ArrayList<>());
    tasksMap.put(7, new ArrayList<>());

    List<Integer> t8 = new ArrayList<>();
    t8.add(5);
    tasksMap.put(8, t8);

    List<Integer> t9 = new ArrayList<>();
    t9.add(4);
    tasksMap.put(9, t9);

    tasksMap.put(10, new ArrayList<>());

    //task to analyze:
    int task = 5;


    List<Integer> res11 = getTasksInOrderDftReqPostOrder(tasksMap, task);
    System.out.println(res11);**//note, no reverse required**

    List<Integer> res12 = getTasksInOrderDftReqPreOrder(tasksMap, task);
    Collections.reverse(res12);//note reverse!
    System.out.println(res12);

    private static List<Integer> getTasksInOrderDftReqPreOrder(Map<Integer, List<Integer>> tasksMap, int task) {
         List<Integer> result = new ArrayList<>();
         Set<Integer> visited = new HashSet<>();
         reqPreOrder(tasksMap,task,result, visited);
         return result;
    }

private static void reqPreOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {

    if(!visited.contains(task)) {
        visited.add(task);
        result.add(task);//pre order!
        List<Integer> children = tasksMap.get(task);
        if (children != null && children.size() > 0) {
            for (Integer child : children) {
                reqPreOrder(tasksMap,child,result, visited);
            }
        }
    }
}

private static List<Integer> getTasksInOrderDftReqPostOrder(Map<Integer, List<Integer>> tasksMap, int task) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    reqPostOrder(tasksMap,task,result, visited);
    return result;
}

private static void reqPostOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
    if(!visited.contains(task)) {
        visited.add(task);
        List<Integer> children = tasksMap.get(task);
        if (children != null && children.size() > 0) {
            for (Integer child : children) {
                reqPostOrder(tasksMap,child,result, visited);
            }
        }
        result.add(task);//post order!
    }
}
    List<Integer> res1 = getTasksInOrderDftStack(tasksMap, task);
    Collections.reverse(res1);//note reverse!
    System.out.println(res1);

    private static List<Integer> getTasksInOrderDftStack(Map<Integer, List<Integer>> tasksMap, int task) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    Stack<Integer> st = new Stack<>();


    st.add(task);
    visited.add(task);

    while(!st.isEmpty()){
        Integer node = st.pop();
        List<Integer> children = tasksMap.get(node);
        result.add(node);
        if(children!=null && children.size() > 0){
            for(Integer child:children){
                if(!visited.contains(child)){
                    st.add(child);
                    visited.add(child);
                }
            }
        }
        //If you put it here - it does not matter - it is anyway a pre-order
        //result.add(node);
    }
    return result;
}
dft(n){
    mark(n)
    for(child: n.children){
        if(marked(child)) 
            explode - cycle found!!!
        dft(child)
    }
    unmark(n)
}