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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.io.Serializable;
  23. import java.util.Arrays;
  24. import java.util.Comparator;
  25. import java.util.Iterator;
  26. import java.util.NoSuchElementException;

  27. import org.hipparchus.exception.MathRuntimeException;

  28. /**
  29.  * Utility to create combinations {@code (n, k)} of {@code k} elements
  30.  * in a set of {@code n} elements.
  31.  *
  32.  * @see <a href="http://en.wikipedia.org/wiki/Combination">
  33.  * Combination @ Wikipedia</a>
  34.  */
  35. public class Combinations implements Iterable<int[]> {
  36.     /** Size of the set from which combinations are drawn. */
  37.     private final int n;
  38.     /** Number of elements in each combination. */
  39.     private final int k;
  40.     /** Iteration order. */
  41.     private final IterationOrder iterationOrder;

  42.     /**
  43.      * Describes the type of iteration performed by the
  44.      * {@link #iterator() iterator}.
  45.      */
  46.     private enum IterationOrder {
  47.         /** Lexicographic order. */
  48.         LEXICOGRAPHIC
  49.     }

  50.    /**
  51.      * Creates an instance whose range is the k-element subsets of
  52.      * {0, ..., n - 1} represented as {@code int[]} arrays.
  53.      * <p>
  54.      * The iteration order is lexicographic: the arrays returned by the
  55.      * {@link #iterator() iterator} are sorted in descending order and
  56.      * they are visited in lexicographic order with significance from
  57.      * right to left.
  58.      * For example, {@code new Combinations(4, 2).iterator()} returns
  59.      * an iterator that will generate the following sequence of arrays
  60.      * on successive calls to
  61.      * {@code next()}:<br>
  62.      * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
  63.      * </p>
  64.      * If {@code k == 0} an iterator containing an empty array is returned;
  65.      * if {@code k == n} an iterator containing [0, ..., n - 1] is returned.
  66.      *
  67.      * @param n Size of the set from which subsets are selected.
  68.      * @param k Size of the subsets to be enumerated.
  69.      * @throws org.hipparchus.exception.MathIllegalArgumentException if {@code n < 0}.
  70.      * @throws org.hipparchus.exception.MathIllegalArgumentException if {@code k > n}.
  71.      */
  72.     public Combinations(int n,
  73.                         int k) {
  74.         this(n, k, IterationOrder.LEXICOGRAPHIC);
  75.     }

  76.     /**
  77.      * Creates an instance whose range is the k-element subsets of
  78.      * {0, ..., n - 1} represented as {@code int[]} arrays.
  79.      * <p>
  80.      * If the {@code iterationOrder} argument is set to
  81.      * {@link IterationOrder#LEXICOGRAPHIC}, the arrays returned by the
  82.      * {@link #iterator() iterator} are sorted in descending order and
  83.      * they are visited in lexicographic order with significance from
  84.      * right to left.
  85.      * For example, {@code new Combinations(4, 2).iterator()} returns
  86.      * an iterator that will generate the following sequence of arrays
  87.      * on successive calls to
  88.      * {@code next()}:<br>
  89.      * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
  90.      * </p>
  91.      * If {@code k == 0} an iterator containing an empty array is returned;
  92.      * if {@code k == n} an iterator containing [0, ..., n - 1] is returned.
  93.      *
  94.      * @param n Size of the set from which subsets are selected.
  95.      * @param k Size of the subsets to be enumerated.
  96.      * @param iterationOrder Specifies the {@link #iterator() iteration order}.
  97.      * @throws org.hipparchus.exception.MathIllegalArgumentException if {@code n < 0}.
  98.      * @throws org.hipparchus.exception.MathIllegalArgumentException if {@code k > n}.
  99.      */
  100.     private Combinations(int n,
  101.                          int k,
  102.                          IterationOrder iterationOrder) {
  103.         CombinatoricsUtils.checkBinomial(n, k);
  104.         this.n = n;
  105.         this.k = k;
  106.         this.iterationOrder = iterationOrder;
  107.     }

  108.     /**
  109.      * Gets the size of the set from which combinations are drawn.
  110.      *
  111.      * @return the size of the universe.
  112.      */
  113.     public int getN() {
  114.         return n;
  115.     }

  116.     /**
  117.      * Gets the number of elements in each combination.
  118.      *
  119.      * @return the size of the subsets to be enumerated.
  120.      */
  121.     public int getK() {
  122.         return k;
  123.     }

  124.     /** {@inheritDoc} */
  125.     @Override
  126.     public Iterator<int[]> iterator() {
  127.         if (k == 0 ||
  128.             k == n) {
  129.             return new SingletonIterator(k);
  130.         }

  131.         if (iterationOrder == IterationOrder.LEXICOGRAPHIC) {
  132.             return new LexicographicIterator(n, k);
  133.         } else {
  134.             throw MathRuntimeException.createInternalError(); // Should never happen.
  135.         }
  136.     }

  137.     /**
  138.      * Defines a lexicographic ordering of combinations.
  139.      * The returned comparator allows to compare any two combinations
  140.      * that can be produced by this instance's {@link #iterator() iterator}.
  141.      * Its {@code compare(int[],int[])} method will throw exceptions if
  142.      * passed combinations that are inconsistent with this instance:
  143.      * <ul>
  144.      *  <li>if the array lengths are not equal to {@code k},</li>
  145.      *  <li>if an element of the array is not within the interval [0, {@code n}).</li>
  146.      * </ul>
  147.      * @return a lexicographic comparator.
  148.      */
  149.     public Comparator<int[]> comparator() {
  150.         return new LexicographicComparator(n, k);
  151.     }

  152.     /**
  153.      * Lexicographic combinations iterator.
  154.      * <p>
  155.      * Implementation follows Algorithm T in <i>The Art of Computer Programming</i>
  156.      * Internet Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All
  157.      * Combinations</a>, D. Knuth, 2004.</p>
  158.      * <p>
  159.      * The degenerate cases {@code k == 0} and {@code k == n} are NOT handled by this
  160.      * implementation.  If constructor arguments satisfy {@code k == 0}
  161.      * or {@code k >= n}, no exception is generated, but the iterator is empty.
  162.      */
  163.     private static class LexicographicIterator implements Iterator<int[]> {
  164.         /** Size of subsets returned by the iterator */
  165.         private final int k;

  166.         /**
  167.          * c[1], ..., c[k] stores the next combination; c[k + 1], c[k + 2] are
  168.          * sentinels.
  169.          * <p>
  170.          * Note that c[0] is "wasted" but this makes it a little easier to
  171.          * follow the code.
  172.          */
  173.         private final int[] c;

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

  176.         /** Marker: smallest index such that c[j + 1] > j */
  177.         private int j;

  178.         /**
  179.          * Construct a CombinationIterator to enumerate k-sets from n.
  180.          * <p>
  181.          * NOTE: If {@code k === 0} or {@code k >= n}, the Iterator will be empty
  182.          * (that is, {@link #hasNext()} will return {@code false} immediately.
  183.          *
  184.          * @param n size of the set from which subsets are enumerated
  185.          * @param k size of the subsets to enumerate
  186.          */
  187.         LexicographicIterator(int n, int k) {
  188.             this.k = k;
  189.             c = new int[k + 3];
  190.             if (k == 0 || k >= n) {
  191.                 more = false;
  192.                 return;
  193.             }
  194.             // Initialize c to start with lexicographically first k-set
  195.             for (int i = 1; i <= k; i++) {
  196.                 c[i] = i - 1;
  197.             }
  198.             // Initialize sentinels
  199.             c[k + 1] = n;
  200.             c[k + 2] = 0;
  201.             j = k; // Set up invariant: j is smallest index such that c[j + 1] > j
  202.         }

  203.         /**
  204.          * {@inheritDoc}
  205.          */
  206.         @Override
  207.         public boolean hasNext() {
  208.             return more;
  209.         }

  210.         /**
  211.          * {@inheritDoc}
  212.          */
  213.         @Override
  214.         public int[] next() {
  215.             if (!more) {
  216.                 throw new NoSuchElementException();
  217.             }
  218.             // Copy return value (prepared by last activation)
  219.             final int[] ret = new int[k];
  220.             System.arraycopy(c, 1, ret, 0, k);

  221.             // Prepare next iteration
  222.             // T2 and T6 loop
  223.             int x = 0;
  224.             if (j > 0) {
  225.                 x = j;
  226.                 c[j] = x;
  227.                 j--;
  228.                 return ret;
  229.             }
  230.             // T3
  231.             if (c[1] + 1 < c[2]) {
  232.                 c[1]++;
  233.                 return ret;
  234.             } else {
  235.                 j = 2;
  236.             }
  237.             // T4
  238.             boolean stepDone = false;
  239.             while (!stepDone) {
  240.                 c[j - 1] = j - 2;
  241.                 x = c[j] + 1;
  242.                 if (x == c[j + 1]) {
  243.                     j++;
  244.                 } else {
  245.                     stepDone = true;
  246.                 }
  247.             }
  248.             // T5
  249.             if (j > k) {
  250.                 more = false;
  251.                 return ret;
  252.             }
  253.             // T6
  254.             c[j] = x;
  255.             j--;
  256.             return ret;
  257.         }

  258.         /**
  259.          * Not supported.
  260.          */
  261.         @Override
  262.         public void remove() {
  263.             throw new UnsupportedOperationException();
  264.         }
  265.     }

  266.     /**
  267.      * Iterator with just one element to handle degenerate cases (full array,
  268.      * empty array) for combination iterator.
  269.      */
  270.     private static class SingletonIterator implements Iterator<int[]> {
  271.         /** Singleton array */
  272.         private final int[] singleton;
  273.         /** True on initialization, false after first call to next */
  274.         private boolean more = true;
  275.         /**
  276.          * Create a singleton iterator providing the given array.
  277.          * @param k number of entries (i.e. entries will be 0..k-1)
  278.          */
  279.         SingletonIterator(final int k) {
  280.             this.singleton = MathArrays.natural(k);
  281.         }
  282.         /** @return True until next is called the first time, then false */
  283.         @Override
  284.         public boolean hasNext() {
  285.             return more;
  286.         }
  287.         /** @return the singleton in first activation; throws NSEE thereafter */
  288.         @Override
  289.         public int[] next() {
  290.             if (more) {
  291.                 more = false;
  292.                 return singleton.clone();
  293.             } else {
  294.                 throw new NoSuchElementException();
  295.             }
  296.         }
  297.         /** Not supported */
  298.         @Override
  299.         public void remove() {
  300.             throw new UnsupportedOperationException();
  301.         }
  302.     }

  303.     /**
  304.      * Defines the lexicographic ordering of combinations, using
  305.      * the {@link #lexNorm(int[])} method.
  306.      */
  307.     private static class LexicographicComparator
  308.         implements Comparator<int[]>, Serializable {
  309.         /** Serializable version identifier. */
  310.         private static final long serialVersionUID = 20130906L;
  311.         /** Size of the set from which combinations are drawn. */
  312.         private final int n;
  313.         /** Number of elements in each combination. */
  314.         private final int k;

  315.         /**
  316.          * @param n Size of the set from which subsets are selected.
  317.          * @param k Size of the subsets to be enumerated.
  318.          */
  319.         LexicographicComparator(int n, int k) {
  320.             this.n = n;
  321.             this.k = k;
  322.         }

  323.         /**
  324.          * {@inheritDoc}
  325.          *
  326.          * @throws org.hipparchus.exception.MathIllegalArgumentException
  327.          * if the array lengths are not equal to {@code k}.
  328.          * @throws org.hipparchus.exception.MathIllegalArgumentException
  329.          * if an element of the array is not within the interval [0, {@code n}).
  330.          */
  331.         @Override
  332.         public int compare(int[] c1, int[] c2) {
  333.             MathUtils.checkDimension(c1.length, k);
  334.             MathUtils.checkDimension(c2.length, k);

  335.             // Method "lexNorm" works with ordered arrays.
  336.             final int[] c1s = c1.clone();
  337.             Arrays.sort(c1s);
  338.             final int[] c2s = c2.clone();
  339.             Arrays.sort(c2s);

  340.             final long v1 = lexNorm(c1s);
  341.             final long v2 = lexNorm(c2s);

  342.             if (v1 < v2) {
  343.                 return -1;
  344.             } else if (v1 > v2) {
  345.                 return 1;
  346.             } else {
  347.                 return 0;
  348.             }
  349.         }

  350.         /**
  351.          * Computes the value (in base 10) represented by the digit
  352.          * (interpreted in base {@code n}) in the input array in reverse
  353.          * order.
  354.          * For example if {@code c} is {@code {3, 2, 1}}, and {@code n}
  355.          * is 3, the method will return 18.
  356.          *
  357.          * @param c Input array.
  358.          * @return the lexicographic norm.
  359.          * @throws org.hipparchus.exception.MathIllegalArgumentException
  360.          * if an element of the array is not within the interval [0, {@code n}).
  361.          */
  362.         private long lexNorm(int[] c) {
  363.             long ret = 0;
  364.             for (int i = 0; i < c.length; i++) {
  365.                 final int digit = c[i];
  366.                 MathUtils.checkRangeInclusive(digit, 0, n - 1);

  367.                 ret += c[i] * ArithmeticUtils.pow(n, i);
  368.             }
  369.             return ret;
  370.         }
  371.     }
  372. }