用java实现奇异递归优化
当我试图回答这个问题时,我遇到了一些奇怪的结果: 但是你不需要读那篇文章。我将在这里给出相关的上下文。 这可能看起来很长,但如果你通读一遍,其实并没有那么复杂。我希望大家都会感兴趣。就上下文而言用java实现奇异递归优化,java,recursion,stack-overflow,tail-recursion,Java,Recursion,Stack Overflow,Tail Recursion,当我试图回答这个问题时,我遇到了一些奇怪的结果: 但是你不需要读那篇文章。我将在这里给出相关的上下文。 这可能看起来很长,但如果你通读一遍,其实并没有那么复杂。我希望大家都会感兴趣。就上下文而言 syra(n) = { 1 if n=1; n + syra(n/2) if n is even; and n + syra(3n+1) if n is odd } 及 e、 g,, syralen(1)=1,syralen(2)
syra(n) = { 1 if n=1;
n + syra(n/2) if n is even; and
n + syra(3n+1) if n is odd
}
及
e、 g,,
syralen(1)=1
,syralen(2)=2,因为我们需要执行两个步骤。
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)
那里的问题海报试图计算长度(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));
}
}
对于测试值=50000
,输出为:
5075114
SyraLengths time taken: 44
5075114
SyraSlow time taken: 31
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)
正如预期的那样(我猜),普通递归稍微好一点。但当我进一步使用TEST\u VAL=500000
时,输出是:
5075114
SyraLengths time taken: 44
5075114
SyraSlow time taken: 31
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版本不会出现堆栈溢出(它甚至在TEST\u 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中)。但不知道它是否真的发生了。但有可能原始版本在使用堆(呃,我指的是堆栈)时效率稍高一些。(或者可能有一个功能上的差异,在粗略的检查中并不明显。)好吧,结果有一个简单的解释:
我使用
长syraLen(int n)
作为方法签名。但是n
的值实际上可以比Integer.MAX\u值大得多。因此,syraLen
得到了负输入,这就是问题所在。如果我将其更改为long syraLen(long n)
,一切都会完美运行!如果(n<1)抛出新的IllegalArgumentException(),我希望我也把放进去代码>与原始海报相似。会为我节省一些时间。当然可以。如果函数的最后一条语句是对自身的调用,则尾部递归可防止大量使用堆栈。在这种情况下,粗略地说,呼叫可以由跳转代替。函数基本上被包装成一种循环,将递归转化为迭代。(不要对我大喊大叫,我知道这有点过于简化。)但我已经尝试了第二个版本,使用累加器,如果尾部调用优化发生了,它应该会有所帮助。根据这个:尾部优化还没有出现。查看尾部调用和尾部递归
。请看这里:?
语句可能会混淆优化器的检查结果,并导致它放弃尾部递归优化。@Xeon..正是。。这就是我无法理解的原因。
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);
}