这已经很晚了,但我想我还是会为了未来的读者的利益而发布(我想 OP 早就解决了这个问题)。
TL;DR:
我想我们可能想使用这个Data.Vector
包来解决这个问题(以及类似类型的问题)。
更长的版本:
根据 Haskell 文档,a Map
(from Data.Map
) 具有 O(log N) 访问时间,而 a Vector
(from Data.Vector
) 具有 O(1) 访问时间;我们可以在下面的结果中看到差异:向量实现的运行速度快了约 3 倍。(两者都比具有 O(N) 访问时间的列表好得多。)
下面包括几个基准。测试故意不一个接一个地运行,以防止任何基于缓存的优化。
几点观察:
最大的绝对改进(来自原始帖子中的代码)是由于添加了类型签名;在没有被明确告知数据是 type 的情况下Int
,Haskell 的类型系统推断数据是 type 的Integer
(obv 更大更慢)
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)