Problem 14 (その3:答えはでた)
なぜスタックがあふれるのか.答えは自明で何でも溜め込むからなんですよね.1000000までの各整数に対してコラッツ列の長さを求めて,最大のものを求めると1000000個の要素のリストを扱わないといけないのは当たり前で,当然あふれても仕方がないです.
キャッシュにしても同様.溜め込む量が多くなるとヒットする確率もあがりますが,無駄な記録も増えるし探す手間もかかります.ヒットしないものを溜め込まないように細工するにも,その細工そのものが手間になったら意味がないです.
ということで,まずはキャッシュせず,しかも各整数ごとにそれ以前の「コラッツ列の最大長」と比較することで,最大長を計算します.
{- Project Euler Problem 14 e14-7.hs -} type Result = (Int, [Integer]) cfold::(Integer -> Result -> Result) -> Result -> [Integer] -> Result cfold _ re [] = re cfold f (l,rs) (n:ns) = seq x $ cfold f x ns where x = f n (l,rs) -- コラッツ列を求める collatz::Integer -> [Integer] collatz n | n == 1 = [1] | otherwise = n:((collatz.next) n) where next:: Integer->Integer next n | n `mod` 2 == 0 = n `div` 2 | otherwise = 3*n+1 {- 整数nとそれまでの結果(l,rs)を与えて (1) n に対するコラッツ列を求める (2) (1)の列の長さl(n)を求める (3) lとl(n)を比較して (3a) l=l(n)ならば(l,n:rs)を返す (3b) l<l(n)ならば(l(n),[n])を返す (3c) l>l(n)ならば(l,rs)を返す -} process::Integer -> Result -> Result process n (l,rs) | l == ln = (l,n:rs) | l < ln = (ln,[n]) | l > ln = (l,rs) where ln = length $ collatz n answer::Integer -> Result answer n = cfold process (1,[1]) [1..n]
1000000まで計算するのに約20分,837799が長さ525でこれが最大です.