mirror of https://github.com/jlizier/jidt
435 lines
15 KiB
Java
435 lines
15 KiB
Java
/*
|
|
* Java Information Dynamics Toolkit (JIDT)
|
|
* Copyright (C) 2017, Joseph T. Lizier
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* This class was originally distributed as part of the Apache Commons
|
|
* Math3 library (3.6.1), under the Apache License Version 2.0, which is
|
|
* copied below. This Apache 2 software is now included as a derivative
|
|
* work in the GPLv3 licensed JIDT project, as per:
|
|
* http://www.apache.org/licenses/GPL-compatibility.html
|
|
*
|
|
* The original Apache source code has been modified as follows:
|
|
* -- We have modified package names to sit inside the JIDT structure.
|
|
*/
|
|
|
|
/*
|
|
* 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 infodynamics.utils.commonsmath3.util;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.Comparator;
|
|
import java.util.Arrays;
|
|
import java.util.NoSuchElementException;
|
|
import java.io.Serializable;
|
|
import infodynamics.utils.commonsmath3.exception.MathInternalError;
|
|
import infodynamics.utils.commonsmath3.exception.DimensionMismatchException;
|
|
import infodynamics.utils.commonsmath3.exception.OutOfRangeException;
|
|
|
|
/**
|
|
* Utility to create <a href="http://en.wikipedia.org/wiki/Combination">
|
|
* combinations</a> {@code (n, k)} of {@code k} elements in a set of
|
|
* {@code n} elements.
|
|
*
|
|
* @since 3.3
|
|
*/
|
|
public class Combinations implements Iterable<int[]> {
|
|
/** 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.
|
|
* <p>
|
|
* 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()}:<br/>
|
|
* {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
|
|
* </p>
|
|
* 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 infodynamics.utils.commonsmath3.exception.NotPositiveException if {@code n < 0}.
|
|
* @throws infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException if {@code k > n}.
|
|
*/
|
|
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.
|
|
* <p>
|
|
* 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()}:<br/>
|
|
* {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
|
|
* </p>
|
|
* 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 infodynamics.utils.commonsmath3.exception.NotPositiveException if {@code n < 0}.
|
|
* @throws infodynamics.utils.commonsmath3.exception.NumberIsTooLargeException if {@code k > n}.
|
|
*/
|
|
private Combinations(int n,
|
|
int k,
|
|
IterationOrder iterationOrder) {
|
|
CombinatoricsUtils.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} */
|
|
public Iterator<int[]> iterator() {
|
|
if (k == 0 ||
|
|
k == n) {
|
|
return new SingletonIterator(MathArrays.natural(k));
|
|
}
|
|
|
|
switch (iterationOrder) {
|
|
case LEXICOGRAPHIC:
|
|
return new LexicographicIterator(n, k);
|
|
default:
|
|
throw new MathInternalError(); // Should never happen.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Defines a lexicographic ordering of combinations.
|
|
* The returned comparator allows to compare any two combinations
|
|
* that can be produced by this instance's {@link #iterator() iterator}.
|
|
* Its {@code compare(int[],int[])} method will throw exceptions if
|
|
* passed combinations that are inconsistent with this instance:
|
|
* <ul>
|
|
* <li>{@code DimensionMismatchException} if the array lengths are not
|
|
* equal to {@code k},</li>
|
|
* <li>{@code OutOfRangeException} if an element of the array is not
|
|
* within the interval [0, {@code n}).</li>
|
|
* </ul>
|
|
* @return a lexicographic comparator.
|
|
*/
|
|
public Comparator<int[]> comparator() {
|
|
return new LexicographicComparator(n, k);
|
|
}
|
|
|
|
/**
|
|
* Lexicographic combinations iterator.
|
|
* <p>
|
|
* Implementation follows Algorithm T in <i>The Art of Computer Programming</i>
|
|
* Internet Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All
|
|
* Combinations</a>, D. Knuth, 2004.</p>
|
|
* <p>
|
|
* 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.
|
|
* </p>
|
|
*
|
|
*/
|
|
private static class LexicographicIterator implements Iterator<int[]> {
|
|
/** 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.
|
|
* <p>
|
|
* Note that c[0] is "wasted" but this makes it a little easier to
|
|
* follow the code.
|
|
* </p>
|
|
*/
|
|
private final int[] c;
|
|
|
|
/** Return value for {@link #hasNext()} */
|
|
private boolean more = true;
|
|
|
|
/** Marker: smallest index such that c[j + 1] > j */
|
|
private int j;
|
|
|
|
/**
|
|
* Construct a CombinationIterator to enumerate k-sets from n.
|
|
* <p>
|
|
* NOTE: If {@code k === 0} or {@code k >= n}, the Iterator will be empty
|
|
* (that is, {@link #hasNext()} will return {@code false} immediately.
|
|
* </p>
|
|
*
|
|
* @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}
|
|
*/
|
|
public boolean hasNext() {
|
|
return more;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
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.
|
|
*/
|
|
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<int[]> {
|
|
/** Singleton array */
|
|
private final int[] singleton;
|
|
/** True on initialization, false after first call to next */
|
|
private boolean more = true;
|
|
/**
|
|
* Create a singleton iterator providing the given array.
|
|
* @param singleton array returned by the iterator
|
|
*/
|
|
SingletonIterator(final int[] singleton) {
|
|
this.singleton = singleton;
|
|
}
|
|
/** @return True until next is called the first time, then false */
|
|
public boolean hasNext() {
|
|
return more;
|
|
}
|
|
/** @return the singleton in first activation; throws NSEE thereafter */
|
|
public int[] next() {
|
|
if (more) {
|
|
more = false;
|
|
return singleton;
|
|
} else {
|
|
throw new NoSuchElementException();
|
|
}
|
|
}
|
|
/** Not supported */
|
|
public void remove() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Defines the lexicographic ordering of combinations, using
|
|
* the {@link #lexNorm(int[])} method.
|
|
*/
|
|
private static class LexicographicComparator
|
|
implements Comparator<int[]>, Serializable {
|
|
/** Serializable version identifier. */
|
|
private static final long serialVersionUID = 20130906L;
|
|
/** 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.
|
|
*/
|
|
LexicographicComparator(int n, int k) {
|
|
this.n = n;
|
|
this.k = k;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* @throws DimensionMismatchException if the array lengths are not
|
|
* equal to {@code k}.
|
|
* @throws OutOfRangeException if an element of the array is not
|
|
* within the interval [0, {@code n}).
|
|
*/
|
|
public int compare(int[] c1,
|
|
int[] c2) {
|
|
if (c1.length != k) {
|
|
throw new DimensionMismatchException(c1.length, k);
|
|
}
|
|
if (c2.length != k) {
|
|
throw new DimensionMismatchException(c2.length, k);
|
|
}
|
|
|
|
// Method "lexNorm" works with ordered arrays.
|
|
final int[] c1s = MathArrays.copyOf(c1);
|
|
Arrays.sort(c1s);
|
|
final int[] c2s = MathArrays.copyOf(c2);
|
|
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.
|
|
* For example if {@code c} is {@code {3, 2, 1}}, and {@code n}
|
|
* is 3, the method will return 18.
|
|
*
|
|
* @param c Input array.
|
|
* @return the lexicographic norm.
|
|
* @throws OutOfRangeException 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 OutOfRangeException(digit, 0, n - 1);
|
|
}
|
|
|
|
ret += c[i] * ArithmeticUtils.pow(n, i);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|