I am trying to break a list of integers (say elements
) into all of its possible partitions with combinations of the same size (say n
), where the combinations are unique across different partitions, not just within the same partition.
The use case is that I have a list of people, and I need to match them so they all meet each other exactly once, in the minimum amount of time. So for a list of 10 people meeting weekly, and meetings of 1:1 (2 people in each combination), they can all meet each other in 9 weeks.
A solution that can take any combination size is ideal, but one that only works for size 2 is also great.
Example
For example, if the list is [1,2,3,4,5,6]
.
We can't have both of these partitions:
[1,2], [3,5], [4,6]
[1,2], [3,4], [5,6]
because then [1,2]
is found twice...
A possible result (or the only? not sure) is:
1: [1,2], [3,4], [5,6]
2: [1,3], [2,5], [4,6]
3: [1,4], [2,6], [3,5]
4: [1,5], [2,4], [3,6]
5: [1,6], [2,3], [4,5]
What I tried?
This is very naive and does not work, because it ends up doing something like this:
1: [1,2], [3,5], [4,6]
2: [1,3], [2,5], ??? -> Can't do [4,6] again!
It basically gets all possible combinations of a certain size, then tries to make the partitions from them.
var result = [Partition<T>]()
var combinations = elements.combinations(ofCount: 2)
while !combinations.isEmpty {
var pairs = [Pair<T>]()
var elementsLeft = elements
while elementsLeft.count > 1 {
let aPair = combinations.first { elementsLeft.contains($0.x) && elementsLeft.contains($0.y) }!
combinations.remove(aPair)
elementsLeft.remove(aPair.x)
elementsLeft.remove(aPair.y)
pairs.append(aPair)
}
let partition = Partition(matches: pairs, unmatched: Array(elementsLeft))
result.append(partition)
}
return result
(I am using Swift, but will happily take advice in any language or pseudo logic! As long as it avoids things I am not sure how to translate like Python's yield
etc.)
I also know of a way to get partitions with combinations of 2 where each partition has exactly 1 duplicated combination. Going back to the use case, it means I will match a list of 6 people in 6 weeks (instead of 5), and that there will be weeks where a couple of people won't have a meeting. That solution can be easily shown in a small table (letters are elements/people in the list, numbers are partition/week number). It essentially just involves shifting the numbers up by 1 row in each column, ignoring pairs of an element with itself…
I can't use this solution because it means having another partition (ie, one more week)...
| | A | B | C | D | E | F |
|---|---|---|---|---|---|---|
| A | | 2 | 3 | 4 | 5 | 6 |
| B | 2 | | 4 | 5 | 6 | 1 |
| C | 3 | 4 | | 6 | 1 | 2 |
| D | 4 | 5 | 6 | | 2 | 3 |
| E | 5 | 6 | 1 | 2 | | 4 |
| F | 6 | 1 | 2 | 3 | 4 | |
eg
week 1: [F,B], [E,C] (where [A,D] "unmatched")
week 2: [A,B], [C,F], [D,E]
// ...