Java “线程中的异常”;“主要”;栈溢出
我有一段代码,但我不明白为什么它会在线程“main”java.lang.StackOverflowerr中给我异常 问题是:Java “线程中的异常”;“主要”;栈溢出,java,recursion,hashmap,stack-overflow,Java,Recursion,Hashmap,Stack Overflow,我有一段代码,但我不明白为什么它会在线程“main”java.lang.StackOverflowerr中给我异常 问题是: Given a positive integer n, prints out the sum of the lengths of the Syracuse sequence starting in the range of 1 to n inclusive. So, for example, the call: lengths(3) will return the th
Given a positive integer n, prints out the sum of the lengths of the Syracuse
sequence starting in the range of 1 to n inclusive. So, for example, the call:
lengths(3)
will return the the combined length of the sequences:
1
2 1
3 10 5 16 8 4 2 1
which is the value: 11. lengths must throw an IllegalArgumentException if
its input value is less than one.
我的代码:
import java.util.HashMap;
public class Test {
HashMap<Integer,Integer> syraSumHashTable = new HashMap<Integer,Integer>();
public Test(){
}
public int lengths(int n)throws IllegalArgumentException{
int sum =0;
if(n < 1){
throw new IllegalArgumentException("Error!! Invalid Input!");
}
else{
for(int i =1; i<=n;i++){
if(syraSumHashTable.get(i)==null)
{
syraSumHashTable.put(i, printSyra(i,1));
sum += (Integer)syraSumHashTable.get(i);
}
else{
sum += (Integer)syraSumHashTable.get(i);
}
}
return sum;
}
}
private int printSyra(int num, int count){
int n = num;
if(n == 1){
return count;
}
else{
if(n%2==0){
return printSyra(n/2, ++count);
}
else{
return printSyra((n*3)+1, ++count) ;
}
}
}
}
。
我知道问题在于递归。如果输入值很小,则不会发生错误,例如:5。但是当这个数字很大时,比如90090249,我在线程“main”java.lang.StackOverflowerr中得到了一个异常。谢谢大家的帮助。:)
我差点忘了错误消息:
Exception in thread "main" java.lang.StackOverflowError
at Test.printSyra(Test.java:60)
at Test.printSyra(Test.java:65)
at Test.printSyra(Test.java:60)
at Test.printSyra(Test.java:65)
at Test.printSyra(Test.java:60)
at Test.printSyra(Test.java:60)
at Test.printSyra(Test.java:60)
at Test.printSyra(Test.java:60)
这是递归算法的固有问题。如果递归的数量足够大,就无法真正避免堆栈溢出,除非该语言能够保证尾部调用优化(Java和大多数类似C的语言不能)。真正修复它的唯一方法是“展开”递归,迭代地重写算法或使用辅助函数来模拟递归调用的状态传递,而不实际嵌套调用。您的算法很好。但是,
int
对于您的计算来说太小了,此输入失败:
printSyra(113383, 1);
在某个点上,整数溢出为负值,您的实现会变得疯狂,无限递归。将int-num
更改为long-num
,一段时间内你会没事的。稍后您将需要biginger
请注意,根据Wikipedia on(粗体):
对于任何小于1亿的初始起始数,最长的级数为63728127,其步数949步。小于10亿的起始数字为670617279,步数为986步;小于100亿的起始数字为9780657630,步数为1132步
步骤总数相当于您所期望的最大嵌套级别(堆栈深度)。因此,即使是相对较大的数字,也不应出现堆栈溢出错误。请使用biginger
查看此实现:
private static int printSyra(BigInteger num, int count) {
if (num.equals(BigInteger.ONE)) {
return count;
}
if (num.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) {
return printSyra(num.divide(BigInteger.valueOf(2)), count + 1);
} else {
return printSyra(num.multiply(BigInteger.valueOf(3)).add(BigInteger.ONE), count + 1);
}
}
它甚至适用于非常大的价值:
printSyra(new BigInteger("9780657630"), 0) //1132
printSyra(new BigInteger("104899295810901231"), 0) //2254
一种解决方案是允许JVM使用java-Xss参数为堆栈递归占用更多空间。它的默认值小于1兆字节,IIRC,最多可以限制为几百个递归 更好的解决方案是重写练习而不使用递归:
private int printSyra(int num){
int count = 1;
int n = num;
while(n != 1){
if(n%2==0){
n = n/2;
++count;
}
else{
n=(n*3)+1;
++count;
}
}
return count;
}
很可能是整数溢出。用long代替。(这里不需要使用递归,尽管如果实现正确,对于某些数字范围应该不会出现堆栈溢出错误)。我在这里没有看到递归??你在隐瞒什么吗??你的
printSyra(i,1)
方法在哪里?@RohitJainprintSyra
调用printSyra
@Pshemo。。你怎么能这么想??它不在那里。@RohitJain检查printSyra
方法的返回。不确定为什么要进行向下投票。。我错过什么了吗?如果你投反对票,请发表评论。递归在这里很好。真正的问题是整数溢出。嗯,你确定吗由于应用程序递归太深而发生堆栈溢出时引发。我确定,因为我知道OP在printSyra
中正在做什么。应该有足够的堆栈用于几百个级别。@Ray.R.Chua最好的解决方案是在不使用递归的情况下实现它(这样可以避免StackOverflow错误)。另外,由于您无法知道在计算3x+1时将生成的最大数,因此请避免使用整数,并使用更大的long
或可能是BigInteger。我想我自己也很困惑,在这里-我认为堆栈溢出通常发生在相对较小的堆栈(存储函数状态的地方)时递归调用太多后内存不足,不是因为某个整数太大。除非我误解了你的答案?我同意使用int而不是long可能是OP问题的真正原因,但仍然有可能递归到足够深,从而导致堆栈溢出,而不管输入数字有多大。@Tomasz:谢谢。我已经将int num更改为long num,现在我面临另一个错误:java.util.HashMap.resize(HashMap.java:462)处java.util.HashMap.addEntry(HashMap.java:755)处java.util.HashMap.put(HashMap.java:385)处Test.length(Test.java:26)处TestApp.main(TestApp.java:10)处的线程“main”java.OutOfMemoryError中的异常我对这个答案的主要问题是:技术上正确,但有误导性。更好的解决方案是完全避免递归(参见@greyfairer的答案)。事实上,在大多数情况下(在Java中),你应该避免递归,但没有很好的理由。@Kiyura:我写道:“[…]整数溢出[…]并且你的实现变得疯狂,无限递归”-这个实现失败的原因不是因为递归太深(正如维基百科所说,它从来没有那么深),但由于整数溢出,这会导致异常深的递归。当实现正确时,StackOverflowerError
不是问题。但是你是对的,如果没有尾部递归优化,这个实现仍然有点低效——但从可读性的角度来看,这一点非常清楚。@Ray.R.Chua:在接近尾声时,你的HashMap将存储大约1亿个元素。你需要它吗?至于加速,如果您遇到在使用HashMap之前计算过的元素,您可以在printSyra中尽早返回值。我的递归实现可以在长度为2254()的N=104899295810901231下运行。虽然我同意递归可能是一个问题,但在递归问题出现之前,中间数可能会溢出整数的范围。您的版本仍然无法计算小到113383
-的数,但不是抛出StackOverflowException
,而是进入无限循环。此外,增加堆栈大小不会有助于断开的算法(错误的变量类型)。
private int printSyra(int num){
int count = 1;
int n = num;
while(n != 1){
if(n%2==0){
n = n/2;
++count;
}
else{
n=(n*3)+1;
++count;
}
}
return count;
}