All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.numbers.combinatorics.Combinations Maven / Gradle / Ivy

Go to download

Statistical sampling library for use in virtdata libraries, based on apache commons math 4

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

package org.apache.commons.numbers.combinatorics;

import java.io.Serializable;
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Iterator;
import java.util.Comparator;

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

/**
 * Utility to create 
 * combinations {@code (n, k)} of {@code k} elements in a set of
 * {@code n} elements.
 */
public class Combinations implements Iterable {
    /** Size of the set from which combinations are drawn. */
    private final int n;
    /** Number of elements in each combination. */
    private final int k;
    /** Iteration order. */
    private final IterationOrder iterationOrder;

    /**
     * Describes the type of iteration performed by the
     * {@link #iterator() iterator}.
     */
    private enum IterationOrder {
        /** Lexicographic order. */
        LEXICOGRAPHIC
    }

   /**
     * Creates an instance whose range is the k-element subsets of
     * {0, ..., n - 1} represented as {@code int[]} arrays.
     * 

* The iteration order is lexicographic: the arrays returned by the * {@link #iterator() iterator} are sorted in descending order and * they are visited in lexicographic order with significance from * right to left. * For example, {@code new Combinations(4, 2).iterator()} returns * an iterator that will generate the following sequence of arrays * on successive calls to * {@code next()}:
* {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]} *

* If {@code k == 0} an iterator containing an empty array is returned; * if {@code k == n} an iterator containing [0, ..., n - 1] is returned. * * @param n Size of the set from which subsets are selected. * @param k Size of the subsets to be enumerated. * @throws IllegalArgumentException if {@code n < 0}. * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}. */ public Combinations(int n, int k) { this(n, k, IterationOrder.LEXICOGRAPHIC); } /** * Creates an instance whose range is the k-element subsets of * {0, ..., n - 1} represented as {@code int[]} arrays. *

* If the {@code iterationOrder} argument is set to * {@link IterationOrder#LEXICOGRAPHIC}, the arrays returned by the * {@link #iterator() iterator} are sorted in descending order and * they are visited in lexicographic order with significance from * right to left. * For example, {@code new Combinations(4, 2).iterator()} returns * an iterator that will generate the following sequence of arrays * on successive calls to * {@code next()}:
* {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]} *

* If {@code k == 0} an iterator containing an empty array is returned; * if {@code k == n} an iterator containing [0, ..., n - 1] is returned. * * @param n Size of the set from which subsets are selected. * @param k Size of the subsets to be enumerated. * @param iterationOrder Specifies the {@link #iterator() iteration order}. * @throws IllegalArgumentException if {@code n < 0}. * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}. */ private Combinations(int n, int k, IterationOrder iterationOrder) { BinomialCoefficient.checkBinomial(n, k); this.n = n; this.k = k; this.iterationOrder = iterationOrder; } /** * Gets the size of the set from which combinations are drawn. * * @return the size of the universe. */ public int getN() { return n; } /** * Gets the number of elements in each combination. * * @return the size of the subsets to be enumerated. */ public int getK() { return k; } /** {@inheritDoc} */ @Override public Iterator iterator() { if (k == 0 || k == n) { return new SingletonIterator(k); } switch (iterationOrder) { case LEXICOGRAPHIC: return new LexicographicIterator(n, k); default: // Should never happen. throw new UnsupportedOperationException("Please file a bug report"); } } /** * Lexicographic combinations iterator. *

* Implementation follows Algorithm T in The Art of Computer Programming * Internet Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All * Combinations, D. Knuth, 2004.

*

* The degenerate cases {@code k == 0} and {@code k == n} are NOT handled by this * implementation. If constructor arguments satisfy {@code k == 0} * or {@code k >= n}, no exception is generated, but the iterator is empty. *

* */ private static class LexicographicIterator implements Iterator { /** Size of subsets returned by the iterator. */ private final int k; /** * c[1], ..., c[k] stores the next combination; c[k + 1], c[k + 2] are * sentinels. *

* Note that c[0] is "wasted" but this makes it a little easier to * follow the code. *

*/ private final int[] c; /** Return value for {@link #hasNext()} */ private boolean more = true; /** Marker: smallest index such that {@code c[j + 1] > j}. */ private int j; /** * Construct a CombinationIterator to enumerate {@code k}-sets from a set * of size {@code n}. *

* NOTE: If {@code k === 0} or {@code k >= n}, the Iterator will be empty * (that is, {@link #hasNext()} will return {@code false} immediately. *

* * @param n Size of the set from which subsets are enumerated. * @param k Size of the subsets to enumerate. */ LexicographicIterator(int n, int k) { this.k = k; c = new int[k + 3]; if (k == 0 || k >= n) { more = false; return; } // Initialize c to start with lexicographically first k-set for (int i = 1; i <= k; i++) { c[i] = i - 1; } // Initialize sentinels c[k + 1] = n; c[k + 2] = 0; j = k; // Set up invariant: j is smallest index such that c[j + 1] > j } /** * {@inheritDoc} */ @Override public boolean hasNext() { return more; } /** * {@inheritDoc} */ @Override public int[] next() { if (!more) { throw new NoSuchElementException(); } // Copy return value (prepared by last activation) final int[] ret = new int[k]; System.arraycopy(c, 1, ret, 0, k); // Prepare next iteration // T2 and T6 loop int x = 0; if (j > 0) { x = j; c[j] = x; j--; return ret; } // T3 if (c[1] + 1 < c[2]) { c[1]++; return ret; } else { j = 2; } // T4 boolean stepDone = false; while (!stepDone) { c[j - 1] = j - 2; x = c[j] + 1; if (x == c[j + 1]) { j++; } else { stepDone = true; } } // T5 if (j > k) { more = false; return ret; } // T6 c[j] = x; j--; return ret; } /** * Not supported. */ @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Iterator with just one element to handle degenerate cases (full array, * empty array) for combination iterator. */ private static class SingletonIterator implements Iterator { /** Number of elements of the singleton array. */ private final int n; /** True on initialization, false after first call to next */ private boolean more = true; /** * Create a singleton iterator providing the given array. * * @param n Size of the singleton array returned by the iterator. */ SingletonIterator(final int n) { this.n = n; } /** * @return {@code true} until next is called the first time, * then {@code false}. **/ @Override public boolean hasNext() { return more; } /** * @return the singleton at the first activation. * @throws NoSuchElementException after the first activation. */ @Override public int[] next() { if (more) { more = false; // Create singleton. final int[] s = new int[n]; for (int i = 0; i < n; i++) { s[i] = i; } return s; } else { throw new NoSuchElementException(); } } /** * Unsupported. * * @throws UnsupportedOperationException */ @Override public void remove() { throw new UnsupportedOperationException(); } } /** * Defines a lexicographic ordering of the combinations. * * The comparison is based on the value (in base 10) represented * by the digit (interpreted in base {@code n}) in the input array, * in reverse order. * For example if {@code c} is {@code {3, 2, 1}}, and {@code n} * is 3, the method will return 18. */ public static class LexicographicComparator implements Comparator, Serializable { /** Serializable version identifier. */ private static final long serialVersionUID = 20170520L; /** Size of the set from which combinations are drawn. */ private final int n; /** Number of elements in each combination. */ private final int k; /** * @param n Size of the set from which subsets are selected. * @param k Size of the subsets to be enumerated. */ public LexicographicComparator(int n, int k) { this.n = n; this.k = k; } /** * Gets the size of the set from which combinations are drawn. * * @return the size of the universe. */ public int getN() { return n; } /** * Gets the number of elements in each combination. * * @return the size of the subsets. */ public int getK() { return k; } /** * {@inheritDoc} * * @throws IllegalArgumentException if the array lengths are not * equal to {@code k}. * @throws IllegalArgumentException if an element of the array is not * within the interval [0, {@code n}). */ @Override public int compare(int[] c1, int[] c2) { if (c1.length != k) { throw new CombinatoricsException(CombinatoricsException.MISMATCH, c1.length, k); } if (c2.length != k) { throw new CombinatoricsException(CombinatoricsException.MISMATCH, c2.length, k); } // Method "lexNorm" works with ordered arrays. final int[] c1s = Arrays.copyOf(c1, k); final int[] c2s = Arrays.copyOf(c2, k); Arrays.sort(c1s); Arrays.sort(c2s); final long v1 = lexNorm(c1s); final long v2 = lexNorm(c2s); if (v1 < v2) { return -1; } else if (v1 > v2) { return 1; } else { return 0; } } /** * Computes the value (in base 10) represented by the digit * (interpreted in base {@code n}) in the input array in reverse * order. * * @param c Input array. * @return the lexicographic norm. * @throws IllegalArgumentException if an element of the array is not * within the interval [0, {@code n}). */ private long lexNorm(int[] c) { long ret = 0; for (int i = 0; i < c.length; i++) { final int digit = c[i]; if (digit < 0 || digit >= n) { throw new CombinatoricsException(CombinatoricsException.OUT_OF_RANGE, digit, 0, n - 1); } ret += c[i] * ArithmeticUtils.pow(n, i); } return ret; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy