import java.util.Arrays; public class CombinatorialEnumerations { // subclass and implement count() method public static abstract class AbstractEnumeration { // range of definition of sequence elements protected final int range; // length of generated sequences protected final int length; protected AbstractEnumeration(int range, int length) { this.range = range; this.length = length; } // returns number of combinatorial sequences starting with prefix // by contract only the last element of prefix can be invalid and in this case 0 must be returned protected abstract long count(int[] prefix); public int[] next(int[] sequence) { return fromNumber(toNumber(sequence) + 1); } public long totalCount() { return count(new int[0]); } public long toNumber(int[] sequence) { long res = 0; for (int i = 0; i < sequence.length; i++) { int[] prefix = Arrays.copyOf(sequence, i + 1); for (prefix[i] = 0; prefix[i] < sequence[i]; ++prefix[i]) { res += count(prefix); } } return res; } public int[] fromNumber(long number) { int[] sequence = new int[length]; for (int i = 0; i < sequence.length; i++) { int[] prefix = Arrays.copyOf(sequence, i + 1); for (prefix[i] = 0; prefix[i] < range; ++prefix[i]) { long cur = count(prefix); if (number < cur) { break; } number -= cur; } sequence[i] = prefix[i]; } return sequence; } public void enumerate() { System.out.println(getClass().getName()); long total = totalCount(); for (long i = 0; i < total; i++) { int[] p = fromNumber(i); System.out.println(i + " " + Arrays.toString(p)); long j = toNumber(p); if (i != j) throw new RuntimeException(); } } } public static class Arrangements extends AbstractEnumeration { public Arrangements(int n, int k) { super(n, k); } @Override protected long count(int[] prefix) { int size = prefix.length; // if the last element appears twice, then prefix is invalid and 0 must be returned for (int i = 0; i < size - 1; i++) if (prefix[size - 1] == prefix[i]) return 0; long res = 1; for (int i = 0; i < length - size; i++) res *= range - size - i; return res; } } public static class Permutations extends Arrangements { public Permutations(int n) { super(n, n); } } public static class Combinations extends AbstractEnumeration { final long[][] binomial; public Combinations(int n, int k) { super(n, k); binomial = new long[n + 1][n + 1]; // calculate binomial coefficients in advance for (int i = 0; i <= n; i++) for (int j = 0; j <= i; j++) binomial[i][j] = (j == 0) ? 1 : binomial[i - 1][j - 1] + binomial[i - 1][j]; } @Override protected long count(int[] prefix) { int size = prefix.length; // if there is no combination with given prefix, 0 must be returned. // by contract only the last element can make prefix invalid. if (size >= 2 && prefix[size - 1] <= prefix[size - 2]) return 0; // prefix is valid. return the number of combinations starting with prefix int last = size > 0 ? prefix[size - 1] : -1; return binomial[range - 1 - last][length - size]; } } public static class CorrectBracketSequences extends AbstractEnumeration { final long[][] d; // sequenceLength must be a multiple of 2 public CorrectBracketSequences(int sequenceLength) { super(2, sequenceLength); d = new long[sequenceLength + 1][sequenceLength / 2 + 1]; // d[i][j] - number of bracket sequences of length i with balance j d[0][0] = 1; for (int i = 1; i <= sequenceLength; i++) { for (int balance = 0; balance <= sequenceLength / 2; balance++) { if (balance - 1 >= 0) d[i][balance] += d[i - 1][balance - 1]; if (balance + 1 <= sequenceLength / 2) d[i][balance] += d[i - 1][balance + 1]; } } } @Override protected long count(int[] prefix) { int size = prefix.length; int balance = 0; for (int cur : prefix) // 0 designates '(' // 1 designates ')' balance += cur == 0 ? 1 : -1; if (balance < 0 || balance > length - size) return 0; return d[length - size][balance]; } } public static class Partitions extends AbstractEnumeration { final long[][] pp; public Partitions(int value) { super(value + 1, value); long[][] p = new long[value + 1][value + 1]; // p[i][j] - number of partitions of i with largest summand equal to j p[0][0] = 1; for (int i = 1; i <= value; i++) for (int j = 1; j <= i; j++) p[i][j] = p[i - 1][j - 1] + p[i - j][j]; pp = new long[value + 1][value + 1]; for (int i = 1; i <= value; i++) for (int j = 1; j <= value; j++) pp[i][j] = p[i][j] + pp[i][j - 1]; } @Override protected long count(int[] prefix) { int size = prefix.length; int sum = 0; for (int e : prefix) sum += e; if (sum == range - 1) return 1; if (sum > range - 1 || size > 0 && prefix[size - 1] == 0 || size >= 2 && prefix[size - 1] > prefix[size - 2]) return 0; int last = size > 0 ? prefix[size - 1] : range - 1; return pp[range - 1 - sum][last]; } } public static void main(String[] args) { Permutations permutations = new Permutations(3); permutations.enumerate(); Combinations combinations = new Combinations(4, 3); combinations.enumerate(); Arrangements arrangements = new Arrangements(3, 2); arrangements.enumerate(); CorrectBracketSequences correctBracketSequences = new CorrectBracketSequences(6); correctBracketSequences.enumerate(); Partitions partitions = new Partitions(4); partitions.enumerate(); } }