使用memoization计算n = ~11000 + fibonacci递归方法时的堆栈溢出

时间:2017-09-08 19:11:01

标签: java recursion stack-overflow fibonacci memoization

我有一个程序用递归方法计算第n个Fibonacci数,并使用memoization。

我起身n = 11000左右,我得到了一个stackoverflow异常。有人可以帮我解决这个问题吗?

这是我的代码:

public class BigInt {

static BigInteger[] fval;

public static void main(String[] args) {
    int index;
    Scanner input = new Scanner(System.in);
    index = input.nextInt();
    fval = new BigInteger[index + 1];

    System.out.println(fib_rec(index));
}

public static BigInteger fib_rec(int index) {
    BigInteger result = BigInteger.ONE;
    if (index <= 2) {
        return result;
    } else {
        if (fval[index] != null) {
            result = fval[index];
        } else {
            result = fib_rec(index - 1).add(fib_rec(index - 2));
            fval[index] = result;
        }
        return result;
    }
}

4 个答案:

答案 0 :(得分:2)

您的记忆不会改变递归的深度...在致电fib_rec(n)时,会调用fib_rec(n-1)来调用fib_rec(n-2)等。如果您改变了呼叫的顺序(所以你做fib_rec(index - 2).add(fib_rec(index - 1))应该允许你将堆栈深度大致减半,因为你要按两个方向向下工作,然后从底部向上填充间隙,堆栈深度为一个归功于你的记忆。

然而,如果没有更严格的算法重写,就无法避免堆栈深度问题。

答案 1 :(得分:1)

不可避免

对于足够大的输入值,StackOverflowError本质上是不可修复的。对此的争论是双重的。首先,Java does not have tail call optimization。其次,对于每次递归调用,Java必须分配一些内存,例如:用于参数。即使您的方法没有参数,Java也需要一点内存来存储在方法调用结束后跳转到的地址。因此,无论多大,你最终都会耗尽你的记忆。您可以通过启动具有更多堆栈内存的JVM来延长不可避免的时间。可以找到选项(以及其他一些选项)here

我们可以做得更好吗?

是的,我们可以。但不是使用递归算法。我们需要在迭代算法中转换这个递归算法。事实上,each recursion can be transformed in an iteration ans vice-versa。仅此一项仍然不够,因为您的算法具有线性内存复杂性。我们实际上只需要两个值来计算下一个斐波纳契数。这导致了以下方法(伪代码):

int fibonacci(int nth)
    if nth is smaller than 0
        then halt and catch fire

    if nth is smaller than 2
        then return nth

    int last <- 1
    int secondToLast <- 0;

    for (int ith <- 2; ith less or equal to nth, increment ith)
        int tmp <- last + secondToLast
        secondToLast <- last
        last <- tmp
    end for

    return last;

上述算法具有线性时间复杂度(假设可以在恒定时间内完成添加)和恒定的内存复杂度,从而解决了您的问题。

答案 2 :(得分:1)

使用memoization时,

避免递归。这是一个例子:

public class BigInt {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int index = input.nextInt();

        System.out.println(fib_rec(index));
    }

    public static BigInteger fib_rec(int index) {
        if (index <= 2) {
            return BigInteger.ONE;
        }

        BigInteger[] fval = new BigInteger[index + 1];
        fval[0] = BigInteger.ONE;
        fval[1] = BigInteger.ONE;

        for (int i = 2; i <= n; i++) {
            fval[i] = fval[i-1].add(fval[i-2]);
        } 

        return fval[n];
   }
}

}

答案 3 :(得分:0)

您获得stackoverflow的原因是因为内存不足。增加可用内存,更具体地说是堆栈大小。只需添加 -Xss1024mb 或您喜欢的任何尺寸。

处理这种情况的最好方法是实际上有一个更好的算法,这样你就不需要消耗大量的内存。