如何迭代List <list <t>&gt;的所有组合

时间:2016-02-26 11:02:40

标签: java iterator combinatorics

我有一个List<List<T>>两个列表尺寸的长度各不相同 Using recursion I can calculate all the combinations

一些示例List<List<T>>及其组合

[
 [1],
 [2, 3],
 [4, 5, 6]
] 
// [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6]

[ 
 [0, 4],
 [3, 4, 1, 2]
]
// [0, 3], [0, 4], [0, 1], [0, 2], [4, 3], [4, 4], [4, 1], [4, 2]

[ 
 ["A", "B", "B"],
 ["C"]
]
// ["A", "C"], ["B", "C"], ["B, "C"]

组合的数量可以快速增长,使用递归会成为记忆和性能问题 如何计算迭代器来迭代组合?

做一些阅读我可能能够使用阶乘或组合数字系统,但我不确定如何在这种情况下应用它。

2 个答案:

答案 0 :(得分:3)

您可以实现一个Iterator<List<T>>,它只包含对原始元素的引用,作为列表列表和每个子列表的当前位置,并根据需要生成新的组合:

class Combinations<T> implements Iterator<List<T>> {

    final List<List<T>> elements;
    final int[] indices;

    public Combinations(List<List<T>> elements) {
        this.elements = elements;
        this.indices = new int[elements.size()];
    }

    @Override
    public boolean hasNext() {
        // has first index not yet reached max position?
        return indices[0] < elements.get(0).size();
    }

    @Override
    public List<T> next() {
        // get next
        List<T> result = new ArrayList<>(indices.length);
        for (int i = 0; i < indices.length; i++) {
            result.add(elements.get(i).get(indices[i]));
        }
        // increase indices
        for (int i = indices.length - 1; i >= 0; i--) {
            indices[i]++;
            if (indices[i] >= elements.get(i).size() && i > 0) {
                indices[i] %= elements.get(i).size();
            } else {
                break;
            }
        }
        return result;
    }
}

增加指数的部分有点棘手。我猜这是因子编号系统发挥作用的部分,因为你基本上必须使用&#34; + 1&#34;一个数字(组合索引),其中每个数字具有不同的基数(各个子列表中的元素数量)。但是你可以通过一个简单的循环来完成它,而不使用任何特殊的库。

我为你的例子尝试了这个,它似乎有效:

List<List<Integer>> elements = Arrays.asList(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
Iterator<List<Integer>> combinations = new Combinations<>(elements);
combinations.forEachRemaining(System.out::println);

输出:

[1, 2, 4]
[1, 2, 5]
[1, 2, 6]
[1, 3, 4]
[1, 3, 5]
[1, 3, 6]

注意:这使用List<List<T>>代替List<Set<T>>,因为您需要索引嵌套集合,但您可以轻松地将其更改为接受List<Collection<T>>并转换为List在构造函数中。

答案 1 :(得分:1)

编辑此答案以适用于List<List<T>>。 tobias_k的答案使用iterator通过迭代列表的索引来获得下一个组合。这样做类似,没有基于迭代器的方法。我遵循的想法是:

     * List 1: [1 2]
     * List 2: [4 5]
     * List 3: [6 7]
     * 
     * Take each element from list 1 and put each element 
     * in a separate list.
     * combinations -> [ [1] [2] ]
     * 
     * Set up something called newCombinations that will contains a list
     * of list of integers
     * Consider [1], then [2]
     * 
     * Now, take the next list [4 5] and iterate over integers
     * [1]
     *  add 4   -> [1 4]
     *      add to newCombinations -> [ [1 4] ]
     *  add 5   -> [1 5]
     *      add to newCombinations -> [ [1 4] [1 5] ]
     * 
     * [2]
     *  add 4   -> [2 4]
     *      add to newCombinations -> [ [1 4] [1 5] [2 4] ]
     *  add 5   -> [2 5]
     *      add to newCombinations -> [ [1 4] [1 5] [2 4] [2 5] ]
     * 
     * point combinations to newCombinations
     * combinations now looks like -> [ [1 4] [1 5] [2 4] [2 5] ]
     * Now, take the next list [6 7] and iterate over integers
     *  ....
     *  6 will go into each of the lists
     *      [ [1 4 6] [1 5 6] [2 4 6] [2 5 6] ]
     *  7 will go into each of the lists
     *      [ [1 4 6] [1 5 6] [2 4 6] [2 5 6] [1 4 7] [1 5 7] [2 4 7] [2 5 7]]

现在的代码。我使用Set只是为了摆脱任何重复。可以用List替换。一切都应该无缝地工作。 :)

public static <T> Set<List<T>> getCombinations(List<List<T>> lists) {
    Set<List<T>> combinations = new HashSet<List<T>>();
    Set<List<T>> newCombinations;

    int index = 0;

    // extract each of the integers in the first list
    // and add each to ints as a new list
    for(T i: lists.get(0)) {
        List<T> newList = new ArrayList<T>();
        newList.add(i);
        combinations.add(newList);
    }
    index++;
    while(index < lists.size()) {
        List<T> nextList = lists.get(index);
        newCombinations = new HashSet<List<T>>();
        for(List<T> first: combinations) {
            for(T second: nextList) {
                List<T> newList = new ArrayList<T>();
                newList.addAll(first);
                newList.add(second);
                newCombinations.add(newList);
            }
        }
        combinations = newCombinations;

        index++;
    }

    return combinations;
}

一个小试块..

public static void main(String[] args) {
    List<Integer> l1 = Arrays.asList(1,2,3);
    List<Integer> l2 = Arrays.asList(4,5);
    List<Integer> l3 = Arrays.asList(6,7);

    List<List<Integer>> lists = new ArrayList<List<Integer>>();
    lists.add(l1);
    lists.add(l2);
    lists.add(l3);

    Set<List<Integer>> combs = getCombinations(lists);
    for(List<Integer> list : combs) {
        System.out.println(list.toString());
    }

}