これは明らかに遅いですが、将来の読者の利益のためにとにかく投稿すると思いました(OPはこの問題で長い間行われていると思います)。
TL; DR:Data.Vector
おそらくこの問題(および同様のタイプの問題)にパッケージ
を使用したいと思います。
長いバージョン:
Haskellのドキュメントによると、Map
(from Data.Map
)にはO(log N)アクセス時間がありますが、Vector
(from Data.Vector
)にはO(1)アクセスがあります。以下の結果に違いがあります。ベクトルの実装は約3倍高速に実行されます。(どちらも、アクセス時間がO(N)のリストよりもはるかに優れています。)
いくつかのベンチマークが以下に含まれています。キャッシュベースの最適化を防ぐために、テストは意図的に次々に実行されませんでした。
いくつかの観察:
(元の投稿のコードからの)最大の絶対的な改善は、型署名の追加によるものでした。データが型であると明示的に言われることなくInt
、Haskellの型システムは、データが型であると推測していましたInteger
(これは明らかに大きくて遅いです)
foldl1'
少し直感に反しますが、結果はとの間で事実上区別できませんfoldl1
。(コードを再確認し、念のためにこれらを数回実行しました。)
Vector
そしてArray
(そして、ある程度までMap
)、主にメモ化の結果として、まともな改善を可能にします。(OPのソリューションは、リストのO(N)アクセス時間を指定してメモ化を使用しようとしたリストベースのソリューションよりもはるかに高速である可能性が高いことに注意してください。)
ここにいくつかのベンチマークがあります(すべてはを使用してコンパイルされていますO2
):
Probably want to look
at these numbers
|
V
Data.Vector 0.35s user 0.10s system 97% cpu 0.468 total
Data.Array (Haskell.org) 0.31s user 0.21s system 98% cpu 0.530 total
Data.Map (above answer) 1.31s user 0.46s system 99% cpu 1.772 total
Control.Parallel (Haskell.org) 1.75s user 0.05s system 99% cpu 1.799 total
OP (`Int` type sigs + foldl') 3.60s user 0.06s system 99% cpu 3.674 total
OP (`Int` type sigs) 3.53s user 0.16s system 99% cpu 3.709 total
OP (verbatim) 3.36s user 4.77s system 99% cpu 8.146 total
Haskell.orgの数字の出典:https ://www.haskell.org/haskellwiki/Euler_problems/11_to_20#Problem_14
上記Data.Vector
の結果を生成するために使用された実装:
import Data.Vector ( Vector, fromList, maxIndex, (!) )
main :: IO ()
main = putStrLn $ show $ largestCollatz 1000000
largestCollatz :: Int -> Int
largestCollatz n = maxIndex vector
where
vector :: Vector Int
vector = fromList $ 0 : 1 : [collatz x x 0 | x <- [2..n]]
collatz m i c =
case i < m of
True -> c + vector ! i
False -> let j = if even i then i `div` 2 else 3*i + 1
in collatz m j (c+1)