xs !! n
的时间复杂度是线性的。你应该尝试使用对数或常数访问的数据结构。
编辑:这里是我通过复制augustss的类似实现而想出来的一个快速实现:
psOpt x = psArr x
where psCall 0 = 1
psCall n = sum $ map getP $ takeWhile ((<= n).fst) pents where
getP (pent,sign) = sign * (psArr (n-pent))
psArr n = if n > ncache then psCall n else psCache ! n
psCache = listArray (0,ncache) $ map psCall [0..ncache]
在ghci中,我观察到与您的列表版本相比没有惊人的加速。运气不好!编辑:确实,按照Chris Kuklewicz的建议使用-O2,此解决方案比您的n=5000 for循环快8倍。结合Hammar的洞见,对10^6取模,我得到了一个足够快的解决方案(在我的机器上大约在10秒内找到了希望正确的答案)。
import Data.List (find)
import Data.Array
ps = 1 : map p [1..] where
p n = sum $ map getP $ takeWhile ((<= n).fst) pents where
getP (pent,sign) = sign * (ps !! (n-pent))
summod li = foldl (\a b -> (a + b) `mod` 10^6) 0 li
ps' = 1 : map p [1..] where
p n = summod $ map getP $ takeWhile ((<= n).fst) pents where
getP (pent,sign) = sign * (ps !! (n-pent))
ncache = 1000000
psCall 0 = 1
psCall n = summod $ map getP $ takeWhile ((<= n).fst) pents
where getP (pent,sign) = sign * (psArr (n-pent))
psArr n = if n > ncache then psCall n else psCache ! n
psCache = listArray (0,ncache) $ map psCall [0..ncache]
pents = zip (map (\n -> ((3*n-1)*n `div` 2) `mod` 10^6) $ [1..] >>= (\x -> [x,-x]))
(cycle [1,1,-1,-1])
我打破了psCache的抽象,所以你应该使用psArr而不是psOpt;这可以确保对psArr的不同调用将重复使用相同的记忆化数组。当你写find ((== 0) . ...)时,这是很有用的...嗯,我认为最好不要公开完整的解决方案。
感谢大家提供额外的建议。