问题
I'm looking for an algorithm that generates the nth combinations without repetition.
I could find a lot on permutations where (a, b) != (b, a), but I'm looking for combinations where {a, b} = {b, a}.
Example:
Set = {a, b, c}
n = 2
Combinations: {a, b}, {a, c}, {a, d}, {b, c}, {b, d}, {c, d}
A generic, recursive implementation in Java that takes a Set or List would be great. I would also appreciate a link with a good explanation, pseudo or example code.
回答1:
You can do this using the following recursive method:
public static<T> ArrayList<ArrayList<T>> getPermutations (List<T> elements, int k) {
return getPermutations (elements,k,0);
}
public static<T> ArrayList<ArrayList<T>> getPermutations (List<T> elements, int k, int i) {
ArrayList<ArrayList<T>> results = new ArrayList<>();
if(k > 0) {
int n = elements.size();
for(int j = i; j <= n-k; j++) {
T val = elements.get(j);
ArrayList<ArrayList<T>> tails = getPermutations(elements,k-1,j+1);
for(ArrayList<T> tail : tails) {
ArrayList<T> result = new ArrayList<>();
result.add(val);
result.addAll(tail);
results.add(result);
}
}
} else {
results.add(new ArrayList<T>());
}
return results;
}
You can then run it for instance with (jDoodle):
ArrayList<Character> set = new ArrayList<>();
set.add('a');
set.add('b');
set.add('c');
for(ArrayList<Character> element : getPermutations(set,2)) {
System.out.println(element);
}
System.out.println("----------");
for(ArrayList<Character> element : getPermutations(set,3)) {
System.out.println(element);
}
System.out.println("----------");
set.add('d');
for(ArrayList<Character> element : getPermutations(set,2)) {
System.out.println(element);
}
System.out.println("----------");
for(ArrayList<Character> element : getPermutations(set,3)) {
System.out.println(element);
}
Which generates:
[a, b]
[a, c]
[b, c]
----------
[a, b, c]
----------
[a, b]
[a, c]
[a, d]
[b, c]
[b, d]
[c, d]
----------
[a, b, c]
[a, b, d]
[a, c, d]
[b, c, d]
The program works as follows: k is the number of elements we still have to pick, and i is the current offset value. Initially that offset value is 0.
Now we iterate from i to n-k looking for potential candidates to be part of the head. Each element in that range will be the head for some combinations. We perform recursion on the remainder of the list. The recursion generates all the lists that take k-1 elements on the remainder of the list. Then our job is simply to add a head in front and return the list.
You can implement this both faster and more memory conservative by using a special form of LinkedLists (which are common in logic and functional programming languages).
回答2:
The recursive algorithm is easy:
- Remove the first element.
- Pair it with each other element in the list
- Add that list to the list created by the remaining elements
回答3:
Do you remember the problem of finding the k-th permutations of the elements? Not a lot of people know the reason behind the algorithm, but it has a mathematical theory behind it. It can be solved by representing the number in factorial number system.
Why am I talking about k-th permutation if the question was about finding k-th combination? Only because it can be solved with the similar mathematical theory. Surprise, surprise, there is a combinatorial number system:
A k-combination of a set S is a subset of S with k (distinct) elements. The main purpose of the combinatorial number system is to provide a representation, each by a single number, of all {\displaystyle {\tbinom {n}{k}}} possible k-combinations of a set S of n elements.
Read the article and most probably you will be able to solve your problem.
回答4:
Given an input list and n, split list into the first item and the rest of the list calling them head and tail. Then, the combinations you seek are:
- the combinations you can get from
tailandn, plus - the combinations you can get from
tailandn-1, each withheadprepended
If n is 0, the result is just one combination: {}
If n more than the size of list, the result is no combinations
Side note: For fun, I solved this in Haskell, putting the n = 0 case at the top:
comb 0 _ = [[]]
comb n (head:tail) | n > 0 = comb n tail ++ map (head:) (comb (n-1) tail)
comb _ _ = []
来源:https://stackoverflow.com/questions/37735513/nth-combinations-a-b-b-a-without-repetition-enumeration-brute-force