web-dev-qa-db-ja.com

Haskellの素因数

Haskellは初めてです。

次の整数の素因数を含むリストのリストを生成するにはどうすればよいですか?

現在、私は素数を生成する方法しか知りません:

primes = map head $ iterate (\(x:xs) -> [y | y<-xs, y `mod` x /= 0 ]) [2..]
14
Chris

nの素因数を決定する簡単なアプローチは次のとおりです。

  • _[2..n-1]_で最初の除数dを検索します
  • dが存在する場合:return d : primeFactors(div n d)
  • それ以外の場合はnを返します(nがプライムであるため)

コード:

_prime_factors :: Int -> [Int]

prime_factors 1 = []
prime_factors n
  | factors == []  = [n]
  | otherwise = factors ++ prime_factors (n `div` (head factors))
  where factors = take 1 $ filter (\x -> (n `mod` x) == 0) [2 .. n-1]
_

これは明らかに多くの最適化を使用する可能性があります(2からsqrt(N)までのみ検索し、これまでに見つかった素数をキャッシュし、これらの除算のみを計算するなど)

[〜#〜] update [〜#〜]

ケースを使用してわずかに変更されたバージョン(@ user5402によって提案されたように):

_prime_factors n =
  case factors of
    [] -> [n]
    _  -> factors ++ prime_factors (n `div` (head factors))
  where factors = take 1 $ filter (\x -> (n `mod` x) == 0) [2 .. n-1]
_
17
Frank Schmitt

配当までm <2

  1. 素数から最初の除数nを取ります。
  2. 分割可能である間、mnで除算することを繰り返します。
  3. 素数から次の除数nを取り、2に進みます。

実際に使用されるすべての除数のリストは、元のmの素因数です。

コード:

-- | prime factors
--
-- >>> factors 13
-- [13]
-- >>> factors 16
-- [2,2,2,2]
-- >>> factors 60
-- [2,2,3,5]
--
factors :: Int -> [Int]
factors m = f m (head primes) (tail primes) where
  f m n ns
    | m < 2 = []
    | m `mod` n == 0 = n : f (m `div` n) n ns
    | otherwise = f m (head ns) (tail ns)

-- | primes
--
-- >>> take 10 primes
-- [2,3,5,7,11,13,17,19,23,29]
--
primes :: [Int]
primes = f [2..] where f (p : ns) = p : f [n | n <- ns, n `mod` p /= 0]

更新:

この置換コードは、不要な評価を回避することでパフォーマンスを向上させます。

factors m = f m (head primes) (tail primes) where
  f m n ns
    | m < 2 = []
    | m < n ^ 2 = [m]   -- stop early
    | m `mod` n == 0 = n : f (m `div` n) n ns
    | otherwise = f m (head ns) (tail ns)

primesは、 Will Nessのコメント で述べたように、大幅に高速化することもできます。

primes = 2 : filter (\n-> head (factors n) == n) [3,5..]
5
Hironobu Nagaya

これはパフォーマンスが高く、理解しやすい実装であり、isPrimeprimesは再帰的に定義され、primesはデフォルトでキャッシュされます。 primeFactors定義はprimesの適切な使用法であり、結果には連続して複製された数値が含まれます。この機能により、(map (head &&& length) . group)を介して各因子の数を簡単に数えることができます。 (map head . group)を介して一意にする:

isPrime :: Int -> Bool
primes :: [Int]

isPrime n | n < 2 = False
isPrime n = all (\p -> n `mod` p /= 0) . takeWhile ((<= n) . (^ 2)) $ primes
primes = 2 : filter isPrime [3..]

primeFactors :: Int -> [Int]
primeFactors n = iter n primes where
    iter n (p:_) | n < p^2 = [n | n > 1]
    iter n ps@(p:ps') =
        let (d, r) = n `divMod` p
        in if r == 0 then p : iter d ps else iter n ps'

そして使用法:

> import Data.List
> import Control.Arrow

> primeFactors 12312
[2,2,2,3,3,3,3,19]

> (map (head &&& length) . group) (primeFactors 12312)
[(2,3),(3,4),(19,1)]

> (map head . group) (primeFactors 12312)
[2,3,19]
4
luochen1990

Haskellを使用すると、相互再帰的な無限リストを作成できます。これを利用しましょう。

まず、数値を可能な限り除算するヘルパー関数を作成しましょう。要因を見つけたら、それを数から完全に排除するために必要になります。

import Data.Maybe (mapMaybe)

-- Divide the first argument as many times as possible by the second one.
divFully :: Integer -> Integer -> Integer
divFully n q | n `mod` q == 0   = divFully (n `div` q) q
             | otherwise        = n

次に、すべての素数のリストがどこかにあると仮定すると、数の平方根未満のすべての素数で除算し、数が割り切れる場合は素数に注目することで、数の因数を簡単に見つけることができます。

-- | A lazy infinite list of non-trivial factors of all numbers.
factors :: [(Integer, [Integer])]
factors = (1, []) : (2, [2]) : map (\n -> (n, divisors primes n)) [3..]
  where
    divisors :: [Integer] -> Integer -> [Integer]
    divisors _ 1          = []   -- no more divisors
    divisors (p:ps) n
        | p^2 > n       = [n]  -- no more divisors, `n` must be prime
        | n' < n        = p : divisors ps n'    -- divides
        | otherwise     = divisors ps n'        -- doesn't divide
      where
        n' = divFully n p

逆に、数のすべての因子のリストがある場合、素数を見つけるのは簡単です。それらはまさにそれらの数であり、その素因数は数自体だけです。

-- | A lazy infinite list of primes.
primes :: [Integer]
primes = mapMaybe isPrime factors
  where
    -- |  A number is prime if it's only prime factor is the number itself.
    isPrime (n, [p]) | n == p  = Just p
    isPrime _                  = Nothing

秘訣は、因子のリストを手動で開始し、ある数の素因数のリストを決定するために必要なのは、その平方根よりも小さい素数だけであるということです。因子のリストを少し消費し、3の因子のリストを計算しようとするとどうなるか見てみましょう。2(手動で与えたものから計算できます)を取る素数のリストを消費しています。 )。 3を除算せず、3の平方根よりも大きいため、3の約数の可能性がないことがわかります。したがって、3の因数のリストは[3]です。これから、3が別の素数であると計算できます。等。

2
Petr Pudlák

私はちょうどこの問題に取り組みました。これが私の解決策です。

2つの支援機能は

factors n = [x | x <- [1..n], mod n x == 0]
isPrime n = factors n == [1,n]

次に、リスト内包を使用して、すべての素因数とそれらの数を取得します。

prime_factors num = [(last $ takeWhile (\n -> (x^n) `elem` (factors num)) [1..], x) | x <- filter isPrime $ factors num]

どこ

x <- filter isPrime $ factors num

与えられた数が持つ素因数を教えてくれます、そして

last $ takeWhile (\n -> (x^n) `elem` (factors num)) [1..]

この要素がいくつあるかを教えてくれます。

> prime_factors 36    -- 36 = 4 * 9
[(2,2),(2,3)]

> prime_factors 1800  -- 1800 = 8 * 9 * 25
[(3,2),(2,3),(2,5)]
2
yuanqili

よりエレガントなコード。2と奇数を使用して数値を除算します。

factors' :: Integral t => t -> [t]
factors' n
  | n < 0 = factors' (-n)
  | n > 0 = if 1 == n
               then []
               else let fac = mfac n 2 in fac : factors' (n `div` fac)
  where mfac m x
          | rem m x == 0 = x
          | x * x > m    = m
          | otherwise    = mfac m (if odd x then x + 2 else x + 1)
1
王万鹏

これが私のバージョンです。他の人ほど簡潔ではありませんが、とても読みやすく、理解しやすいと思います。

import Data.List

factor :: Int -> [Int]
factor n
  | n <= 1 = []
  | even n = 2 : factor(div n 2)
  | otherwise =
    let root = floor $ sqrt $ fromIntegral n
    in
      case find ((==) 0 . mod n) [3, 5.. root] of
        Nothing  -> [n]
        Just fac -> fac : factor(div n fac)
0
Matt Diamond