所有可能的n组合

时间:2013-05-14 18:01:48

标签: java algorithm combinations

我有n套。每个集合都有不同数量的元素。我想写一个算法,它给出了所有可能的组合。 例如: 让我们说:

S1={1,2}, S2={A,B,C}, S3={$,%,£,!}

组合应该看起来像

C1={1,A,$}
C2={1,A,%}

......等可能组合的数量为2*3*4 = 24

请帮我用Java编写这个算法。

非常感谢提前

4 个答案:

答案 0 :(得分:13)

递归是你的朋友:

public class PrintSetComb{
    public static void main(String[] args){
        String[] set1 = {"1","2"};
        String[] set2 = {"A","B","C"};
        String[] set3 = {"$", "%", "£", "!"};
        String[][] sets = {set1, set2, set3};

        printCombinations(sets, 0, "");
    }

    private static void printCombinations(String[][] sets, int n, String prefix){
        if(n >= sets.length){
            System.out.println("{"+prefix.substring(0,prefix.length()-1)+"}");
            return;
        }
        for(String s : sets[n]){
            printCombinations(sets, n+1, prefix+s+",");
        }
    }
}

回答OP关于将其概括为对象集的问题:

import java.util.Arrays;

public class PrintSetComb{

    public static void main(String[] args){
        Integer[] set1  = {1,2};
        Float[] set2    = {2.0F,1.3F,2.8F};
        String[] set3   = {"$", "%", "£", "!"};
        Object[][] sets = {set1, set2, set3};

        printCombinations(sets, 0, new Object[0]);
    }

    private static void printCombinations(Object[][] sets, int n, Object[] prefix){
        if(n >= sets.length){
            String outp = "{";
            for(Object o: prefix){
                outp = outp+o.toString()+",";
            }
            System.out.println(outp.substring(0,outp.length()-1)+"}");
            return;
        }
        for(Object o : sets[n]){
            Object[] newPrefix = Arrays.copyOfRange(prefix,0,prefix.length+1);
            newPrefix[newPrefix.length-1] = o;
            printCombinations(sets, n+1, newPrefix);
        }
    }
}

最后是一个迭代变体。它基于增加一个计数器数组,当计数器达到集合的大小时,计数器会进行包装和携带:

import java.util.Arrays;

public class PrintSetCombIterative{

    public static void main(String[] args){
            String[] set1 = {"1","2"};
            String[] set2 = {"A","B","C"};
            String[] set3 = {"$", "%", "£", "!"};
            Object[][] sets = {set1, set2, set3};

            printCombinations(sets);
    }


    private static void printCombinations(Object[][] sets){
        int[] counters = new int[sets.length];

        do{
           System.out.println(getCombinationString(counters, sets));
        }while(increment(counters, sets));
    }

    private static boolean increment(int[] counters, Object[][] sets){
            for(int i=counters.length-1;i>=0;i--){
                if(counters[i] < sets[i].length-1){
                    counters[i]++;
                    return true;
                } else {
                    counters[i] = 0;
                }
            }
            return false;
    }

    private static String getCombinationString(int[] counters, Object[][] sets){
        String combo = "{";
        for(int i = 0; i<counters.length;i++){
            combo = combo+sets[i][counters[i]]+",";
        }
        return combo.substring(0,combo.length()-1)+"}";

    }
}

答案 1 :(得分:2)

如果有人想要矩阵而不是打印,我稍微修改了代码:

public class TestSetCombinations2 {

    public static void main(String[] args) {
        Double[] set1 = {2.0,3.0};
        Double[] set2 = {4.0,2.0,1.0};
        Double[] set3 = {3.0, 2.0, 1.0, 5.0};
        Double[] set4 = {1.0,1.0};
        Object[][] sets = {set1, set2, set3, set4};

        Object[][] combinations = getCombinations(sets);

        for (int i = 0; i < combinations.length; i++) {
            for (int j = 0; j < combinations[0].length; j++) {
              System.out.print(combinations[i][j]+" ");
            }
            System.out.println();
        }
    }

    private static Object[][] getCombinations(Object[][] sets) {

      int[] counters = new int[sets.length];
        int count = 1;   
        int count2 = 0;

        for (int i = 0; i < sets.length; i++) {
          count *= sets[i].length;
        }

        Object[][] combinations = new Object[count][sets.length];

        do{
           combinations[count2++] = getCombinationString(counters, sets);
        } while(increment(counters, sets));

        return combinations;
    }

    private static Object[] getCombinationString(int[] counters, Object[][] sets) {

      Object[] o = new Object[counters.length];
      for(int i = 0; i<counters.length;i++) {
        o[i] = sets[i][counters[i]];
      }
      return o;

    }

    private static boolean increment(int[] counters, Object[][] sets) {
        for(int i=counters.length-1;i>=0;i--) {
            if(counters[i] < sets[i].length-1) {
                counters[i]++;
                return true;
            } else {
                counters[i] = 0;
            }
        }
        return false;
    }
}

答案 2 :(得分:0)

krilid提供了很好的解决方案,我修改了一下以返回所有组合的列表。

@Test
public void testMap() throws Exception {
    char[] one = new char[]{'a', 'b', 'c'};
    char[] two = new char[]{'d', 'e', 'f'};
    char[] three = new char[]{'g', 'h', 'i', 'j'};
    char[][] sets = new char[][]{one, two, three};
    List<List<Character>> collector = new ArrayList<>();
    ArrayList<Character> combo = new ArrayList<>();
    combinations(collector, sets, 0, combo);
    System.out.println(collector);
}

private void combinations(List<List<Character>> collector, char[][] sets, int n, ArrayList<Character> combo) {
    if (n == sets.length) {
        collector.add(new ArrayList<>(combo));
        return;
    }
    for (char c : sets[n]) {
        combo.add(c);
        combinations(collector, sets, n + 1, combo);
        combo.remove(combo.size() - 1);
    }
}

答案 3 :(得分:0)

map 和 reduce 方法

您可以创建一个通用方法来获取不同类型和数量的集合及其元素的笛卡尔积

Try it online!

public static void main(String[] args) {
  Set<Integer> a = Set.of(1, 2);
  List<String> b = List.of("A", "B", "C");
  Set<Character> c = Set.of('$', '%', '£', '!');

  Set<Set<Object>> cpSet = cartesianProduct(HashSet::new, a, b, c);
  List<List<Object>> cpList = cartesianProduct(ArrayList::new, a, b, c);

  // output, order may vary
  System.out.println(cpSet);
  System.out.println(cpList);
}
/**
 * @param nCol the supplier of the output collection
 * @param cols the input array of collections
 * @param <R>  the type of the return collection
 * @return the cartesian product of the multiple collections
 */
@SuppressWarnings("unchecked")
public static <R extends Collection<?>> R cartesianProduct(
      Supplier nCol, Collection<?>... cols) {
  // check if supplier is not null
  if (nCol == null) return null;
  return (R) Arrays.stream(cols)
      // non-null and non-empty collections
      .filter(col -> col != null && col.size() > 0)
      // represent each element of a collection as a singleton collection
      .map(col -> (Collection<Collection<?>>) col.stream()
          .map(e -> Stream.of(e).collect(Collectors.toCollection(nCol)))
          .collect(Collectors.toCollection(nCol)))
      // summation of pairs of inner collections
      .reduce((col1, col2) -> (Collection<Collection<?>>) col1.stream()
          // combinations of inner collections
          .flatMap(inner1 -> col2.stream()
              // concatenate into a single collection
              .map(inner2 -> Stream.of(inner1, inner2)
                  .flatMap(Collection::stream)
                  .collect(Collectors.toCollection(nCol))))
          // collection of combinations
          .collect(Collectors.toCollection(nCol)))
      // otherwise an empty collection
      .orElse((Collection<Collection<?>>) nCol.get());
}

输出(裁剪后,顺序可能会有所不同):

[[1, A, !], [1, !, B], [A, !, 2], [1, A, £], [1, !, C]...
[[1, A, $], [1, A, %], [1, A, £], [1, A, !], [1, B, $]...

另见:Finding cartesian product in Java