/* * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Version 1.0, and under the Eclipse Public License, Version 1.0 * (http://h2database.com/html/license.html). * Initial Developer: H2 Group * * According to a mail from Alan Tucker to Chris H Miller from IBM, * the algorithm is in the public domain: * * Date: 2010-07-15 15:57 * Subject: Re: Applied Combinatorics Code * * Chris, * The combinatorics algorithms in my textbook are all not under patent * or copyright. They are as much in the public domain as the solution to any * common question in an undergraduate mathematics course, e.g., in my * combinatorics course, the solution to the problem of how many arrangements * there are of the letters in the word MATHEMATICS. I appreciate your due * diligence. * -Alan */ package org.h2.util; import org.h2.message.DbException; /** * A class to iterate over all permutations of an array. * The algorithm is from Applied Combinatorics, by Alan Tucker as implemented in * http://www.koders.com/java/fidD3445CD11B1DC687F6B8911075E7F01E23171553.aspx * * @param <T> the element type */ public class Permutations<T> { private T[] in; private T[] out; private int n, m; private int[] index; private boolean hasNext = true; private Permutations(T[] in, T[] out, int m) { this.n = in.length; this.m = m; if (n < m || m < 0) { DbException.throwInternalError("n < m or m < 0"); } this.in = in; this.out = out; index = new int[n]; for (int i = 0; i < n; i++) { index[i] = i; } // The elements from m to n are always kept ascending right to left. // This keeps the dip in the interesting region. reverseAfter(m - 1); } /** * Create a new permutations object. * * @param <T> the type * @param in the source array * @param out the target array * @return the generated permutations object */ public static <T> Permutations<T> create(T[] in, T[] out) { return new Permutations<T>(in, out, in.length); } /** * Create a new permutations object. * * @param <T> the type * @param in the source array * @param out the target array * @param m the number of output elements to generate * @return the generated permutations object */ public static <T> Permutations<T> create(T[] in, T[] out, int m) { return new Permutations<T>(in, out, m); } /** * Move the index forward a notch. The algorithm first finds the rightmost * index that is less than its neighbor to the right. This is the dip point. * The algorithm next finds the least element to the right of the dip that * is greater than the dip. That element is switched with the dip. Finally, * the list of elements to the right of the dip is reversed. * For example, in a permutation of 5 items, the index may be {1, 2, 4, 3, * 0}. The dip is 2 the rightmost element less than its neighbor on its * right. The least element to the right of 2 that is greater than 2 is 3. * These elements are swapped, yielding {1, 3, 4, 2, 0}, and the list right * of the dip point is reversed, yielding {1, 3, 0, 2, 4}. */ private void moveIndex() { // find the index of the first element that dips int i = rightmostDip(); if (i < 0) { hasNext = false; return; } // find the least greater element to the right of the dip int leastToRightIndex = i + 1; for (int j = i + 2; j < n; j++) { if (index[j] < index[leastToRightIndex] && index[j] > index[i]) { leastToRightIndex = j; } } // switch dip element with least greater element to its right int t = index[i]; index[i] = index[leastToRightIndex]; index[leastToRightIndex] = t; if (m - 1 > i) { // reverse the elements to the right of the dip reverseAfter(i); // reverse the elements to the right of m - 1 reverseAfter(m - 1); } } /** * Get the index of the first element from the right that is less * than its neighbor on the right. * * @return the index or -1 if non is found */ private int rightmostDip() { for (int i = n - 2; i >= 0; i--) { if (index[i] < index[i + 1]) { return i; } } return -1; } /** * Reverse the elements to the right of the specified index. * * @param i the index */ private void reverseAfter(int i) { int start = i + 1; int end = n - 1; while (start < end) { int t = index[start]; index[start] = index[end]; index[end] = t; start++; end--; } } /** * Go to the next lineup, and if available, fill the target array. * * @return if a new lineup is available */ public boolean next() { if (!hasNext) { return false; } for (int i = 0; i < m; i++) { out[i] = in[index[i]]; } moveIndex(); return true; } }