diff --git a/src/main/java/org/apache/commons/math4/random/RandomUtils.java b/src/main/java/org/apache/commons/math4/random/RandomUtils.java new file mode 100644 index 000000000..e72655ab4 --- /dev/null +++ b/src/main/java/org/apache/commons/math4/random/RandomUtils.java @@ -0,0 +1,492 @@ +/* + * 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.math4.random; + +import java.util.Random; +import java.util.List; +import java.util.ArrayList; +import java.util.Collection; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import org.apache.commons.math4.exception.MathInternalError; +import org.apache.commons.math4.exception.NotANumberException; +import org.apache.commons.math4.exception.NotFiniteNumberException; +import org.apache.commons.math4.exception.NotStrictlyPositiveException; +import org.apache.commons.math4.exception.NumberIsTooLargeException; +import org.apache.commons.math4.exception.util.LocalizedFormats; +import org.apache.commons.math4.rng.UniformRandomProvider; +import org.apache.commons.math4.util.MathArrays; + +/** + * Factory for creating generators of miscellaneous data. + * + * @since 4.0 + */ +public class RandomUtils { + /** + * Class contains only static methods. + */ + private RandomUtils() {} + + /** + * @param rng Underlying generator. Reference is copied so the RNG + * is shared with the caller. + * @return a {@link DataGenerator data generator}. + */ + public static DataGenerator createDataGenerator(final UniformRandomProvider rng) { + return new DataGenerator(rng); + } + + /** + * Wraps an instance of the JDK's {@link Random} class. + * The actual generation of random numbers will be delegated to that + * instance. + *
+ * If cryptographically secure data is required, one can use this + * factory method, with an instance of the {@link java.security.SecureRandom} + * class as the argument. + * Note that data generation will be much slower in this case. + *
+ * + * @param rng Underlying generator. Reference is copied so the RNG + * is shared with the caller. + * @return a {@link DataGenerator data generator}. + */ + public static DataGenerator createDataGenerator(final Random rng) { + return createDataGenerator(asUniformRandomProvider(rng)); + } + + /** + * Wraps a {@link Random} instance. + * + * @param rng JDK {@link Random} instance to which the random number + * generation is delegated. Reference is copied so the RNG is shared + * with the caller. + * @return a {@link UniformRandomProvider} instance. + */ + public static UniformRandomProvider asUniformRandomProvider(final Random rng) { + return new UniformRandomProvider() { + /** {@inheritDoc} */ + @Override + public void nextBytes(byte[] bytes) { + rng.nextBytes(bytes); + } + + /** {@inheritDoc} */ + @Override + public void nextBytes(byte[] bytes, + int start, + int len) { + final byte[] reduced = new byte[len]; + rng.nextBytes(reduced); + System.arraycopy(reduced, 0, bytes, start, len); + } + + /** {@inheritDoc} */ + @Override + public int nextInt() { + return rng.nextInt(); + } + + /** {@inheritDoc} */ + @Override + public int nextInt(int n) { + if (n <= 0) { + throw new NotStrictlyPositiveException(n); + } + return rng.nextInt(n); + } + + /** {@inheritDoc} */ + @Override + public long nextLong() { + return rng.nextLong(); + } + + /** {@inheritDoc} */ + @Override + public long nextLong(long n) { + // Code copied from "o.a.c.m.rng.internal.BaseProvider". + + if (n > 0) { + long bits; + long val; + do { + bits = rng.nextLong() >>> 1; + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + throw new NotStrictlyPositiveException(n); + } + + /** {@inheritDoc} */ + @Override + public boolean nextBoolean() { + return rng.nextBoolean(); + } + + /** {@inheritDoc} */ + @Override + public float nextFloat() { + return rng.nextFloat(); + } + + /** {@inheritDoc} */ + @Override + public double nextDouble() { + return rng.nextDouble(); + } + }; + } + + /** + * Various random data generation routines. + */ + public static class DataGenerator { + /** Underlying RNG. */ + private final UniformRandomProvider rng; + + /** + * @param rng Underlying generator. + */ + DataGenerator(UniformRandomProvider rng) { + this.rng = rng; + } + + /** + * Generates a random string of hex characters of length {@code len}. + * + * Algorithm Description: how hexadecimal strings are + * generated depends on the value of the {@code useSha1} argument. + * + *+ * Definition: + * + * Uniform Distribution {@code lower} and {@code upper - lower} are the + * + * location and scale parameters, respectively.
+ *+ * Algorithm Description: scales the output of + * Random.nextDouble(), but rejects 0 values (i.e., will generate another + * random double if Random.nextDouble() returns 0). This is necessary to + * provide a symmetric output interval (both endpoints excluded). + *
+ * + * @param lower Lower bound of the support (excluded). + * @param upper Upper bound of the support (excluded). + * @return a uniformly distributed random value between lower and upper + * (both excluded). + * @throws NumberIsTooLargeException if {@code lower >= upper}. + * @throws NotFiniteNumberException if one of the bounds is infinite. + * @throws NotANumberException if one of the bounds is NaN. + */ + public double nextUniform(double lower, double upper) { + return nextUniform(lower, upper, false); + } + + /** + * Generates a uniformly distributed random value from the interval + * {@code (lower, upper)} or the interval {@code [lower, upper)}. The lower + * bound is thus optionally included, while the upper bound is always + * excluded. + *+ * Definition: + * + * Uniform Distribution {@code lower} and {@code upper - lower} are the + * + * location and scale parameters, respectively.
+ *+ * Algorithm Description: if the lower bound is excluded, + * scales the output of "nextDouble()", but rejects 0 values (i.e. it + * will generate another random double if "nextDouble()" returns 0). + * This is necessary to provide a symmetric output interval (both + * endpoints excluded). + *
+ * + * @param lower Lower bound of the support. + * @param upper Exclusive upper bound of the support. + * @param lowerInclusive {@code true} if the lower bound is inclusive. + * @return a uniformly distributed random value in the {@code (lower, upper)} + * interval, if {@code lowerInclusive} is {@code false}, or in the + * {@code [lower, upper)} interval, if {@code lowerInclusive} is + * {@code true}. + * @throws NumberIsTooLargeException if {@code lower >= upper}. + * @throws NotFiniteNumberException if one of the bounds is infinite. + * @throws NotANumberException if one of the bounds is NaN. + */ + public double nextUniform(double lower, + double upper, + boolean lowerInclusive) { + if (lower >= upper) { + throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, + lower, upper, false); + } + if (Double.isInfinite(lower)) { + throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, lower); + } + if (Double.isInfinite(upper)) { + throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, upper); + } + if (Double.isNaN(lower) || Double.isNaN(upper)) { + throw new NotANumberException(); + } + + // Ensure nextDouble() isn't 0.0 + double u = rng.nextDouble(); + while (!lowerInclusive && u <= 0.0) { + u = rng.nextDouble(); + } + + return u * upper + (1.0 - u) * lower; + } + + /** + * Generates an integer array of length {@code k} whose entries are selected + * randomly, without repetition, from the integers {@code 0, ..., n - 1} + * (inclusive). + *+ * Generated arrays represent permutations of {@code n} taken {@code k} at a + * time. + *
+ *+ * This method calls {@link MathArrays#shuffle(int[],UniformRandomProvider) + * MathArrays.shuffle} in order to create a random shuffle of the set + * of natural numbers {@code { 0, 1, ..., n - 1 }}. + *
+ * + * @param n Domain of the permutation. + * @param k Size of the permutation. + * @return a random {@code k}-permutation of {@code n}, as an array of + * integers. + * @throws NumberIsTooLargeException if {@code k > n}. + * @throws NotStrictlyPositiveException if {@code k <= 0}. + */ + public int[] nextPermutation(int n, + int k) + throws NumberIsTooLargeException, NotStrictlyPositiveException { + if (k > n) { + throw new NumberIsTooLargeException(LocalizedFormats.PERMUTATION_EXCEEDS_N, + k, n, true); + } + if (k <= 0) { + throw new NotStrictlyPositiveException(LocalizedFormats.PERMUTATION_SIZE, + k); + } + + final int[] index = MathArrays.natural(n); + MathArrays.shuffle(index, rng); + + // Return a new array containing the first "k" entries of "index". + return MathArrays.copyOf(index, k); + } + + /** + * Returns a list of {@code k} objects selected randomly from the + * given {@code collection}. + * + *+ * Sampling is without replacement; but if {@code collection} contains + * identical objects, the sample may include repeats. If all elements + * are distinct, the resulting object array represents a + * + * Simple Random Sample of size {@code k} from the elements of + * the {@code collection}. + *
+ *+ * This method calls {@link #nextPermutation(int,int) nextPermutation(c.size(), k)} + * in order to sample the collection. + *
+ * + * @param