[英]Java recursion method StackOverflowError with Integer object
我一直在尝试制作一个斐波那契数字返回器(假设输入n返回斐波那契序列中索引位置n的元素)。 我试图使其既递归又具有较低的空间复杂度(我不实例化任何新变量)。 我将Integer对象用作值,尽管我知道它们的值会溢出(它们返回负值),但这实际上是故意的(出于教育目的)。 该功能之所以称为smartestFib,是因为它的空间复杂度低于我的其他功能。
当我将smartestFib(n)的值设置为130或更高时,就会出现问题。 对于129或更低的版本,它工作得很好(考虑到溢出),但是对于130或更高版本,它给出了例外。 主要问题是我无法找出异常的真正含义:它显示了太多错误,导致我看不到第一个错误,因此不知道确切的错误是什么。 因为我不知道错误的类型,所以我无法捕获它。
我的代码是:
private static int smartestFib(Integer goalNum)
{
if (goalNum < 2)
return 1;
else
return smartestFib(goalNum-2, 0, 1,1);
}
private static int smartestFib(Integer goalNum, Integer currentNum, Integer lastValue, Integer beforeLastValue)
{
if (goalNum == currentNum)
return lastValue + beforeLastValue;
else
{
return smartestFib(goalNum, currentNum + 1, lastValue+beforeLastValue,lastValue);
}
}
对于这个问题,我将不胜感激,因为问题的任意性使我相信这是我不知道的技术问题,也不知道在哪里寻找。 提前非常感谢。
编辑:显然这可能是一个StackOverflowError,非常感谢! 但是现在我想知道,我是否还有其他功能,具有更高的空间复杂度,并且它们没有这个问题。 怎么样?
private static int smarterFib(int goalNum)
{
assert (goalNum >= 0): "The variable goalNum may not negative.";
if (goalNum < 2)
return 1;
else
{
ArrayList<Integer> sequenceList = new ArrayList<Integer>(Arrays.asList(new Integer[] {1,1}));
return smarterFib(goalNum-2, sequenceList);
}
}
private static int smarterFib(int goalNum, ArrayList<Integer> priorValues)
{
assert (goalNum >= 0): "The variable goalNum may not negative.";
assert (priorValues != null): "The array priorValues has not been initialized.";
if (goalNum <= priorValues.size()-2)
return (priorValues.get(priorValues.size()-1) + priorValues.get(priorValues.size()-2));
else
{
priorValues.add(priorValues.get(priorValues.size()-1) + priorValues.get(priorValues.size()-2));
return smarterFib(goalNum, priorValues);
}
}
我不明白为什么这个不会引起问题,而我的新的却会引起问题。
递归程序通过再次调用相同的方法并获得收益(即通过调用该方法的线程创建越来越多的stackframes
)来工作,并在达到默认堆栈大小之后,将得到stackoverflowerror
。
因此,要解决此问题,您需要通过传递-Xss
作为JVM参数来增加堆栈大小,您可以在此处查看 。
我还有其他功能,具有更高的空间复杂度,而且它们没有此问题。 怎么样?
因此,第一和第二程序之间的区别如下:
区别在于,一个使用Boxing(当对goalNum
变量使用Integer
类型时),另一个使用原始int
;当您使用Boxing时,这会导致性能问题,并且程序无法完成。
因此,将您的goalNum
变量从Integer
类型更改为int
,然后它可以工作(我测试过):
private static int smartestFib(int goalNum, int currentNum,
int lastValue, int beforeLastValue) {
System.out.println(goalNum);
if (goalNum == currentNum)
return lastValue + beforeLastValue;
else {
return smartestFib(goalNum,
currentNum + 1, lastValue+beforeLastValue,lastValue);
}
}
因此,总而言之,请始终避免不必要的装箱/拆箱(从原语转换为包装器类型,即从int
为Integer
),这在您运行大量计算时(例如在递归中)变得更加重要 。 您可以在此处查找有关拳击如何导致执行问题的更多信息。
堆栈溢出不是基于例程的空间复杂度。 这意味着您已经使用了调用堆栈上的空间。 每个活动函数调用都占用一些堆栈空间,跟踪传入和传回的值,并经常进行上下文切换空间(寄存器值等)。
SmarterFib传递一个整数(一个字)和一个数组(一个字作为参考;总内存使用量为N (目标)字,外加几个字的开销)。 堆栈上总共有2N个字。
SmartestFib为每个调用传递四个整数,即堆栈上的4N个字。
我通常不希望它们对运行时的堆栈有完全不同的要求:如果SmartestFib在N = 130时将堆栈炸毁,我希望SmarterFib在N = 260之前就将其炸毁(有固定的通话开销)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.