我一直在做项目欧拉问题来学习Haskell。
我在途中遇到了一些障碍,但设法解决了问题14。 问题是,1 000 000以下的起始数量是最长的Collatz链(链条开始后数量可以超过100万)。
我尝试了几种解决方案,但没有一种解决方案。 我想反过来。从1开始,当数字超过一百万时终止,但由于条款可能高于一百万,这显然不起作用。
我已经尝试过记忆正常的算法,但是再次,数字太多,以及更多的记忆。
我已经读到最明显的解决方案应该适用于此,但出于某种原因,我的解决方案需要10秒以上才能达到最大值20 000.更别说100万了。
这是我目前正在使用的代码:
reg_collatz 1 = 1
reg_collatz n
| even n = 1 + reg_collatz (n `div` 2)
| otherwise = 1 + reg_collatz (n * 3 + 1)
solution = foldl1 (\a n -> max a (reg_collatz n)) [1..20000]
非常欢迎任何帮助。
答案 0 :(得分:6)
答案很简单:不要记忆超过一百万的数字,但要用下面的数字来做。
module Main where
import qualified Data.Map as M
import Data.List
import Data.Ord
main = print $ fst $ maximumBy (comparing snd) $ M.toList ansMap
ansMap :: M.Map Integer Int
ansMap = M.fromAscList [(i, collatz i) | i <- [1..1000000]]
where collatz 1 = 0
collatz x = if x' <= 1000000 then 1 + ansMap M.! x'
else 1 + collatz x'
where x' = if even x then x `div` 2 else x*3 + 1
答案 1 :(得分:3)
这已经很晚了,但我想我会发布以备将来读者的利益(我想OP已经很久没有完成这个问题了)。
<强> TL; DR:强>
我想我们可能想要使用Data.Vector
包来解决这个问题(以及类似的问题)。
更长的版本:
根据Haskell文档,Map
(来自Data.Map
)具有O(log N)访问时间,而Vector
(来自Data.Vector
)具有O(1访问;我们可以看到下面结果的差异:矢量实现运行速度快〜3倍。 (两者都比具有O(N)访问时间的列表更好。)
下面列出了几个基准。故意不会一个接一个地运行测试,以防止任何基于缓存的优化。
有几点意见:
最大的绝对改进(来自原帖中的代码)是由于添加了类型签名;在没有明确告知数据属于Int
类型的情况下,Haskell的类型系统推断数据类型为Integer
(obv更大更慢)
有点违反直觉,但结果在foldl1'
和foldl1
之间几乎无法区分。 (我仔细检查了代码并运行了几次以确保。)
Vector
和Array
(并且,在某种程度上,Map
)主要通过记忆来实现体面的改进。 (请注意,OP的解决方案可能比基于列表的解决方案快得多,该解决方案在给定列表的O(N)访问时间的情况下尝试使用memoization。)
以下是几个基准测试(全部使用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)