我在哈斯克尔学习。
我一直在实施一个列出除数列表的函数 我的第一个代码在这里:
代码:
divisors :: Integral a => a -> [a]
divisors n
| n < 1 = []
| otherwise = filter ((== 0) . (mod n)) [1..n]
此代码与Making a list of divisors in Haskell几乎相同 它工作但很慢。
我认为除以[1..n]
的每一个都没有效率
是否有另一种聪明的方法可以列出除数列表?
更新
如果是n < 1
,则[1..n]
与[]
相同
因此,根本不需要警卫:
divisors :: Integral a => a -> [a]
divisors n = filter ((== 0) . (mod n)) [1..n]
答案 0 :(得分:4)
使用此代码:
import Data.List (group)
import Control.Arrow ((&&&))
divisors n = foldr go [1] . map (head &&& length) . group $ fac n 2
where
go (_, 0) xs = xs
go (p, k) xs = let ys = map (* p) xs in go (p, pred k) ys ++ xs
fac n i
| n < i * i = if n == 1 then [] else [n]
| n `mod` i == 0 = i: fac (n `div` i) i
| otherwise = fac n $ succ i
你会得到:
\> -- print timing/memory stats after each evaluation
\> :set +s
\> length $ divisors 260620460100
2187
(0.01 secs, 0 bytes)
\> length $ divisors 1000000007 -- prime number
2
(0.08 secs, 13,678,856 bytes)
您可以与您的实施进行比较。
答案 1 :(得分:3)
我自己的实现现在使用素数因子的幂集。
例如,要获取30
的除数列表,其中素因子为[2,3,5]
,
[[],[2],[3],[5],[2,3],[2,5],[3,5],[2,3,5]]
[1,2,3,5,6,10,15,30]
,这是[{1}} <强>代码:强>
30
在此代码中,如果存在重复的素因子,则会出现重复的除数 我在最后一步删除了重复的除数,但它没有解决重复的基本原因 我确信有更聪明的方法。
注意:强>
divisors :: Integral a => a -> [a]
divisors n
| n < 1 = []
| otherwise = distinct $ map product $ (powerset . factors) n
-- | remove duplicated element in a list
distinct :: Eq a => [a] -> [a]
distinct [] = []
distinct (x : xs)
| x `elem` xs = distinct xs
| otherwise = x : distinct xs
-- | generate power set of a list
powerset :: [a] -> [[a]]
powerset [] = [[]]
powerset (x : xs) = xss ++ map (x :) xss where xss = powerset xs
-- | generate prime factors of a integer
factors :: Integral a => a -> [a]
factors m = f m (head primes) (tail primes) where
f m n ns
| m < 2 = []
| m < n ^ 2 = [m]
| m `mod` n == 0 = n : f (m `div` n) n ns
| otherwise = f m (head ns) (tail ns)
-- | prime sequence
primes :: Integral a => [a]
primes = 2 : filter (\n-> head (factors n) == n) [3,5..]
和primes
来自Prime factors in Haskell。factors
来自powerset。<强>更新强>
感谢comingstorm's advice,我研究了更多关于 Data.List 模块并更新我的代码:
powerset
我注意到原始代码中的import Data.List (group, subsequences)
divisors :: Integral a => a -> [a]
divisors = map product . concatMap sequence . subsequences . map (scanr1 (*)) . group . factors
primes = ... -- same as before
factors m = ... -- same as before
和distinct
与 Data.List 模块中的powerset
和nub
相同。
它变得简单,但behzad.nouri's code要快得多。
答案 2 :(得分:2)
强制性的monadic代码:
import Control.Monad
divisors = map product . mapM (scanl (*) 1) . group . factors
factors
和您的其他关联问题一样。
您已使用List monad中的sequence
,无需明确调用concatMap
(mapM f
相当于sequence . map f
)。
该列表不会被订购,就像您原来按顺序划分的 O(n)代码生成的那样 - 您可以将其设为 O(sqrt(n)) 顺便说一下,有一个简单的技巧,但平均来说它仍然比这段代码慢得多。