以下函数计算我将一个数字除以另一个数字的频率:
divs n p = if (n `mod` p == 0) then 1 + divs (n `div` p) p else 0
是否有更短的方式来撰写divs
?
答案 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)