【问题标题】:Java recursion method StackOverflowError with Integer object带有整数对象的Java递归方法StackOverflowError
【发布时间】:2016-12-02 19:32:40
【问题描述】:

我一直在尝试制作一个斐波那契数返回器(给定输入 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);
    }
}

我不明白为什么这个不会引起问题,而我的新问题会..

【问题讨论】:

标签: java recursion runtime-error fibonacci


【解决方案1】:

递归程序通过再次调用相同的方法并获得(即,通过调用该方法的线程创建更多和更多stackframes)来工作,并且在达到默认堆栈大小后,您将获得stackoverflowerror

所以要解决这个问题,你需要通过传递-Xss作为JVM参数来增加堆栈大小,你可以查看here

我还有其他功能,空间复杂度更高,它们不是 有这个问题。怎么样?

所以第一和第二程序的区别解释如下:

不同之处在于一个使用 Boxing(当您使用 Integer 类型作为 goalNum 变量时),另一个使用原始 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),这在您运行大量计算时变得更加重要(例如在您的递归中) 。您可以查看here 以了解有关拳击如何导致执行问题的更多信息。

【讨论】:

  • 感谢您的评论,这真的很有帮助!但是对于基本上做同样事情的程序,我没有这个问题,而且我完全不知道区别在哪里。你能看看我的其他代码吗?我编辑了主要帖子,这对我来说意义重大。
【解决方案2】:

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

SmarterFib 传递一个整数(一个单词)和一个数组(一个单词,通过引用;总内存使用量为 N (goalnum) 个单词加上几个单词开销)。堆栈中总共有 2N 个单词。

SmartestFib 为每次调用传递四个整数,或堆栈中的 4N 个字。

我通常不会期望它们具有截然不同的运行时堆栈要求:如果 SmartestFib 在 N=130 时破坏堆栈,我预计 SmarterFib 会破坏它早在 N=260 之前(有一些固定的调用开销)。

【讨论】:

    猜你喜欢
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    • 2021-03-22
    • 1970-01-01
    • 2014-02-03
    • 2015-08-22
    • 1970-01-01
    相关资源
    最近更新 更多