制作一个除数列表,而不是在Haskell中顺序划分

时间:2016-02-07 14:57:05

标签: haskell math factors

我在哈斯克尔学习。

我一直在实施一个列出除数列表的函数 我的第一个代码在这里:

代码:

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]

3 个答案:

答案 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]

  1. 制定素因子的幂集,[[],[2],[3],[5],[2,3],[2,5],[3,5],[2,3,5]]
  2. 为每个元素生成并获得结果[1,2,3,5,6,10,15,30],这是[{1}}
  3. 的除数列表

    <强>代码:

    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 模块中的powersetnub相同。
    它变得简单,但behzad.nouri's code要快得多。

答案 2 :(得分:2)

强制性的monadic代码:

import Control.Monad

divisors = map product . mapM (scanl (*) 1) . group . factors

factors和您的其他关联问题一样。

您已使用List monad中的sequence,无需明确调用concatMapmapM f相当于sequence . map f)。

该列表不会被订购,就像您原来按顺序划分的 O(n)代码生成的那样 - 您可以将其设为 O(sqrt(n)) 顺便说一下,有一个简单的技巧,但平均来说它仍然比这段代码慢得多。