繁体   English   中英

具有Integer对象的Java递归方法StackOverflowError

[英]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);
        }
 }

因此,总而言之,请始终避免不必要的装箱/拆箱(从原语转换为包装器类型,即从intInteger ),这在您运行大量计算时(例如在递归中)变得更加重要 您可以在此处查找有关拳击如何导致执行问题的更多信息。

堆栈溢出不是基于例程的空间复杂度。 这意味着您已经使用了调用堆栈上的空间。 每个活动函数调用都占用一些堆栈空间,跟踪传入和传回的值,并经常进行上下文切换空间(寄存器值等)。

SmarterFib传递一个整数(一个字)和一个数组(一个字作为参考;总内存使用量为N (目标)字,外加几个字的开销)。 堆栈上总共有2N个字。

SmartestFib为每个调用传递四个整数,即堆栈上的4N个字。

我通常不希望它们对运行时的堆栈有完全不同的要求:如果SmartestFib在N = 130时将堆栈炸毁,我希望SmarterFib在N = 260之前就将其炸毁(有固定的通话开销)。

暂无
暂无

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

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