3

I want to find all prime factors of a given number using only list comprehension method and/or . (function composition operator) in Haskell. I specifically want to avoid a recursive solution.

For example, pfactors 120 must produce [2,2,2,3,5] output.

I tried:

pfactors n = [p | p <- [2..n], n `mod` p == 0, [d | d <- [1..p], p `mod` d == 0] == [1,p]]

But when I call pfactors 120, the result is [2,3,5], not all prime factors.

Christian Conkle
  • 5,932
  • 1
  • 28
  • 52
sasas
  • 65
  • 7

1 Answers1

5

Here's how I'd do it:

pfactors :: Integer -> [Integer]
pfactors n = [ p
             | p <- [2..n]                                  -- Possible factors
             , [d | d <- [1..p], p `mod` d == 0] == [1,p]   -- Are prime 
             , _ <- [ p | i <- [1..n], n `mod` p^i == 0] ]  -- Divisible powers

It's essentially the solution you have, but the difference is that it has an extra list comprehension at the end which contains as many elements as p factors into n.

Disclaimer I really wouldn't do it like this in reality.

EDIT I felt dirty writing the above, so for reference, this is something closer to what I would write:

pfactors' :: Int -> [Int]
pfactors' = unfoldr firstFactor
  where
    firstFactor n =
        listToMaybe [(f, n `div` f)
                    | f <- [2..n]
                    , n `mod` f == 0]

Dependencies: Data.List (unfoldr), Data.Maybe (listToMaybe)

amnn
  • 3,657
  • 17
  • 23
  • Ok, I stand corrected, but shouldn't you filter to keep only the highest powers of each `p`? – didierc Jun 02 '14 at 21:53
  • Great @asQuirrel. That's my answer. Thank you very much! – sasas Jun 02 '14 at 21:54
  • 1
    @didierc, I don't believe so. What I'm doing here is keeping all the powers of `p` that divide `n` (by definition, these will be the smallest powers). – amnn Jun 02 '14 at 21:56
  • Yeah, *all the powers*, but strictly speaking, shouldn't you only return the highest ones? (I say strictly, because one can always filter them afterwards, so your answer is ok). – didierc Jun 02 '14 at 22:01
  • I agree with you @asQuirrel. Recursive solution is the best one. But I was wondering whether it is possible with this method. Everything is possible with you:) Thanks. – sasas Jun 02 '14 at 22:05
  • 1
    @didierc, I'm still not following you, if you're talking about the value for p, it is checked for primality first, if you are talking about the last part (what I added), then the values are discarded, all we were actually interested in was the number of powers. – amnn Jun 02 '14 at 22:13
  • Ahhh, ok, I did not understand your code. Very clever. – didierc Jun 02 '14 at 22:17
  • 1
    very nice `listToMaybe` trick!.. Though your formulation is inefficient; it should be ``factors n = unfoldr g (2,n) where g (d, n) = listToMaybe [(x, (x, div n x)) | n > 1, x <- [d..isqrt n]++[n], rem n x == 0]``. More [here](http://stackoverflow.com/a/15703327/849891). :) – Will Ness Jun 03 '14 at 08:59
  • Very nice, that's the solution I was aiming for :) – amnn Jun 03 '14 at 10:49
  • it's still suboptimal: it tests by all numbers instead of by primes only (precalculating the primes takes time but is worth it when doing more than a few calls to `factors`). cf. http://www.haskell.org/haskellwiki/Testing_primality#Primality_Test_and_Integer_Factorization . But at least the deficiency is not *quadratic*... – Will Ness Jun 03 '14 at 14:58