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 я не наблюдаю впечатляющего ускорения по сравнению с вашей версией списка. Не повезло!
Редактировать:
Действительно, с -O2
, как предложил Крис Куклевич, это решение в восемь раз быстрее, чем ваше для n=5000
. В сочетании с пониманием Хаммаром выполнения сумм по модулю 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) . ...)
.. Я подумал, что лучше не публиковать решение complete .
Спасибо всем за дополнительный совет.