尝试回答此问题时,我遇到了一些奇怪的结果:How to improve the performance of the recursive method?

但是您无需阅读该帖子。我将在此处提供相关的上下文。
这看似冗长,但如果您通读一次,实际上并没有那么复杂。我希望这对所有人都有意义。对于上下文,

syra(n) = { 1 if n=1;
            n + syra(n/2) if n is even; and
            n + syra(3n+1) if n is odd
          }


syralen(n) = No. of steps to calculate syra (n)

例如,syralen(1)=1syralen(2)=2 since we need to go two steps.syra(10) = 10 + syra(5) = 15 + syra(16) = 31 + syra(8) = 39 + syra(4) = 43 + syra(2) = 45 + syra(1) = 46。因此syra(10)需要7个步骤。因此syralen(10)=7
最后,
lengths(n) = syralen(1)+syralen(2)+...+syralen(n)

那里的问题发布者正在尝试计算lengths(n)
我的问题是关于Op发布的递归解决方案(这是该问题的第二个片段)。我会在这里重新发布:
public class SyraLengths{

        int total=1;
        public int syraLength(long n) {
            if (n < 1)
                throw new IllegalArgumentException();
            if (n == 1) {
                int temp=total;
                total=1;
                return temp;
            }
            else if (n % 2 == 0) {
                total++;
                return syraLength(n / 2);
            }
            else {
                total++;
                return syraLength(n * 3 + 1);
            }
        }

        public int lengths(int n){
            if(n<1){
                throw new IllegalArgumentException();
            }
            int total=0;
            for(int i=1;i<=n;i++){
                total+=syraLength(i);
            }

            return total;
        }

        public static void main(String[] args){
            System.out.println(new SyraLengths().lengths(5000000));
        }
       }

当然,这是一种不寻常的(也许不建议这样做)递归的方法,但是它确实计算出正确的方法,我已经证实了这一点。我试图写一个更普通的递归版本:
public class SyraSlow {

    public long lengths(int n) {
        long total = 0;
        for (int i = 1; i <= n; ++i) {
            total += syraLen(i);
        }
        return total;
    }

    private long syraLen(int i) {
        if (i == 1)
            return 1;
        return 1 + ((i % 2 == 0) ? syraLen(i / 2) : syraLen(i * 3 + 1));
    }

现在这是一个奇怪的部分-我试图测试上述两个版本的性能,例如:
public static void main(String[] args){
            long t1=0,t2=0;
            int TEST_VAL=50000;

            t1 = System.currentTimeMillis();
            System.out.println(new SyraLengths().lengths(TEST_VAL));
            t2 = System.currentTimeMillis();
            System.out.println("SyraLengths time taken: " + (t2-t1));

            t1 = System.currentTimeMillis();
            System.out.println(new SyraSlow().lengths(TEST_VAL));
            t2 = System.currentTimeMillis();
            System.out.println("SyraSlow time taken: " + (t2-t1));
        }

对于TEST_VAL=50000,输出为:
5075114
SyraLengths time taken: 44
5075114
SyraSlow time taken: 31

正如预期的那样(我想),纯递归稍微好一些。但是当我更进一步并使用TEST_VAL=500000时,输出为:
62634795
SyraLengths time taken: 378
Exception in thread "main" java.lang.StackOverflowError
    at SyraSlow.syraLen(SyraSlow.java:15)
    at SyraSlow.syraLen(SyraSlow.java:15)
    at SyraSlow.syraLen(SyraSlow.java:15)

为什么? Java正在执行哪种优化,使得SyraLengths版本没有达到StackOverflow(即使在TEST_VAL=5000000上也可以工作)? 我什至尝试使用基于累加器的递归版本,以防万一我的JVM正在做一些尾部调用优化:
private long syraLenAcc(int i, long acc) {
        if (i == 1) return acc;
    if(i%2==0) {
        return syraLenAcc(i/2,acc+1);
    }
    return syraLenAcc(i * 3 + 1, acc+1);
    }

但是我仍然得到相同的结果(因此这里没有尾部调用优化)。 那么,这是怎么回事?

附注:如果可以想到的话,请编辑一个更好的标题。

最佳答案

在原始版本中,尾部递归优化是可能的(在JIT中)。邓诺(Dunno),无论它实际上是否发生。但是,使用堆[er,我的意思是堆栈]时,原始的效率可能会稍高一些。 (或者可能存在在粗略检查中不明显的功能差异。)

10-04 17:43