簡體   English   中英

計算n =〜11000 +帶備忘錄的斐波那契遞歸方法時的堆棧溢出

[英]Stack Overflow when computing n = ~11000 + fibonacci recursive method with memoization

我有一個程序可以使用遞歸方法並使用記憶來計算第n個斐波那契數。

我最多達到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;
    }
}

您的記憶不會改變遞歸的深度...在調用fib_rec(n) ,它調用fib_rec(n-1) ,在調用fib_rec(n-2)等時。如果反轉調用順序(這樣您可以執行fib_rec(index - 2).add(fib_rec(index - 1)) ,這樣可以將堆棧深度大致減少一半,因為您需要將根部的距離減少兩倍,然后填補空白從頭到尾的堆棧深度為一,這要歸功於您的備忘錄。

但是,如果不對算法進行更戲劇性的重寫,就無法避免堆棧深度問題。

必然

對於足夠大的輸入值, StackOverflowError本質上是不可修復的。 這個觀點有兩個方面。 首先, Java沒有尾調用優化 其次,對於每個遞歸調用,Java必須分配一些內存,例如用於參數。 即使您的方法沒有參數,Java也需要一點內存來存儲要在方法調用結束后跳轉到的地址。 因此,無論大小,最終您都將耗盡內存。 您可以通過使用更多堆棧內存啟動JVM來延長不可避免的時間。 該選項(以及其他選項)可以在此處找到。

我們可以做得更好嗎?

我們可以。 但不能使用遞歸算法。 我們需要將此遞歸算法轉換為迭代算法。 實際上, 每次遞歸都可以迭代進行轉換,反之亦然 僅憑這還不夠,因為您的算法具有線性內存復雜性。 實際上,我們僅需要兩個值即可計算下一個斐波那契數。 這導致以下方法(偽代碼):

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;

上面的算法具有線性時間復雜度(假設可以在恆定時間內完成加法)和恆定內存復雜度,從而解決了您的問題。

使用備忘錄時避免遞歸 這是一個例子:

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];
   }
}

}

出現stackoverflow的原因是內存不足。 增加可用內存,尤其是增加堆棧大小。 只需添加-Xss1024mb或您喜歡的任何大小即可。

處理這種情況的最好方法是實際上有一個更好的算法,這樣您就不需要消耗大量內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM