Life Goes On

まあまあだけど楽しんでる方です

メモ化

メモ化について確認している中で、以前に解いた23問目のコードについて、メモ化したものと性能を比較してみたところ、気になることがありました。
オリジナルはこれ。

main = print $ euler023 5000

euler023 :: Int -> Int
euler023 limit = sum $ filter (not . isSumOfAbundants) [1..limit]

isSumOfAbundants :: Int -> Bool
isSumOfAbundants n = any test [1..(div n 2)]
    where test a = isAbundant a && isAbundant (n - a)

isAbundant :: Int -> Bool
isAbundant n = n < (sum $ divisors n)

divisors :: Int -> [Int]
divisors n = [1] ++ halfDivisors ++ (filter (/= root) $ map (div n) halfDivisors)
    where halfDivisors = filter ((== 0) . mod n) [2..root]
          root = truncate $ sqrt $ fromIntegral n

メモ化版はこれ。関数isAbundantをリストabundantsに置き換えただけです。

main = print $ euler023 5000

euler023 :: Int -> Int
euler023 limit = sum $ filter (not . isSumOfAbundants) [1..limit]

isSumOfAbundants :: Int -> Bool
isSumOfAbundants n = any test [1..(div n 2)]
    where test a = abundants !!  a && abundants !! (n - a)

abundants :: [Bool]
abundants = [ n < (sum $ divisors n) | n <- [0..] ]

divisors :: Int -> [Int]
divisors n = [1] ++ halfDivisors ++ (filter (/= root) $ map (div n) halfDivisors)
    where halfDivisors = filter ((== 0) . mod n) [2..root]
          root = truncate $ sqrt $ fromIntegral n

コンパイルせずにrunghcで実行したときは、メモ化版がオリジナルの2〜3倍の性能で、これは納得がいきます。ところがコンパイルすると、メモ化版はあまり速くならずオリジナルのコードが劇的に改善するため、性能が逆転するのです。

何か見逃している最適化が働いているでしょうか?謎です。