第N个组合({a,b} = {b,a})没有重复(枚举/暴力)

时间:2016-06-09 20:26:26

标签: java algorithm generics combinations combinatorics

我正在寻找一种能够在不重复的情况下生成第n个组合的算法。

我可以在(a, b) != (b, a)的排列中找到很多,但我正在寻找{a, b} = {b, a}的组合。

示例:

Set = {a, b, c}
n = 2
Combinations: {a, b}, {a, c}, {a, d}, {b, c}, {b, d}, {c, d}

Java中使用Set或List的通用递归实现会很棒。我也很感激与一个很好的解释,伪或示例代码的链接。

4 个答案:

答案 0 :(得分:3)

递归算法很简单:

  1. 删除第一个元素。
  2. 将其与列表中的其他元素配对
  3. 将该列表添加到由其余元素
  4. 创建的列表中

答案 1 :(得分:3)

你还记得找到k-th permutations of the elements的问题吗?并不是很多人都知道算法背后的原因,但它背后有一个数学理论。它可以通过在factorial number system中表示数字来解决。

如果问题是关于找到第k个组合,我为什么要谈论第k个排列?只因为它可以用类似的数学理论来解决。令人惊讶的是,有一个combinatorial number system

  

集合S的k-组合是S的子集,具有k(不同)   元素。组合数系统的主要目的是   提供一个代表,每个代表一个数字   {\ displaystyle {\ tbinom {n} {k}}}集合S的可能k组合   n个元素。

阅读文章,很可能您将能够解决您的问题。

答案 2 :(得分:3)

根据输入listn,将list拆分为第一项,列表的其余部分将其称为headtail。然后,您寻求的组合是:

  • 您可以从tailn获得的组合,以及
  • 您可以从tailn-1获得的组合,每个组合都有head前置

如果n为0,则结果只有一个组合:{}

如果n大于list,则结果为无组合

旁注:为了好玩,我在Haskell中解决了这个问题,将n = 0个案放在了最上面:

comb 0 _ = [[]]
comb n (head:tail) | n > 0 = comb n tail ++ map (head:) (comb (n-1) tail)
comb _ _ = []

答案 3 :(得分:1)

您可以使用以下递归方法执行此操作:

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;
}

然后您可以使用(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);
}

生成:

[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]

该程序的工作原理如下:k是我们仍需要选择的元素数,i是当前的偏移值。最初该偏移值为0

现在我们从i迭代到n-k,寻找潜在的候选人参与其中。该范围内的每个元素都是某些组合的头部。我们对列表的其余部分执行递归。递归生成在列表的其余部分上采用 k-1 元素的所有列表。然后我们的工作就是在前面添加一个头并返回列表。

通过使用特殊形式的LinkedList(在逻辑和函数式编程语言中很常见),您可以更快,更保守地实现这一点。