我正在尝试解决名为的Codewars问题:N的尾随零数!与Haskell。 我知道我不需要计算阶乘来知道尾随的零,事实上我只是计算有多少个数可以被5整除,有多少次被分割。 我写了2个版本,一个在编写数字时使用memoization以获得可被5整除的次数以及另一个不使用memoization的版本。 令我感到惊讶的是,假定的DP方法比简单的递归方法花费更长的时间。我可能在我的代码中做了一些非常愚蠢的事情。
这些是功能:
zeros x = helperZeros [1..x]
helperZeros :: [Integer] -> Integer
helperZeros = sumArrayTuple . filter (\x -> x `mod` 5 == 0)
sumArrayTuple = foldl (\acc x -> acc + (fastDef x)) 0
data Tree a = Tree (Tree a) a (Tree a)
instance Functor Tree where
fmap f (Tree l m r) = Tree (fmap f l) (f m) (fmap f r)
index :: Tree Integer -> Integer -> Integer
index (Tree _ m _) 0 = m
index (Tree l _ r) n = case (n-1) `divMod` 2 of
(q,0) -> index l q
(q,1) -> index r q
nats = go 0 1
where
go n s = Tree (go l s') n (go r s' )
where
l = n + s
r = l + s
s' = s * 2
fastDef:: Integer -> Integer
fastDef x = trace (show x) index memTreetDef x
memTreetDef = fmap (defact fastDef) nats
defact f n
| n `mod` 5 /= 0 = 0
| otherwise = 1 + f (n `div` 5)
zeros' x = helperZeros' [1..x]
helperZeros' :: [Integer] -> Integer
helperZeros' = sumArrayTuple' . filter (\x -> x `mod` 5 == 0)
sumArrayTuple' = foldl (\acc x -> acc + (def x)) 0
def n
| n `mod` 5 /= 0 = 0
| otherwise = 1 + def (n `div` 5)
我想要记住的是defact函数的结果,例如,如果我已经计算了defact 200,那么它将重用此结果来计算defact 1000。
我在Haskell中对DP很新。
答案 0 :(得分:1)
如果您在此处使用trace
和show
测试了代码性能,那就是问题:与主代码相比,它们非常慢。如果不是,变体的性能必须大致相同。
def
函数不适合进行记忆。递归的平均深度与1
没有太大差别。其余的复杂性被简化为操作mod
,也就是说,除了表查找之外几乎不会更加昂贵的划分(并且可以将常数除法优化为乘法)。