Life Goes On

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

78問目

http://projecteuler.net/index.php?section=problems&id=78
n 個のコインをいくつかの山に分けるときの場合の数を p(n) とおく。例えば 5 個のコインは 7 通りの方法で分けられるので p(5)=7 である。p(n) が百万の倍数となるような n のうち最も小さい値を求める。
真正面から解こうとすると何年経っても終わらないので、76問目のフォーラムを見て、Euler の Partition Function分割関数)のお世話になりました。
これだけ対象の数字が大きくなると List では力不足なので、Array に初挑戦。
List だと 30 分、Array だと 10 秒といったところです。

import Data.Array

main = print $ euler078 1000000

euler078 :: Integer -> Int
euler078 m = head $ filter (\ n -> (mod (parts ! n) m) == 0) [1..]

parts :: Array Int Integer
parts = listArray (0, 1000000) (1 : map part [1..1000000])

part :: Int -> Integer
part n = sum [ s * (parts ! (n - p)) | (p, s) <- takeWhile ((n >=) . fst) penta ]

penta :: [(Int, Integer)]
penta = zip (concat $ zipWith (\ a b -> [a, b]) (scanl1 (+) [1,4..]) (scanl1 (+) [2,5..])) (cycle [1,1,-1,-1])