簡體   English   中英

Haskell - 遞歸堆棧溢出

[英]Haskell - Recursion Stack Overflow

我試圖將所有n從1加到一個非常大的數字(現在是10 ** 9),但它會給出堆棧溢出。 另外我不認為在1處停止並且在不同行中執行總和n是最有效的方式,但下面的代碼是我對Haskell的全部知識。 我真的不太了解函數式編程,我想盡可能多的解釋。 (我也試過把$!strict放在最后一行,這在其他地方被告知但是沒有改變。如果你解釋了我能做這種遞歸函數最有效的方法,我會很高興的。)

main :: IO()

summ 1 = 1
summ n = 1/(n**2) + summ (n-1)

expected_value = pi*pi/6
errorPercent n = n / expected_value * 100

main = do
    putStr "%"
    print (errorPercent (summ $! (10**9)))

chi回答了一個問題,我認為這是主要的問題,但還有其他一些問題讓我煩惱。 當你說10**9 ,你得到一個浮點數 (因為**是“分數”取冪)。 然后,您使用浮點相等來檢查遞歸的基本情況。

summ 1 = ...

這樣做的問題在於它是可能的,並且隨着參數變大,可能會因為數值誤差而幾乎不會錯過基本情況並永遠下降到負值。

summ 4 =        ... summ 3
summ 3 =        ... summ 2.000001
summ 2.000001 = ... summ 1.000001 
summ 1.000001 = ... summ 0.000001  -- BASE CASE MISSED!
summ 0.000001 = ... summ (-1.000001)
summ (-1.000001) = ... summ (-2.000001)

等等。 如果你沒有從10 9個電話中獲得堆棧溢出,你肯定會無限多。

您應該在整數上定義函數,這樣就沒有舍入錯誤

summ :: Int -> Double
summ 1 = 1
summ n = 1 / (fromIntegral n ** 2) + summ (n - 1)
--            ^^^^^^^^^^^^
-- conversion necessary to go from Int to Double

main = ... print (summ (10 ^ 9))
--                      ^^^^^^
--      use integral exponentiation (^) instead of (**)

或使用更寬容的基礎案例

summ :: Double -> Double
summ n | n <= 1 = 1
summ n = 1 / (n ** 2) + summ (n - 1)

在任何一種情況下,你絕對應該采取chi的建議,以累加器的方式做到這一點,你也應該明確地設置類型簽名。

如果你好奇的話, 更多關於你如何在Haskell中獲得堆棧溢出的問題

這里的問題是,在整個10 ^ 9遞歸調用結束之前,總和無法開始計算。 從本質上講,你是在計算

1/(n**2) + ( 1/((n-1)**2) + ( 1/((n-2)**2) + ....

並且括號阻止開始求和。 相反,我們希望擁有

(( 1/(n**2) + 1/((n-1)**2) ) + 1/((n-2)**2) ) + ....

最簡單的方法是使用“累加器”附加參數:

summ 1 acc = 1 + acc
summ n acc = summ (n-1) $! acc + 1/(n**2)

main = do
    putStr "%"
    print (errorPercent (summ (10^9) 0))  -- set acc to 0 at the beginning

為了提高性能,我建議在summ添加一個類型簽名,例如summ :: Int -> Double -> Double


完整的程序如下。 這里運行12秒( ghc -O2 )。

summ :: Int -> Double -> Double
summ 1 acc = 1 + acc
summ n acc = summ (n-1) $! acc + 1 / (fromIntegral n**2)

main :: IO ()
main = do
    putStr "%"
    print (summ (10^9) 0)  -- set acc to 0 at the beginning

暫無
暫無

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

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