8

I have an array of integers: n[].

Also, I have an array (Nr[]) contains n.length integers. I need to generate all combinations of n[] in a following way:

/* 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

The goal is to find all combinations of n, where n[i] can be 0 to Nr[i].

I did not succeed... How to solve it in Java? Or not in Java...

amit
  • 175,853
  • 27
  • 231
  • 333
ivkremer
  • 1,234
  • 3
  • 23
  • 42

2 Answers2

8

You might want to use recursion, try all possibilities for each index, and recursively invoke with the subarray, "without" the last element.

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

invoke with:

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

will get you:

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

Note however - that using this method does print all elements as you describes, but in a different order then your example.

amit
  • 175,853
  • 27
  • 231
  • 333
  • excellent answer, but how could one adapt this to combinations? – David Homes Jul 01 '12 at 17:28
  • @dhomes: Do you mean something similar to what covered in [this thread](http://stackoverflow.com/a/10149671/572670) [this is php and not java, but I provided a pseudo code, which can obviously be implemented in java as well]? Or are you looking for [all permutations](http://stackoverflow.com/a/9148661/572670)? – amit Jul 01 '12 at 17:46
  • all combinations indeed! been trying this on Java (just learning it and I'm so so at recursion) all morning. Been trying to use Arrays.copyOfRange and what not to no avail (p.s.: I'm not a computer / math student). Thanks for replying so fast admit, i thought i wouldn't receive a reply being this thread a few months old – David Homes Jul 01 '12 at 17:55
  • @dhomes: I hope the linked thread helps you. Try to implement these ideas, and if you are having any troubles - ask your own question, with your specific code, and the problem description - remember that questions with code (even not working) are likely to get much better answers! – amit Jul 01 '12 at 18:01
4

Note: Unlike the question logic, the following code is upper-exclusive, as is the standard in Java, e.g. input of 3 will count from 0 to 2 (inclusive), not 0 to 3.

This can be done without recursion like this:

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

Test

printPermutations(2, 3, 3);

Output

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

As an exercise in Java 8 streams, here is a class for iterating or streaming the permutations.

How to use:

// 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));

All three produce the same output as above.

Here is the code:

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
  • 154,647
  • 11
  • 152
  • 247
  • Thanks @Andreas. Any chance I can calculate index of permutation? For example given [1 0 2] will return offset (index) of 11. – t4dohx Apr 22 '20 at 20:18