我有一个程序用递归方法计算第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;
}
}
答案 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)
避免递归。这是一个例子:
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 或您喜欢的任何尺寸。
处理这种情况的最好方法是实际上有一个更好的算法,这样你就不需要消耗大量的内存。