嗨haskell研究員。我目前正在致力於Project Euler的23rd problem。我在atm的地方是我的代碼似乎對我來說不合適 - 不是「好算法」的意思,而是「應該工作」的含義 - 但會產生堆棧內存溢出。項目歐拉23:洞悉這個stackoverflow-ing計劃需要
我知道我的算法並不完美(特別是我可以避免在我的worker
函數的每個遞歸步驟中計算如此大的中間結果)。
雖然,在學習Haskell的過程中,我想了解爲什麼這段代碼失敗得如此悲慘,爲的是避免下次出現這種錯誤。
任何有關爲什麼這個計劃是錯誤的見解,將不勝感激。
import qualified Data.List as Set ((\\))
main = print $ sum $ worker abundants [1..28123]
-- Limited list of abundant numbers
abundants :: [Int]
abundants = filter (\x -> (sum (divisors x)) - x > x) [1..28123]
-- Given a positive number, returns its divisors unordered.
divisors :: Int -> [Int]
divisors x | x > 0 = [1..squareRoot x] >>=
(\y -> if mod x y == 0
then let d = div x y in
if y == d
then [y]
else [y, d]
else [])
| otherwise = []
worker :: [Int] -> [Int] -> [Int]
worker (a:[]) prev = prev Set.\\ [a + a]
worker (a:as) prev = worker as (prev Set.\\ (map ((+) a) (a:as)))
-- http://www.haskell.org/haskellwiki/Generic_number_type#squareRoot
(^!) :: Num a => a -> Int -> a
(^!) x n = x^n
squareRoot :: Int -> Int
squareRoot 0 = 0
squareRoot 1 = 1
squareRoot n =
let twopows = iterate (^!2) 2
(lowerRoot, lowerN) =
last $ takeWhile ((n>=) . snd) $ zip (1:twopows) twopows
newtonStep x = div (x + div n x) 2
iters = iterate newtonStep (squareRoot (div n lowerN) * lowerRoot)
isRoot r = r^!2 <= n && n < (r+1)^!2
in head $ dropWhile (not . isRoot) iters
編輯:確切的錯誤是Stack space overflow: current size 8388608 bytes.
。通過+RTS -K...
增加堆棧內存限制不能解決問題。
編輯2:關於sqrt的事情,我只是從評論的鏈接粘貼它。爲了避免必須將Integer投出雙打併面對舍入問題等......
你能發佈確切的錯誤嗎?我從來沒有在Haskell中看到實際的堆棧溢出。空間泄漏?另外,你對牛頓方法的實現對我來說真的很困惑。你爲什麼不使用包含的sqrt函數? – 2012-04-01 18:22:28
@Josh:我編輯我的OP來回答你 – m09 2012-04-01 18:28:45
好吧,這是一個空間泄漏。當這種情況發生時,你運行什麼功能?通常這意味着你要麼需要記憶該函數,要麼將其重寫爲尾遞歸。 – 2012-04-01 18:30:43