Let's assume I have a one-dimensional array of integers of size n. My problem is to generate all the combination of all possible groups of size 1 to n, such as each combination has exactly one occurrence of each element. The order does not count:
[[1,2]] == [[2,1]]
[[1],[2]] == [[2],[1]]
[[1,2],[3,4]] == [[2,1],[4,3]] == [[4,3],[2,1]]...
For example, for the input array [1,2], I would get:
[[1,2]]
[[1],[2]]
With [1,2,3], I would have:
[[1,2,3]]
[[1,2],[3]]
[[1,3],[2]]
[[2,3],[1]]
[[1],[2],[3]]
And with [1,2,3,4]:
[[1,2,3,4]]
[[1,2,3],[4]]
[[1,2,4],[3]]
[[1,3,4],[2]]
[[2,3,4],[1]]
[[1,2],[3,4]] <= Equivalent to [[3,4],[1,2]]
[[1,3],[2,4]] <= Equivalent to [[2,4],[1,3]]
[[1,4],[2,3]] <= Equivalent to [[2,3],[1,4]]
[[1,2],[3],[4]]
[[1,3],[2],[4]]
[[1,4],[2],[3]]
[[2,3],[1],[4]]
[[2,4],[1],[3]]
[[3,4],[1],[2]]
[[1],[2],[3],[4]]
My code below is working but is actually very slow when the list has more than 6 elements:
public Set<Set<Set<Integer>>> recurseGroups(List<Integer> initialArray, List<Set<Integer>> currentGroups, int i) {
if (i == initialArray.size()) {
// Deep copy current group to save its content
Set<Set<Integer>> copy = currentGroups.stream()
.map(HashSet::new)
.collect(Collectors.toSet());
return Collections.singleton(copy);
}
Set<Set<Set<Integer>>> result = new HashSet<>();
for (int j = i; j < initialArray.size(); ++j) {
// Add a singleton group with the i-th integer
// And generate groups with the remaining elements
Set<Integer> newGroup = new HashSet<>(Collections.singletonList(initialArray.get(i)));
currentGroups.add(newGroup);
result.addAll(recurseGroups(initialArray, currentGroups, i + 1));
currentGroups.remove(newGroup);
// Add the i-th integer to each previous existing groups
// And generate groups with the remaining elements
for (int k = 0; k < currentGroups.size(); ++k) {
currentGroups.get(k).add(initialArray.get(i));
result.addAll(recurseGroups(initialArray, currentGroups, i + 1));
currentGroups.get(k).remove(initialArray.get(i));
}
}
return result;
}
// Calling line
Set<Set<Set<Integer>>> groups = recurseGroups(Arrays.asList(1,2,3), new ArrayList<>(), 0)
By adding a log in the stop condition, I noticed the code actually adds many times the same group combinations. I suspect it would be possible to improve the stop condition or the loop, but I don't see how.