Euler 4 を Haskell で高速化してみたが、、、
下記のような記事があって、
HaskellとOCamlの速度を比較してみた - let start programming = fun life -> life * 100;;
キャミバ様とガリグ先生がOCamlでコードを戦わせていたようだったので、こりゃ勝てないと思ってHaskellで高速化をしてみた。
結果、ぼくのコードは10倍に膨れ上がってしまった。
改善の趣旨としては、この問題は最大値を求める問題なので、探索空間をおおよそ大きい方からのリストにして、最初に見つかった解に対して、明らかに小さくなってしまう部分を切り捨てる。という方針。
is_circle n = let is_circle_sub m n r = if m == 0 then r == n else is_circle_sub (m `div` 10) n (r * 10 + m `mod` 10) in is_circle_sub n n 0 declist a b limit tail = if b <= a then (a,b):(declist (a-1) (b+1) limit tail ) else tail -- decendant list of pair (a,b) on value a + b declist_rec a b limit = declist a b limit $ if(a + b > 2) then if (a+b-1) > limit then declist_rec limit (a + b -1 -limit) limit else declist_rec (a+b-2) 1 limit else [] mult_list = map (\ (x,y) -> x * y) $ declist_rec 999 999 999 index_mult_list = zip [1..] mult_list main = let (index,first_sol) = head $ filter (is_circle.snd) index_mult_list root = floor $ sqrt $ fromInteger first_sol search_limit = (\x->x * x) $ (\x -> (999 - x +1)) $ root in do print $ maximum $ filter is_circle $ drop (index-1) $ take search_limit mult_list
これがhideshi_oさんのオリジナルのコード
--Haskell import Data.List main = do print $ last $ filter (\x -> (reverse $ show x) == show x) $ sort ([x * y | x <- [100..999], y <- [100..999]])
さて、実行結果は
オリジナルコード:
[seg@seg-PC ~/haskell-sample]$ time ./Euler0 906609 0.000u 0.046s 0:04.09 0.9% 0+0k 0+0io 1355pf+0w
今回アルゴリズム改善したコード:
[seg@seg-PC ~/haskell-sample]$ time ./Euler 906609 0.000u 0.031s 0:00.04 75.0% 0+0k 0+0io 1356pf+0w
爆速になった。100万要素リストのトラバースをしないのだから当然と言っては当然。実際に探索した空間は2300くらい。500倍の差がつかないのは他に余計な計算をたくさんしているせいか。あ、takeを使わないでリストをたぐるだけの関数書けば速くなりそうな気がしますね。
ただ、元のコードにあった簡潔さはもうみじんもないし、コード行数も実質15倍。トレードオフといえばそれまでだけどなんだかなぁ、という気分にもなる。
元の趣旨であったと思われる「簡潔で見やすいコードの速度を比べてみる」というところからは明らかに外れたコード修正でした。