算一下我分裂的频率

时间:2011-08-30 09:22:39

标签: haskell recursion integer division

以下函数计算我将一个数字除以另一个数字的频率:

divs n p = if (n `mod` p == 0) then 1 + divs (n `div` p) p else 0

是否有更短的方式来撰写divs

5 个答案:

答案 0 :(得分:5)

我的变体是:

factors :: Integral i => i -> i -> Int
factors f =
    length . takeWhile (== 0) .
    map (`mod` f) . iterate (`div` f)

迭代/地图模式对许多问题非常有用。

答案 1 :(得分:3)

divs n p = case n `divMod` p of (q,0) -> 1 + divs q p; _ -> 0

也更高效!请注意quotRem,其行为与负值不同,仍然是一个非常小的效率。

答案 2 :(得分:2)

好的,这里真正的问题是:您正在列表[0..]中搜索m除以p^m的最后一个值n。目前,您正在按顺序进行搜索,线性地运行列表。有没有更好的搜索?

我不确定,但也许你可以通过在开始时更快地向前跳,直到你在m上有上限和下限来做得更好。例如,一种方法是猜测上限(例如,1),然后重复加倍,直到它确实是一个上限;然后算法看起来很像重复的平方:

upBound p n = if n `mod` p == 0 then 2 * upBound (p^2) n else 1
divs p n = m + divs' p (n `div` (p ^ m)) where
    m = upBound p n `div` 2
    divs' p n = {- your algorithm -}

一旦问题以这种形式构建,您可能会想到其他一些搜索策略,但是这个问题很简单,如果期望m的大值是常见的,那么它应该快两倍。

编辑:哎呀,看起来我正在回答“有没有办法写得更快”而不是“有没有办法把它写得更短”

答案 3 :(得分:1)

import Control.Arrow

divs n d = pred $ length $ takeWhile ((== 0).snd) $ iterate (((`div` d) &&& (`mod` d)).fst) (n,0)

Rube Goldberg会为我感到骄傲......

[编辑]

这看起来更好:

divs n d = length $ takeWhile ((==0).(`mod` d)) $ scanl div n $ repeat d

答案 4 :(得分:0)

您正在做的是计算logarithm。也许您可以使用Haskell的log函数来执行此操作。

例如:

log(16) / log(2) == 4