[英]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);
}
}
因此,總而言之,請始終避免不必要的裝箱/拆箱(從原語轉換為包裝器類型,即從int
為Integer
),這在您運行大量計算時(例如在遞歸中)變得更加重要 。 您可以在此處查找有關拳擊如何導致執行問題的更多信息。
堆棧溢出不是基於例程的空間復雜度。 這意味着您已經使用了調用堆棧上的空間。 每個活動函數調用都占用一些堆棧空間,跟蹤傳入和傳回的值,並經常進行上下文切換空間(寄存器值等)。
SmarterFib傳遞一個整數(一個字)和一個數組(一個字作為參考;總內存使用量為N (目標)字,外加幾個字的開銷)。 堆棧上總共有2N個字。
SmartestFib為每個調用傳遞四個整數,即堆棧上的4N個字。
我通常不希望它們對運行時的堆棧有完全不同的要求:如果SmartestFib在N = 130時將堆棧炸毀,我希望SmarterFib在N = 260之前就將其炸毀(有固定的通話開銷)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.