Haskell Memoization Codewars阶乘n的尾随零数

时间:2016-10-17 02:12:01

标签: haskell dynamic-programming factorial

我正在尝试解决名为的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很新。

1 个答案:

答案 0 :(得分:1)

如果您在此处使用traceshow测试了代码性能,那就是问题:与主代码相比,它们非常慢。如果不是,变体的性能必须大致相同。

def函数不适合进行记忆。递归的平均深度与1没有太大差别。其余的复杂性被简化为操作mod,也就是说,除了表查找之外几乎不会更加昂贵的划分(并且可以将常数除法优化为乘法)。