Hallo haskell Stipendiaten. Ich arbeite gerade an der 23rd problem von Project Euler. Wo ich bei atm bin ist, dass mein Code richtig zu mir scheint - nicht in der "guten Algorithmus" Bedeutung, sondern in der "sollte funktionieren" Bedeutung - aber erzeugt einen Stack Speicherüberlauf.Projekt Euler 23: Einblick in dieses Stack-Overflow-Programm benötigt
Ich weiß, dass mein Algorithmus nicht perfekt ist (insbesondere könnte ich sicherlich die Berechnung eines so großen Zwischenergebnisses bei jedem Rekursionsschritt in meiner worker
Funktion vermeiden).
Obwohl ich gerade dabei bin, Haskell zu lernen, würde ich gerne verstehen, warum dieser Code so kläglich versagt, um diese Art von Fehlern beim nächsten Mal zu vermeiden.
Jeder Einblick, warum dieses Programm falsch ist, wird geschätzt.
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
Edit: der genaue Fehler ist Stack space overflow: current size 8388608 bytes.
. Erhöhen des Stack-Speicherlimits durch +RTS -K...
löst das Problem nicht.
Edit2: über die sqrt-Sache, ich kopiere es einfach eingefügt aus dem Link in den Kommentaren. Um zu vermeiden, dass Integer auf das Doppelte geworfen werden muss und die Rundungsprobleme auftreten müssen ...
Können Sie den genauen Fehler posten? Ich habe noch nie einen tatsächlichen Stack-Überlauf in Haskell gesehen. Platzleck? Außerdem ist meine Implementierung von Newtons Methode wirklich verwirrend für mich. Warum benutzen Sie nicht die mitgelieferte sqrt-Funktion? –
@Josh: Ich habe mein OP bearbeitet, um Ihnen zu antworten – m09
Ok, das ist ein Weltraumleck. Welche Funktion führen Sie, wenn das passiert? Normalerweise bedeutet dies, dass Sie die Funktion entweder memoisieren oder sie als tail-rekursiv schreiben müssen. –