3
votes

I have a combinatorial problem that can be solved inefficiently using the cartesian product of multiple sets. Concretely, I have multiple items and multiple elements that satisfy each item. The problem consists of finding all possible combinations of elements that satisfy all items. For example:

items -> elements
------------------------
1 -> {a,b}     // a and b cover the item 1
2 -> {a,b}     // a and b cover the item 2
3 -> {a,b,c}   // a, b and c cover the item 3
4 -> {a,b,e,f} // a, b, e, f cover the item 4

Alternative representation:
element -> items covered
------------------------
a -> {1,2,3,4}
b -> {1,2,3,4}
c -> {3}
e -> {4}
f -> {4}

The goal is to find all combinations that cover items 1,2,3,4. Valid solutions are:

{a},{a,b},{a,c},{a,e},{a,f},{a,b,c},{a,b,e},{a,b,f},{a,b,c,e},{a,b,c,f}
{b},{b,c},{b,e},{b,f},{b,c,e},{b,c,f}

Note that the order is not important, so {a,b} = {b,a} ({a,b} x {c,d} = {c,d} x {a,b}). Also, note that {a,a,a,a}, {a,a,a,b}... are redundant combinations.

As you can see, this problem is similar to the set cover problem, where the universe of elements for this example are the items U={1,2,3,4} and the set of subsets from U is S={ab={1,2,3,4},c={3},ef{4}}, where set {1,2,3,4} is the set of items covered by the element a and b, {3} is the set of elements covered by c, and {4} is the set of elements covered by elements e and f. However, the goal here is not finding the minimal combination of sets from S that covers all elements from U, but finding all combinations of elements {a,b,c,e,f} that cover all items {1,2,3,4}.

A näive implementation could be done by performing a cartesian product between sets for 1,2,3 and 4, and then filtering the combinations that are redundant. However, this approach is very inefficient. Suppose I have this situation:

1 -> {a,b,c,d,e,f,g,h}
2 -> {a,b,c,d,e,f,g,h}
3 -> {a,b,c,d,e,f,g,h}
4 -> {a,b,c,d,e,f,g,h}
5 -> {a,b,c,d,e,f,g,h}
6 -> {a,b,c,d,e,f,g,h,i}

A cartesian product between the six sets will result in a 8^5*9=294912 combinations, when there are actually many fewer combinations, which are: {a,b,c,d,e,f,g} U {a,b,c,d,e,f,g} x {i}.

Another way to solve this problem is to enumerate all elements, skipping the combinations that are equivalent to other previously generated, and also skipping repeated elements. This is kinda easy to compute and can be implemented as an iterator that returns a combination at a time, but I don't know if there is a better way to solve this problem, or if this problem was studied before.

How would you solve this problem?

2
Are you trying to find all combinations of the elements found by taking the union of N sets? If so, then here is some pseudocode to accomplish this. Otherwise, please explain what you mean by "cover" - what does it mean for a set to cover 1,2,3,etc (usually cover is meant in the sense of the set covering problem, but I don't think that's what you're after here)Zim-Zam O'Pootertoot
You are using the word combination in a nonstandard way so need to define your meaning of it. Also need to define satisfy (you apparently mean cover), and define item and element. Or rewrite your question using standard set terminology.James Waldby - jwpat7
Maybe I should have formalized the problem instead of explaining it in an informal way, but I thought that it would be easy to understand with some examples. I edited the description trying to describe the problem better. Thanks for the tips.Markov Unchained
why didn't you list {a,b,e} as a valid solution in your first example? Is it because you listed only some of the valid combinations? By the way, I added Haskell code to my answer, which provides an immediate result for the two examples given..how big would you expect the input data to be?גלעד ברקן
You're right. {a,b,e} is a valid solution too.Markov Unchained

2 Answers

2
votes

First, realize that if a set of elements does not satisfy all items, neither does any of its subsets.

Second, realize that if a set satisfies all items, so do all its supersets.

Now, all you have to do is:

Let S be the set of all elements. Let R be the empty set.

Define a function find( s, r ) which does:

If r includes s, return r.
If s does not satisfy all items, return r.

Otherwise add s to r.
For every item I in  s,
     let s' be s-I
     let s be f(s', r)

return s.

Just call find(S,R) and you have your answer.

This method performs some duplicate tests, but always kills a branch whenever it is identified as such. This leads to a lot of pruning on a large set of elements.

Both lookup of whether r includes a particular set of elements and the check if s satisfies all items can be made very fast at the expense of extra memory.

1
votes

What if you did this:

1 -> {a,b}
2 -> {b,c}
3 -> {a,b,c}
4 -> {a,e,f}

=>

a -> [1,3,4]
b -> [1,2,3]
c -> [2,3]
e -> [4]
f -> [4]

Then enumerate the combinations of the left side that provide (at least) [1,2,3,4]

For each item in the set of all-satisfying sets, enumerate combinations 
with other items.

All-Satisfying-Sets: {{a,b},{b,e},{b,f}}
Combinations within All-Satisfiying-Sets: {{a,b,e},{a,b,f},{b,e,f},{a,b,e,f}}
Others: {c}
Combinations with Others: {{a,b,c},{b,e,c},{b,f,c}
                          ,{a,b,e,c},{a,b,f,c},{b,e,f,c},{a,b,e,f,c}}

Or you could do this in Haskell:

import Data.List (union, subsequences, sort)

example1 = [(["a"],[1,2,3,4])
           ,(["b"],[1,2,3,4])
           ,(["c"],[3])
           ,(["e"],[4])
           ,(["f"],[4])]

example2 = [(["a"],[1,2,3,4,5,6])
           ,(["b"],[1,2,3,4,5,6])
           ,(["c"],[1,2,3,4,5,6])
           ,(["e"],[1,2,3,4,5,6])
           ,(["f"],[1,2,3,4,5,6])
           ,(["g"],[1,2,3,4,5,6])
           ,(["h"],[1,2,3,4,5,6])
           ,(["i"],[6])]

combs items list = 
  let unify (a,b) (a',b') = (sort (a ++ a'), sort (union b b')) 
  in map fst
     . filter ((==items) . snd) 
     . map (foldr unify ([],[])) 
     . subsequences 
     $ list


OUTPUT:

*Main> combs [1..4] example1
[["a"],["b"],["a","b"],["a","c"],["b","c"],["a","b","c"],["a","e"],["b","e"],
["a","b","e"],["a","c","e"],["b","c","e"],["a","b","c","e"],["a","f"],["b","f"], 
["a","b","f"],["a","c","f"],["b","c","f"],["a","b","c","f"],["a","e","f"],
["b","e","f"],["a","b","e","f"],["a","c","e","f"],["b","c","e","f"],
["a","b","c","e","f"]]