简体   繁体   English

Lua与Java的递归

[英]Recursion in Lua vs. Java

I have a recursive algorithm (in three different forms) which computes the sum of the first n odd positive integers. 我有一个递归算法(以三种不同的形式),它计算前n个奇数正整数的总和。 These algorithms are for learning only, I'm aware that there are better ways of solving this problem. 这些算法仅供学习,我知道有更好的方法可以解决这个问题。 I've written the three variants in both Lua and Java, which I'll call Lua 1, Lua 2, Lua 3 and Java 1, Java 2 and Java 3. The first two are very similar, just rearranged. 我已经在Lua和Java中编写了三个变体,我将其称为Lua 1,Lua 2,Lua 3和Java 1,Java 2和Java 3.前两个非常相似,只是重新排列。

The Lua programs are here , and the Java programs are here . Lua程序在这里 ,Java程序在这里

Lua 1 and 2 perform extremely well and could easily reach n = 100,000,000. Lua 1和2表现非常好,很容易达到n = 100,000,000。 Lua 3 hits a stack overflow where n > 16,000. Lua 3遇到堆栈溢出,其中n> 16,000。

Java 1 could only reach n = 4000 before hitting a stack overflow while Java 2 reached 9000. Java 3 managed to get to n = 15,000 before again hitting a stack overflow. Java 1只能在达到堆栈溢出之前达到n = 4000而Java 2达到9000.在再次遇到堆栈溢出之前,Java 3设法达到n = 15,000。

Can anyone explain these results? 谁能解释这些结果? Why did Java 1, 2 and 3 perform so poorly while Lua 1 and 2 performed so well? 为什么Java 1,2和3表现如此糟糕而Lua 1和2表现如此之好?

Lua does tail-call elimination . Lua做尾声消除 For example, a function like this: 例如,这样的函数:

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

will never cause stack overflow no matter what value of n you are calling. 会不会导致堆栈溢出无论什么价值n您呼叫。

In Lua, only a call with the form return func(args) is a tail call, like what your first two Lua programs do. 在Lua中,只有一个带有return func(args)形式的调用是尾调用,就像你的前两个Lua程序那样。 But in the third Lua program: 但在第三个Lua计划中:

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

Lua still need to do the calculation before returning, so there's no proper tail call. Lua在返回之前仍然需要进行计算,所以没有正确的尾调用。

Java is not designed for recursive algorithms. Java不是为递归算法设计的。 In particular it doesn't support common optimisations like tail-call optimisation. 特别是它不支持常见的优化,如尾调用优化。

Java is better suited to use loops and generally is much faster, and often simple to use a plain loop. Java更适合使用循环,通常更快,并且通常使用简单循环很简单。

If you use iteration and a Deque, you should fine there is almost no limit to the value of n 如果你使用迭代和Deque,你应该很好, n的值几乎没有限制

When you are running very inefficient code, you tend to find that whether one case or another can optimise way the inefficiency can make a big difference. 当您运行非常低效的代码时,您倾向于发现无论是一种情况还是其他情况都可以优化低效率的方式可以产生很大的不同。

One way to do it more efficiently 一种更有效的方法

// 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);
}

prints 版画

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM