プロジェクト オイラーの 15 番目の問題、格子パス ( http://projecteuler.net/problem=15 )を解決しようとしています。
私の最初の試みは、問題を 1 行ずつ解決し、最後の行の最後の要素を取得することでした。
number_of_ways_new_line last_line = foldl calculate_values [] last_line
where
calculate_values lst x = lst ++ [(if length lst > 0 then (last lst) + x else head last_line)]
count_ways x = foldl (\ lst _ -> number_of_ways_new_line lst) (take x [1,1..]) [1..x-1]
result_15 = last $ count_ways 21
これは機能し、高速ですが、本当に醜いと思います。そこで、しばらく考えて、再帰を使用して問題を解決する、より慣用的な関数を思いつきました (これが間違っている場合は修正してください)。
lattice :: Int -> Int -> Int
lattice 0 0 = 1
lattice x 0 = lattice (x-1) 0
lattice 0 y = lattice (y-1) 0
lattice x y
| x >= y = (lattice (x-1) y) + (lattice x (y-1))
| otherwise = (lattice y (x-1)) + (lattice (y-1) x)
これは短い数値には適していますが、まったくスケーリングしません。lattice 1 2
とlattice 2 1
が常に同じになるという事実を利用して、少し最適化しました。なぜこれはとても遅いのですか?Haskell は以前の結果をメモ化していないので、lattice 2 1
呼び出されるたびに古い結果が記憶されますか?