【问题标题】:Combinatorics: generate all "states" - array combinations组合学:生成所有“状态”——数组组合
【发布时间】:2012-03-09 10:41:24
【问题描述】:

我有一个整数数组:n[]

另外,我有一个数组 (Nr[]) 包含 n.length 整数。我需要通过以下方式生成n[] 的所有组合:

/* let n.length == 3 and Nr[0] = 2, Nr[1] = 3, Nr[2] = 3 */
n = {0, 0, 0};
n = {1, 0, 0};
n = {2, 0, 0};
n = {0, 1, 0};
n = {0, 2, 0};
n = {0, 3, 0};
n = {0, 0, 1};
...
n = {1, 1, 0};
n = {1, 2, 0};
n = {1, 3, 0};
n = {2, 1, 0};
n = {2, 2, 0};
n = {2, 3, 0};
n = {1, 1, 1};
...
n = {0, 1, 1};
// many others

目标是找到n 的所有组合,其中n[i] 可以是0 to Nr[i]

我没有成功...如何在Java中解决它?或者不在 Java 中...

【问题讨论】:

  • 您的代码在哪里?哪条线路有问题?
  • 问题要大得多,我完全没有什么好的想法(

标签: java algorithm loops combinatorics


【解决方案1】:

您可能想使用recursion,尝试每个索引的所有可能性,然后使用子数组递归调用,“不使用”最后一个元素。

public static void printPermutations(int[] n, int[] Nr, int idx) {
    if (idx == n.length) {  //stop condition for the recursion [base clause]
        System.out.println(Arrays.toString(n));
        return;
    }
    for (int i = 0; i <= Nr[idx]; i++) { 
        n[idx] = i;
        printPermutations(n, Nr, idx+1); //recursive invokation, for next elements
    }
}

调用:

public static void main(String[] args) {
    /* let n.length == 3 and Nr[0] = 2, Nr[1] = 3, Nr[2] = 3 */
    int[] n = new int[3];
    int Nr[] = {2,3,3 };
    printPermutations(n, Nr, 0);
}

会得到你:

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

但是请注意 - 使用此方法会打印您描述的所有元素,但顺序与您的示例不同。

【讨论】:

  • 很好的答案,但是如何将其适应组合?
  • @dhomes:您的意思是不是类似于this thread 中所涵盖的内容[这是 php 而不是 java,但我提供了一个伪代码,显然也可以在 java 中实现]?还是您在寻找all permutations
  • 确实是所有组合!整个早上都在 Java 上尝试这个(只是学习它,我非常喜欢递归)。一直在尝试使用 Arrays.copyOfRange 什么都无济于事(ps:我不是计算机/数学学生)。感谢您这么快回复承认,我以为几个月前我不会收到回复
  • @dhomes:我希望链接的线程对您有所帮助。尝试实现这些想法,如果您遇到任何问题 - 使用您的特定代码和问题描述提出您自己的问题 - 请记住,使用代码提出的问题(即使不工作)可能会得到更好的答案!
【解决方案2】:

注意: 与问题逻辑不同,以下代码是高位排他的,正如 Java 中的标准一样,例如3 的输入将从 0 到 2(含)计数,而不是从 0 到 3。

这可以在没有递归的情况下完成:

public static void printPermutations(int... size) {
    int total = 1;
    for (int i : size)
        total *= i;
    int[] n = new int[size.length];
    for (int value = 0; value < total; value++) {
        int remain = value;
        for (int i = size.length - 1; i >= 0; i--) {
            n[i] = remain % size[i];
            remain /= size[i];
        }
        System.out.println(Arrays.toString(n));
    }
}

测试

printPermutations(2, 3, 3);

输出

[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 1, 0]
[0, 1, 1]
[0, 1, 2]
[0, 2, 0]
[0, 2, 1]
[0, 2, 2]
[1, 0, 0]
[1, 0, 1]
[1, 0, 2]
[1, 1, 0]
[1, 1, 1]
[1, 1, 2]
[1, 2, 0]
[1, 2, 1]
[1, 2, 2]

作为 Java 8 流的练习,这里有一个用于迭代或流式传输排列的类。

使用方法:

// Using Iterator
for (int[] n : Permutations.iterable(2, 3, 3))
    System.out.println(Arrays.toString(n));

// Using streams
Permutations.stream(2, 3, 3)
            .parallel()
            .map(Arrays::toString) // this will be done in parallel
            .forEachOrdered(System.out::println);

// Getting all
int[][] results = Permutations.get(2, 3, 3);
for (int[] n : results)
    System.out.println(Arrays.toString(n));

所有三个都产生与上述相同的输出。

代码如下:

class Permutations implements Spliterator<int[]>, Iterator<int[]> {

    public static Stream<int[]> stream(int... sizes) {
        return StreamSupport.stream(spliterator(sizes), false);
    }

    public static Spliterator<int[]> spliterator(int... sizes) {
        long total = sum(sizes);
        return (total == 0 ? Spliterators.emptySpliterator() : new Permutations(sizes.clone(), 0, total));
    }

    public static Iterable<int[]> iterable(int... sizes) {
        long total = sum(sizes);
        if (total == 0)
            return Collections.emptyList();
        int[] clonedSizes = sizes.clone();
        return new Iterable<int[]>() {
            @Override public Iterator<int[]> iterator() { return new Permutations(clonedSizes, 0, total); }
            @Override public Spliterator<int[]> spliterator() { return new Permutations(clonedSizes, 0, total); }
        };
    }

    public static int[][] get(int... sizes) {
        long total = sum(sizes);
        if (total == 0)
            return new int[0][];
        if (total > Integer.MAX_VALUE)
            throw new IllegalArgumentException("Invalid sizes (overflow): " + Arrays.toString(sizes));
        Permutations generator = new Permutations(sizes.clone(), 0, total);
        int[][] result = new int[(int) total][];
        for (int i = 0; i < result.length; i++)
            result[i] = generator.next();
        return result;
    }

    private static long sum(int[] sizes) {
        long total = 1;
        for (int size : sizes) {
            if (size < 0)
                throw new IllegalArgumentException("Invalid size: " + size);
            try {
                total = Math.multiplyExact(total, size); // Java 8+: Fail on overflow
            } catch (@SuppressWarnings("unused") ArithmeticException e) {
                throw new IllegalArgumentException("Invalid sizes (overflow): " + Arrays.toString(sizes));
            }
        }
        return total;
    }

    private final int[] sizes;
    private final long  end;
    private long        next;

    Permutations(int[] sizes, long start, long end) {
        this.sizes = sizes;
        this.end = end;
        this.next = start;
    }

    @Override
    public boolean hasNext() {
        return (this.next < this.end);
    }

    @Override
    public int[] next() {
        if (this.next == this.end)
            throw new NoSuchElementException();
        long value = this.next++;
        int[] arr = new int[this.sizes.length];
        for (int i = arr.length - 1; i >= 0; i--) {
            arr[i] = (int) (value % this.sizes[i]);
            value /= this.sizes[i];
        }
        return arr;
    }

    @Override
    public int characteristics() {
        // Note: Can easily be made SORTED by implementing a Comparator<int[]>
        return ORDERED | DISTINCT | NONNULL | IMMUTABLE | SIZED | SUBSIZED; // not SORTED or CONCURRENT
    }

    @Override
    public long estimateSize() {
        return this.end - this.next;
    }

    @Override
    public boolean tryAdvance(Consumer<? super int[]> action) {
        if (this.next == this.end)
            return false;
        action.accept(next());
        return true;
    }

    @Override
    public Spliterator<int[]> trySplit() {
        if (this.next > this.end - 2)
            return null;
        long split = (this.end - this.next) / 2 + this.next;
        Permutations prefix = new Permutations(this.sizes, this.next, split);
        this.next = split;
        return prefix;
    }

    @Override
    public void forEachRemaining(Consumer<? super int[]> action) {
        Spliterator.super.forEachRemaining(action);
    }

}

【讨论】:

  • 谢谢@Andreas。我有机会计算排列指数吗?例如给定 [1 0 2] 将返回 11 的偏移量(索引)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 2013-06-19
  • 1970-01-01
  • 2016-08-16
  • 2013-07-30
  • 1970-01-01
相关资源
最近更新 更多