Ciao haskell utenti. Attualmente sto lavorando allo 23rd problem di Project Euler. Dove sono atm è che il mio codice mi sembra giusto - non nel significato di "buon algoritmo", ma nel significato "dovrebbe funzionare" - ma produce un sovraccarico di memoria dello stack.Progetto Eulero 23: approfondimento su questo programma stackoverflow necessario
So che il mio algoritmo non è perfetto (in particolare potrei sicuramente evitare di calcolare un risultato intermedio così grande ad ogni passo di ricorsione nella mia funzione worker
).
Tuttavia, essendo in procinto di apprendere Haskell, mi piacerebbe capire perché questo codice fallisce così miseramente, al fine di evitare questo tipo di errori la prossima volta.
Qualsiasi giudizio sul motivo per cui questo programma è sbagliato sarà apprezzato.
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
Modifica: l'errore esatto è Stack space overflow: current size 8388608 bytes.
. L'aumento del limite di memoria dello stack tramite +RTS -K...
non risolve il problema.
Edit2: sulla cosa sqrt, ho appena copiato incollato dal collegamento nei commenti. Per evitare di dover lanciare Integer a Doubles e affrontare i problemi di arrotondamento ecc ...
Puoi pubblicare l'errore esatto? Non ho mai visto un vero e proprio overflow di stack in Haskell. Perdita di spazio? Inoltre, la tua implementazione del metodo di Newton mi confonde davvero. Perché non stai usando la funzione sqrt inclusa? –
@Josh: ho modificato il mio OP per risponderti – m09
Ok, questa è una perdita di spazio. Quale funzione esegui quando ciò accade? Solitamente ciò significa che è necessario memorizzare la funzione o riscriverla in modo ricorsivo. –