Project Euler 88

http://projecteuler.net/index.php?section=problems&id=88


雑にSetを使って。コンパイルして最適化オプションつけたら非常に速くなった。

import qualified Data.Set as S

primes = 2:3:5:7:[ p + 6 | p <- tail (tail primes) ]

calc_exp n p | mod n p /= 0 = (n, 0)
             | otherwise    = (m, e + 1)
                        where (m, e) = calc_exp (div n p) p
factorize :: Int -> [Int] -> [(Int,Int)]
factorize 1 ps = []
factorize n (p:ps) | e > 0     = (p,e):(factorize m ps)
                   | p * p > n = [(n,1)]
                   | otherwise = factorize m ps
                   where (m, e) = calc_exp n p
fact n = factorize n primes
val f = foldr (\(p,e) y -> p^e * y) 1 f

divisors [] = [([], [])]
divisors ((p,e):fs) = [ (neg ((p,e1):a),neg ((p,e-e1):b)) |
                                e1 <- [0..e], (a,b) <- divisors fs ]
neg [] = []
neg ((p,e):a) = if e == 0 then neg a else (p,e):(neg a)
divs :: Int -> [[Int]]
divs n = divs2 f 2 where
        f = fact n
        divs2 f l | val f < l = []
        divs2 f l | val f < l^2 = [[val f]]
        divs2 f l | otherwise = [val f]:(concat (map (\(f1,f2) ->
                map (\ns -> (val f1):ns) (divs2 f2 (val f1))) a)) where
                a = filter (range l) (divisors f)
                range l (f1,f2) = l <= val f1 && val f1 <= val f2

product_sum_length a = (product a) - (sum a) + (length a)

update ((n,k):xs) sn sk
        | S.null sk     = sum (S.toList sn)
        | S.member k sk = update xs (S.insert n sn) (S.delete k sk)
        | otherwise     = update xs sn sk

limit = 12000
a :: [(Int,Int)]
a = [ (n, product_sum_length a) | n <- [2..], a <- divs n, length a > 1 ]
main = print (update a S.empty (S.fromList [2..limit]))