Nth combinations ({a, b} = {b, a}) without repetition (enumeration / brute force)

余生长醉 提交于 2019-11-30 21:09:20

问题


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:

  1. Remove the first element.
  2. Pair it with each other element in the list
  3. 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 tail and n, plus
  • the combinations you can get from tail and n-1, each with head prepended

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!