Combinations.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.numbers.combinatorics;

  18. import java.io.Serializable;
  19. import java.util.Arrays;
  20. import java.util.NoSuchElementException;
  21. import java.util.Iterator;
  22. import java.util.Comparator;

  23. import org.apache.commons.numbers.core.ArithmeticUtils;

  24. /**
  25.  * Utility to create <a href="http://en.wikipedia.org/wiki/Combination">
  26.  * combinations</a> {@code (n, k)} of {@code k} elements in a set of
  27.  * {@code n} elements.
  28.  */
  29. public final class Combinations implements Iterable<int[]> {
  30.     /** Size of the set from which combinations are drawn. */
  31.     private final int n;
  32.     /** Number of elements in each combination. */
  33.     private final int k;

  34.     /**
  35.      * @param n Size of the set from which subsets are selected.
  36.      * @param k Size of the subsets to be enumerated.
  37.      * @throws IllegalArgumentException if {@code n < 0}.
  38.      * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}.
  39.      */
  40.     private Combinations(int n,
  41.                          int k) {
  42.         BinomialCoefficient.checkBinomial(n, k);
  43.         this.n = n;
  44.         this.k = k;
  45.     }

  46.     /**
  47.      * Create an instance.
  48.      *
  49.      * @param n Size of the set from which subsets are selected.
  50.      * @param k Size of the subsets to be enumerated.
  51.      * @throws IllegalArgumentException if {@code n < 0}.
  52.      * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}.
  53.      * @return a new instance.
  54.      */
  55.     public static Combinations of(int n,
  56.                                   int k) {
  57.         return new Combinations(n, k);
  58.     }

  59.     /**
  60.      * Gets the size of the set from which combinations are drawn.
  61.      *
  62.      * @return the size of the universe.
  63.      */
  64.     public int getN() {
  65.         return n;
  66.     }

  67.     /**
  68.      * Gets the number of elements in each combination.
  69.      *
  70.      * @return the size of the subsets to be enumerated.
  71.      */
  72.     public int getK() {
  73.         return k;
  74.     }

  75.     /**
  76.      * Creates an iterator whose range is the k-element subsets of
  77.      * {0, ..., n - 1} represented as {@code int[]} arrays.
  78.      * <p>
  79.      * The iteration order is lexicographic: the arrays returned by the
  80.      * {@link #iterator() iterator} are sorted in descending order and
  81.      * they are visited in lexicographic order with significance from
  82.      * right to left.
  83.      * For example, {@code new Combinations(4, 2).iterator()} returns
  84.      * an iterator that will generate the following sequence of arrays
  85.      * on successive calls to
  86.      * {@code next()}:<br>
  87.      * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
  88.      * </p>
  89.      * If {@code k == 0} an iterator containing an empty array is returned;
  90.      * if {@code k == n} an iterator containing [0, ..., n - 1] is returned.
  91.      */
  92.     @Override
  93.     public Iterator<int[]> iterator() {
  94.         return k == 0 || k == n ?
  95.             new SingletonIterator(k) :
  96.             new LexicographicIterator(n, k);
  97.     }

  98.     /**
  99.      * Creates a comparator.
  100.      * When performing a comparison, if an element of the array is not
  101.      * within the interval [0, {@code n}), an {@code IllegalArgumentException}
  102.      * will be thrown.
  103.      *
  104.      * @return a comparator.
  105.      */
  106.     public Comparator<int[]> comparator() {
  107.         return new LexicographicComparator(n, k);
  108.     }

  109.     /**
  110.      * Lexicographic combinations iterator.
  111.      * <p>
  112.      * Implementation follows Algorithm T in <i>The Art of Computer Programming</i>
  113.      * Internet Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All
  114.      * Combinations, D. Knuth, 2004.</p>
  115.      * <p>
  116.      * The degenerate cases {@code k == 0} and {@code k == n} are NOT handled by this
  117.      * implementation. It is assumed that {@code n > k > 0}.
  118.      * </p>
  119.      */
  120.     private static class LexicographicIterator implements Iterator<int[]> {
  121.         /** Size of subsets returned by the iterator. */
  122.         private final int k;

  123.         /**
  124.          * c[1], ..., c[k] stores the next combination; c[k + 1], c[k + 2] are
  125.          * sentinels.
  126.          * <p>
  127.          * Note that c[0] is "wasted" but this makes it a little easier to
  128.          * follow the code.
  129.          * </p>
  130.          */
  131.         private final int[] c;

  132.         /** Return value for {@link #hasNext()}. */
  133.         private boolean more = true;

  134.         /** Marker: smallest index such that {@code c[j + 1] > j}. */
  135.         private int j;

  136.         /**
  137.          * Construct a CombinationIterator to enumerate {@code k}-sets from a set
  138.          * of size {@code n}.
  139.          * <p>
  140.          * NOTE: It is assumed that {@code n > k > 0}.
  141.          * </p>
  142.          *
  143.          * @param n Size of the set from which subsets are enumerated.
  144.          * @param k Size of the subsets to enumerate.
  145.          */
  146.         LexicographicIterator(int n, int k) {
  147.             this.k = k;
  148.             c = new int[k + 3];
  149.             // Initialize c to start with lexicographically first k-set
  150.             for (int i = 1; i <= k; i++) {
  151.                 c[i] = i - 1;
  152.             }
  153.             // Initialize sentinels
  154.             c[k + 1] = n;
  155.             c[k + 2] = 0;
  156.             j = k; // Set up invariant: j is smallest index such that c[j + 1] > j
  157.         }

  158.         /**
  159.          * {@inheritDoc}
  160.          */
  161.         @Override
  162.         public boolean hasNext() {
  163.             return more;
  164.         }

  165.         /**
  166.          * {@inheritDoc}
  167.          */
  168.         @Override
  169.         public int[] next() {
  170.             if (!more) {
  171.                 throw new NoSuchElementException();
  172.             }
  173.             // Copy return value (prepared by last activation)
  174.             final int[] ret = new int[k];
  175.             System.arraycopy(c, 1, ret, 0, k);

  176.             // Prepare next iteration
  177.             // T2 and T6 loop
  178.             int x = 0;
  179.             if (j > 0) {
  180.                 x = j;
  181.                 c[j] = x;
  182.                 j--;
  183.                 return ret;
  184.             }
  185.             // T3
  186.             if (c[1] + 1 < c[2]) {
  187.                 c[1]++;
  188.                 return ret;
  189.             } else {
  190.                 j = 2;
  191.             }
  192.             // T4
  193.             boolean stepDone = false;
  194.             while (!stepDone) {
  195.                 c[j - 1] = j - 2;
  196.                 x = c[j] + 1;
  197.                 if (x == c[j + 1]) {
  198.                     j++;
  199.                 } else {
  200.                     stepDone = true;
  201.                 }
  202.             }
  203.             // T5
  204.             if (j > k) {
  205.                 more = false;
  206.                 return ret;
  207.             }
  208.             // T6
  209.             c[j] = x;
  210.             j--;
  211.             return ret;
  212.         }

  213.         /**
  214.          * Not supported.
  215.          */
  216.         @Override
  217.         public void remove() {
  218.             throw new UnsupportedOperationException();
  219.         }
  220.     }

  221.     /**
  222.      * Iterator with just one element to handle degenerate cases (full array,
  223.      * empty array) for combination iterator.
  224.      */
  225.     private static class SingletonIterator implements Iterator<int[]> {
  226.         /** Number of elements of the singleton array. */
  227.         private final int n;
  228.         /** True on initialization, false after first call to next. */
  229.         private boolean more = true;
  230.         /**
  231.          * Create a singleton iterator providing the given array.
  232.          *
  233.          * @param n Size of the singleton array returned by the iterator.
  234.          */
  235.         SingletonIterator(final int n) {
  236.             this.n = n;
  237.         }
  238.         /**
  239.          * @return {@code true} until next is called the first time,
  240.          * then {@code false}.
  241.          **/
  242.         @Override
  243.         public boolean hasNext() {
  244.             return more;
  245.         }
  246.         /**
  247.          * @return the singleton at the first activation.
  248.          * @throws NoSuchElementException after the first activation.
  249.          */
  250.         @Override
  251.         public int[] next() {
  252.             if (more) {
  253.                 more = false;
  254.                 // Create singleton.
  255.                 final int[] s = new int[n];
  256.                 for (int i = 0; i < n; i++) {
  257.                     s[i] = i;
  258.                 }
  259.                 return s;
  260.             } else {
  261.                 throw new NoSuchElementException();
  262.             }
  263.         }
  264.         /**
  265.          * Unsupported.
  266.          *
  267.          * @throws UnsupportedOperationException Remove is not supported.
  268.          */
  269.         @Override
  270.         public void remove() {
  271.             throw new UnsupportedOperationException();
  272.         }
  273.     }

  274.     /**
  275.      * Defines a lexicographic ordering of the combinations.
  276.      *
  277.      * The comparison is based on the value (in base 10) represented
  278.      * by the digit (interpreted in base {@code n}) in the input array,
  279.      * in reverse order.
  280.      */
  281.     private static class LexicographicComparator
  282.         implements Comparator<int[]>,
  283.                    Serializable {
  284.         /** Serializable version identifier. */
  285.         private static final long serialVersionUID = 20170520L;
  286.         /** Size of the set from which combinations are drawn. */
  287.         private final int n;
  288.         /** Number of elements in each combination. */
  289.         private final int k;

  290.         /**
  291.          * @param n Size of the set from which subsets are selected.
  292.          * @param k Size of the subsets to be enumerated.
  293.          */
  294.         LexicographicComparator(int n,
  295.                                 int k) {
  296.             this.n = n;
  297.             this.k = k;
  298.         }

  299.         /**
  300.          * {@inheritDoc}
  301.          *
  302.          * @throws IllegalArgumentException if the array lengths are not
  303.          * equal to {@code k}.
  304.          * @throws IllegalArgumentException if an element of the array is not
  305.          * within the interval [0, {@code n}).
  306.          */
  307.         @Override
  308.         public int compare(int[] c1,
  309.                            int[] c2) {
  310.             if (c1.length != k) {
  311.                 throw new CombinatoricsException(CombinatoricsException.MISMATCH, k, c1.length);
  312.             }
  313.             if (c2.length != k) {
  314.                 throw new CombinatoricsException(CombinatoricsException.MISMATCH, k, c2.length);
  315.             }

  316.             // Method "lexNorm" works with ordered arrays.
  317.             final int[] c1s = Arrays.copyOf(c1, k);
  318.             final int[] c2s = Arrays.copyOf(c2, k);
  319.             Arrays.sort(c1s);
  320.             Arrays.sort(c2s);

  321.             final long v1 = lexNorm(c1s);
  322.             final long v2 = lexNorm(c2s);

  323.             return Long.compare(v1, v2);
  324.         }

  325.         /**
  326.          * Computes the value (in base 10) represented by the digit
  327.          * (interpreted in base {@code n}) in the input array in reverse
  328.          * order.
  329.          * For example if {@code c} is {@code {3, 2, 1}}, and {@code n}
  330.          * is 3, the method will return 18.
  331.          *
  332.          * @param c Input array.
  333.          * @return the lexicographic norm.
  334.          * @throws IllegalArgumentException if an element of the array is not
  335.          * within the interval [0, {@code n}).
  336.          */
  337.         private long lexNorm(int[] c) {
  338.             long ret = 0;
  339.             for (int i = 0; i < c.length; i++) {
  340.                 final int digit = c[i];
  341.                 if (digit < 0 ||
  342.                     digit >= n) {
  343.                     throw new CombinatoricsException(CombinatoricsException.OUT_OF_RANGE, digit, 0, n - 1);
  344.                 }

  345.                 ret += c[i] * ArithmeticUtils.pow((long) n, i);
  346.             }
  347.             return ret;
  348.         }
  349.     }
  350. }