メモ化
メモ化について確認している中で、以前に解いた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倍の性能で、これは納得がいきます。ところがコンパイルすると、メモ化版はあまり速くならずオリジナルのコードが劇的に改善するため、性能が逆転するのです。
何か見逃している最適化が働いているでしょうか?謎です。