懒惰地获取n组的所有k组合

时间:2014-12-02 09:02:54

标签: java combinations combinatorics

给出一个大的1-d数组。 为了节省大数组的内存,我需要在循环中获得下一个k组合(组合学中的C(n,k)),懒惰

由于问题不小,我很难实施。

这是我正在尝试做的伪代码:

public void doSomething(int[] array, int k) {

  for(current combinations in combinations(i, k)) {
     ... // do something
  }
  ...
}

更新 请参阅下面的解决方案。

3 个答案:

答案 0 :(得分:0)

我在C ++中做了类似的事情。我所做的是实现一个迭代器,它将系统生成的组合序列中的迭代位置作为其状态信息。递增迭代器会更改状态信息以指示下一个组合。取消引用迭代器计算序列中当前位置的组合。

答案 1 :(得分:0)

我假设问题被赋予一组值{x1,x2,x3,...,xn}生成{xi,xii,xiii,...,xk}的所有可能值。这是一个难以编程的问题......但是模式非常简单。我假设所有{x1,x2,x3,... xn}都是不同的(否则它会更难)。假设所有xn都是不同的,那么有一个非常简单的模式来生成所有可能的k = 2集:

public class Set{
    private final int[] set;
    public Set(final int ...set){
        this.set = new int[set.length];
        for(int i = 0; i < set.length; ++i)
            this.set[i] = set[i];
    }
}

如果您知道您想要多少组合,那么找到该组合的组合很简单,这里是一个例子:

public int[][] get2Comb(final int[] set){
    int combIndex = 0;
    final int[][] comb = new int[comb2(set.length)][2];
    for(int i = 0; i < set.length - 1; ++i)
        for(int j = i + 1; j < set.length; ++j){
            comb[combIndex][0] = i;
            comnb[combIndex++][1] = j;
        }
    return comb;

}

对于k = 3的设置,它必须如下所示:

public int[][] get3Comb(final int[] set){
    int combIndex = 0;
    final int[][] comb = new int[comb3(set.length)][3];
    for(int i = 0; i < set.length - 2; ++i)
        for(int j = i + 1; j < set.length - 1; ++j)
            for(k = j + 1; k < set.length; ++k)
                comb[combIndex][0] = i;
                comb[combIndex][1] = j;
                comnb[combIndex++][2] = k;
            }
    return comb;

}

你看到它显然是recurses ...我发现不太可能存在迭代方案(尽管技术上总是存在)。我曾经遇到过像这样的问题,我的解决方案是生成代码而不是试图找出递归解决方案(我怀疑应该存在)。 Java反射当然是一个想法,但这基本上是生成代码 - 所以应该有一个更好的解决方案。

答案 2 :(得分:-1)

我在Groovy中实现了一个懒惰的迭代器,它不需要额外的内存或初步计算(只有长度为k的数组用于跟踪索引,这些索引的长度通常非常小)。 如果愿意,您可以轻松地将其转换为Java代码。

Lazy Iterator用于选择所有C(n,k)组合:

import sun.reflect.generics.reflectiveObjects.NotImplementedException

class Combinations implements Iterator {

    int[] indices
    def itemset
    def choose
    boolean hasNext = true

    Combinations(def itemset, int choose) {
        this.choose = choose
        this.itemset = itemset

        //Initialize indices
        indices = new int[choose]
        for (i in 0..<choose) {
            indices[i] = i
        }
    }

    private def prepareNext() {
        int rightMostIndex = { /* Closure to find the right-most index */
            for (i in choose-1..0){
                int bounds = itemset.size() - choose + i
                if (indices[i] < bounds) return i
            }
            return -1
        }() // execute closure

        // increment all indices
        if (rightMostIndex >= 0) {
            indices[rightMostIndex]++
            for (i in rightMostIndex+1..<choose) {
                indices[i] = indices[i-1] + 1;
            }
            // there are still more combinations
            hasNext = true
            return
        }
        // reached the end, no more combinations
        hasNext = false
    }

    @Override
    boolean hasNext() {
        return hasNext
    }

    @Override
    def next() {
        if (!hasNext)
            throw new NoSuchElementException();

        def combination = []

        for (i in 0..<indices.size()) {
            combination << itemset[indices[i]]
        }
        prepareNext()
        return combination
    }

    @Override
    public void remove() {
        throw new NotImplementedException()
    }
}

用法示例:

        def c = new Combinations([1, 2, 3], 2)
        for (e in c) {
            println e
        }

<强>输出:

[1, 2]
[1, 3]
[2, 3]