【问题标题】:Recursion in Lua vs. JavaLua 与 Java 中的递归
【发布时间】:2013-07-24 16:28:32
【问题描述】:

我有一个递归算法(以三种不同的形式),它计算前 n 个奇数正整数的总和。这些算法仅用于学习,我知道有更好的方法来解决这个问题。我已经用 Lua 和 Java 编写了三个变体,我将它们称为 Lua 1、Lua 2、Lua 3 和 Java 1、Java 2 和 Java 3。前两个非常相似,只是重新排列。

Lua 程序为here,Java 程序为here

Lua 1 和 2 的表现非常好,很容易达到 n = 100,000,000。 Lua 3 在 n > 16,000 时遇到堆栈溢出。

Java 1 在遇到堆栈溢出之前只能达到 n = 4000,而 Java 2 达到 9000。Java 3 在再次遇到堆栈溢出之前设法达到 n = 15,000。

谁能解释这些结果?为什么 Java 1、2 和 3 表现如此糟糕,而 Lua 1 和 2 表现如此出色?

【问题讨论】:

    标签: java recursion lua stack-overflow


    【解决方案1】:

    Lua 执行tail-call elimination。比如这样的函数:

    function foo (n)
        if n > 0 then return foo(n - 1) end
    end
    

    无论您调用的n 是什么值,都不会导致堆栈溢出。

    在 Lua 中,只有 return func(args) 形式的调用是尾调用,就像前两个 Lua 程序所做的一样。但是在第三个 Lua 程序中:

    return (sumOdds3(n-1)) + (2*n - 1)
    

    Lua 在返回之前仍然需要进行计算,所以没有适当的尾调用。

    【讨论】:

      【解决方案2】:

      Java 不是为递归算法设计的。特别是它不支持尾调用优化等常见优化。

      Java 更适合使用循环,通常速度更快,而且使用普通循环通常很简单。

      如果你使用迭代和双端队列,你应该没问题n的值几乎没有限制

      当您运行非常低效的代码时,您往往会发现无论是一种情况还是另一种情况都可以优化方式,但效率低下会产生很大的影响。

      一种更有效的方法

      // function to compute the sum of the first n odd positive integers
      public static long sumOdds(long n) {
          long sumAll = n * (n + 1)/2;
          long sumEven = n/2 * (n/2 + 1);
          return sumAll - sumEven;
      }
      public static void main(String[] args) throws Exception {
          sumOdds(1);
      
          long start = System.nanoTime();
          long l = sumOdds(Integer.MAX_VALUE);
          long time = System.nanoTime() - start;
          System.out.printf("sumOdds(%,d) took %,d ns%n", Integer.MAX_VALUE, time);
      }
      

      打印

      sumOdds(2,147,483,647) took 343 ns
      

      【讨论】:

        猜你喜欢
        • 2019-01-17
        • 2012-08-29
        • 2015-03-15
        • 2011-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-01
        相关资源
        最近更新 更多