3

I've been solving a few combinatoric problems on Haskell, so I wrote down those 2 functions:

permutations :: (Eq a) => [a] -> [[a]]
permutations [] = [[]]
permutations list = do
    x  <- list
    xs <- permutations (filter (/= x) list)
    return (x : xs)

combinations :: (Eq a, Ord a) => Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n list = do
    x  <- list
    xs <- combinations (n-1) (filter (> x) list)
    return (x : xs)

Which works as follows:

*Main> permutations [1,2,3]
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
*Main> combinations 2 [1,2,3,4]
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

Those were uncomfortably similar, so I had to abstract it. I wrote the following abstraction:

combinatoric next [] = [[]]
combinatoric next list = do
    x  <- list
    xs <- combinatoric next (next x list)
    return (x : xs)

Which receives a function that controls how to filter the elements of the list. It can be used to easily define permutations:

permutations :: (Eq a) => [a] -> [[a]]
permutations = combinatoric (\ x ls -> filter (/= x) ls)

But I couldn't define combinations this way since it carries an state (n). I could extend the combinatoric with an additional state argument, but that'd become too clunky and I remember such approach was not necessary in a somewhat similar situation. Thus, I wonder: is it possible to define combinations using combinatorics? If not, what is a better abstraction of combinatorics which successfully subsumes both functions?

Community
  • 1
  • 1
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • If you don't mind, can you please explain how `(filter (/= x) list)` works? I see that `x` is `list` (as per the previous line) and you are trying to filter the elements from `list` which are not equal to `list`? – thefourtheye Mar 08 '15 at 07:36
  • A good implementation of `permutations` or `combinations` probably shouldn't require either `Eq` or `Ord` instances unless you are trying to do something more sophisticated so that `permuations [1,1] = [[1,1]]`. – Cirdec Mar 08 '15 at 07:41
  • @Cirdec hm makes sense. I guess you are right. – MaiaVictor Mar 08 '15 at 07:57
  • 2
    @thefourtheye no, here `x` is just an element of the list (`1` or `2` or `3`...). This is actually a dirty and wrong way to remove an element from a list, i.e., `filter (/= 2) [1,2,3,4] == [1,3,4]`. I believe there are better alternatives. Notice that the monad instance of lists is implemented so that `x <- list` causes `x` to be each element of `list` nondeterministically, as if that line split the program in different universes. – MaiaVictor Mar 08 '15 at 07:58
  • 1
    @Viclib So, `x <- list` will be iterating the `list` and give `x` a new value from `list` every time? – thefourtheye Mar 08 '15 at 07:59
  • 1
    @thefourtheye yes, that's what the list monad does. Try this on ghci: `do { x <- [1,2]; y <- [3,4]; return (x,y) }`. The result is: `[(1,3),(1,4),(2,3),(2,4)]`. – MaiaVictor Mar 08 '15 at 08:00
  • @thefourtheye no problems :) – MaiaVictor Mar 08 '15 at 08:01
  • @Viclib does the input list to combinotorics have all unique elements ? – advocateofnone Mar 08 '15 at 08:02
  • Yes, I should've used a set actually. – MaiaVictor Mar 08 '15 at 08:02
  • Just coming from a noob not so familiar with functional programming but combinations ( say r out of n ) can be found out by picking the sorted first r elements of all permutations and inserting in a set ( so duplicates are not there ) although it might be an overkill in terms of time – advocateofnone Mar 08 '15 at 08:10

2 Answers2

5

This isn't a direct answer to your question (sorry), but I don't think your code is correct. The Eq and Ord constraints tipped me off - they shouldn't be necessary - so I wrote a couple of QuickCheck properties.

prop_numberOfPermutations xs = length (permutations xs) === factorial (length xs)
    where _ = (xs :: [Int])  -- force xs to be instantiated to [Int]

prop_numberOfCombinations (Positive n) (NonEmpty xs) = n <= length xs ==>
        length (combinations n xs) === choose (length xs) n
    where _ = (xs :: [Int])


factorial :: Int -> Int
factorial x = foldr (*) 1 [1..x]

choose :: Int -> Int -> Int
choose n 0 = 1
choose 0 r = 0
choose n r = choose (n-1) (r-1) * n `div` r

The first property checks that the number of permutations of a list of length n is n!. The second checks that the number of r-combinations of a list of length n is C(n, r). Both of these properties fail when I run them against your definitions:

ghci> quickCheck prop_numberOfPermutations
*** Failed! Falsifiable (after 5 tests and 4 shrinks):    
[0,0,0]
3 /= 6
ghci> quickCheck prop_numberOfCombinations 
*** Failed! Falsifiable (after 4 tests and 1 shrink):     
Positive {getPositive = 2}
NonEmpty {getNonEmpty = [3,3]}
0 /= 1

It looks like your functions fail when the input list contains duplicate elements. Writing an abstraction for an incorrect implementation isn't a good idea - don't try and run before you can walk! You might find it helpful to read the source code for the standard library's definition of permutations, which does not have an Eq constraint.

Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • No problems! But I commented below the original thread the list can't contain duplicate elements. You are right, though: I should've used a set to make this clearer. Thanks for the concern! Anyway, if anyone is curious, in order to fix this issue on my code, instead of filtering, just remove the element by its index. One could, for example, write a monad instance for sets which provided not only the element but the "rest" of the list. – MaiaVictor Mar 08 '15 at 15:40
  • I don't think such a monad exists – Benjamin Hodgson Mar 08 '15 at 15:50
1

First let's improve the original functions. You assume that all elements are distinct wrt their equality for permutations, and that they're distinct and have an ordering for combinations. These constraints aren't necessary and as described in the other answer, the code can produce wrong results. Following the robustness principle, let's accept just unconstrained lists. For this we'll need a helper function that produces all possible splits of a list:

split :: [a] -> [([a], a, [a])]
split = loop []
  where
    loop _  []      = []
    loop rs (x:xs)  = (rs, x, xs) : loop (x:rs) xs

Note that the implementation causes prefixes returned by this function to be reversed, but it's nothing we require.

This allows us to write generic permutations and combinations.

permutations :: [a] -> [[a]]
permutations [] = [[]]
permutations list = do
    (pre, x, post) <- split list
    -- reversing 'pre' isn't really necessary, but makes the output
    -- order natural
    xs <- permutations (reverse pre ++ post)
    return (x : xs)

combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n list = do
    (_, x, post) <- split list
    xs <- combinations (n-1) post
    return (x : xs)

Now what they have in common:

  • At each step they pick an element to output,
  • update the list of elements to pick from and
  • stop after some condition is met.

The last point is a bit problematic, as for permutations we end once the list to choose from is empty, while for combinations we have a counter. This is probably the reason why it was difficult to generalize. We can work around this by realizing that for permutations the number of steps is equal to the length of the input list, so we can express the condition in the number of repetitions.

For such problems it's often very convenient to express them using StateT s [] monad, where s is the state we're working with. In our case it'll be the list of elements to choose from. The core of our combinatorial functions can be then expressed with StateT [a] [] a: pick an element from the state and update the state for the next step. Since the stateful computations all happen in the [] monad, we automatically branch all possibilities. With that, we can define a generic function:

import Control.Monad.State

combinatoric :: Int -> StateT [a] [] b -> [a] -> [[b]]
combinatoric n k = evalStateT $ replicateM n k

And then define permutations and combinations by specifying the appropriate number of repetitions and what's the core StateT [a] [] a function:

permutations' :: [a] -> [[a]]
permutations' xs = combinatoric (length xs) f xs
  where
    f = StateT $ map (\(pre, x, post) -> (x, reverse pre ++ post)) . split


combinations' :: Int -> [a] -> [[a]]
combinations' n xs = combinatoric n f xs
  where
    f = StateT $ map (\(_, x, post) -> (x, post)) . split
Petr
  • 62,528
  • 13
  • 153
  • 317