Java tutorial
/* Copyright 2009-2015 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package org.moeaframework.core; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.junit.Assert; import org.junit.Test; import org.moeaframework.TestThresholds; /** * Tests a {@link PRNG} to ensure the {@code nextX} methods produce a sequence * of values satisfying theoretical properties of the desired distribution. */ public class PRNGTest { /** * The number of samples used for statistics. */ private static int N = 1000000; /** * Tests if the {@code nextFloat} method produces uniformly-distributed * values in the range {@code [0, 1]}. */ @Test public void testNextFloat() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextFloat()); } testUniformDistribution(0.0, 1.0, statistics); } /** * Tests if the {@code nextFloat} method produces uniformly-distributed * values in a specified range. */ @Test public void testNextFloatRange() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextFloat(15.0f, 20.0f)); } testUniformDistribution(15.0, 20.0, statistics); } /** * Tests if the {@code nextDouble} method produces uniformly-distributed * values in the range {@code [0, 1]}. */ @Test public void testNextDouble() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextDouble()); } testUniformDistribution(0.0, 1.0, statistics); } /** * Tests if the {@code nextDouble} method produces uniformly-distributed * values in a specified range. */ @Test public void testNextDoubleRange() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextDouble(15.0, 20.0)); } testUniformDistribution(15.0, 20.0, statistics); } /** * Tests if the {@code nextInt} method produces uniformly-distributed * values using a histogram. */ @Test public void testNextInt() { int lessThanEqualZero = 0; int greaterThanZero = 0; int lessThanHalfNegative = 0; int greaterThanHalfPositive = 0; for (int i = 0; i < N; i++) { int value = PRNG.nextInt(); if (value <= 0) { lessThanEqualZero++; if (value < Integer.MIN_VALUE / 2) { lessThanHalfNegative++; } } else { greaterThanZero++; if (value > Integer.MAX_VALUE / 2) { greaterThanHalfPositive++; } } } Assert.assertEquals(N / 2.0, lessThanEqualZero, TestThresholds.STATISTICS_EPS * N / 2.0); Assert.assertEquals(N / 2.0, greaterThanZero, TestThresholds.STATISTICS_EPS * N / 2.0); Assert.assertEquals(N / 4.0, lessThanHalfNegative, TestThresholds.STATISTICS_EPS * N / 4.0); Assert.assertEquals(N / 4.0, greaterThanHalfPositive, TestThresholds.STATISTICS_EPS * N / 4.0); } /** * Tests if the {@code nextInt} method produces uniformly-distributed * values in a specified range. */ @Test public void testNextIntRange1() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextInt(15)); } testUniformDistribution(0, 14, statistics); } /** * Tests if the {@code nextInt} method produces uniformly-distributed * values in a specified range. */ @Test public void testNextIntRange2() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextInt(15, 20)); } testUniformDistribution(15, 20, statistics); } /** * Tests if the {@code nextBoolean} method produces {@code true} and * {@code false} with equal probability. */ @Test public void testNextBoolean() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextBoolean() ? 1 : 0); } testUniformDistribution(0, 1, statistics); } /** * Tests if the {@code nextGaussian} method produces a Gaussian distribution * with mean {@code 0} and standard deviation {@code 1}. */ @Test public void testNextGaussian() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextGaussian()); } testGaussianDistribution(0.0, 1.0, statistics); } /** * Tests if the {@code nextGaussian} method produces a Gaussian distribution * with a specified mean and standard deviation. */ @Test public void testNextGaussianParameterized() { DescriptiveStatistics statistics = new DescriptiveStatistics(); for (int i = 0; i < N; i++) { statistics.addValue(PRNG.nextGaussian(5.0, 2.0)); } testGaussianDistribution(5.0, 2.0, statistics); } /** * Tests if the {@code shuffle} method produces valid permutations of a * list, and that the distribution of the values for each index are * approximately uniform. */ @Test public void testShuffleList() { int P = 7; List<Integer> list = new ArrayList<Integer>(); List<DescriptiveStatistics> statistics = new ArrayList<DescriptiveStatistics>(); for (int i = 0; i < P; i++) { list.add(i); statistics.add(new DescriptiveStatistics()); } for (int i = 0; i < 50000; i++) { PRNG.shuffle(list); for (int j = 0; j < P; j++) { statistics.get(j).addValue(list.get(j)); } testPermutation(P, list.toArray(new Integer[list.size()])); } for (int i = 0; i < P; i++) { testUniformDistribution(0, P - 1, statistics.get(i)); } } /** * Tests if the {@code shuffle} method produces valid permutations of an * array, and that the distribution of the values for each index are * approximately uniform. */ @Test public void testShuffleObjectArray() throws Exception { testShuffleArray(Object[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty array. */ @Test public void testShuffleEmptyObjectArray() throws Exception { testShuffleArray(Object[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of an * integer array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleIntArray() throws Exception { testShuffleArray(int[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * integer array. */ @Test public void testShuffleEmptyIntArray() throws Exception { testShuffleArray(int[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * double array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleDoubleArray() throws Exception { testShuffleArray(double[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * double array. */ @Test public void testShuffleEmptyDoubleArray() throws Exception { testShuffleArray(double[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * float array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleFloatArray() throws Exception { testShuffleArray(float[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * float array. */ @Test public void testShuffleEmptyFloatArray() throws Exception { testShuffleArray(float[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * long array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleLongArray() throws Exception { testShuffleArray(long[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * long array. */ @Test public void testShuffleEmptyLongArray() throws Exception { testShuffleArray(long[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * short array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleShortArray() throws Exception { testShuffleArray(short[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * short array. */ @Test public void testShuffleEmptyShortArray() throws Exception { testShuffleArray(short[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * byte array, and that the distribution of the values for each index are * approximately uniform. * * @throws Exception if a reflection error occurred */ @Test public void testShuffleByteArray() throws Exception { testShuffleArray(byte[].class, 5); } /** * Tests if the {@code shuffle} method correctly handles an empty * byte array. */ @Test public void testShuffleEmptyByteArray() throws Exception { testShuffleArray(byte[].class, 0); } /** * Tests if the {@code shuffle} method produces valid permutations of a * boolean array, and that the distribution of the values for each index are * approximately uniform. */ @Test public void testShuffleBooleanArray() { int K = 4; int P = 2 * K; boolean[] array = new boolean[P]; DescriptiveStatistics[] statistics = new DescriptiveStatistics[P]; for (int i = 0; i < P; i++) { if (i < K) { array[i] = true; } statistics[i] = new DescriptiveStatistics(); } for (int i = 0; i < 50000; i++) { PRNG.shuffle(array); for (int j = 0; j < P; j++) { statistics[j].addValue(array[j] ? 1 : 0); } testPermutation(K, array); } for (int i = 0; i < P; i++) { testUniformDistribution(0, 1, statistics[i]); } } /** * Tests if the {@code shuffle} method produces valid permutations of a * typed array, and that the distribution of the values for each index are * approximately uniform. * * @param type the class of the array * @param size the size of the array */ public void testShuffleArray(Class<?> type, int size) throws Exception { Object array = Array.newInstance(type.getComponentType(), size); DescriptiveStatistics[] statistics = new DescriptiveStatistics[size]; for (int i = 0; i < size; i++) { // casts are needed when truncating the int if (type.getComponentType() == short.class) { Array.set(array, i, (short) i); } else if (type.getComponentType() == byte.class) { Array.set(array, i, (byte) i); } else { Array.set(array, i, i); } statistics[i] = new DescriptiveStatistics(); } for (int i = 0; i < 50000; i++) { Method method = PRNG.class.getMethod("shuffle", type); method.invoke(null, array); Integer[] integerArray = new Integer[size]; for (int j = 0; j < size; j++) { int value = ((Number) Array.get(array, j)).intValue(); integerArray[j] = value; statistics[j].addValue(value); } testPermutation(size, integerArray); } for (int i = 0; i < size; i++) { testUniformDistribution(0, size - 1, statistics[i]); } } /** * Asserts that the statistical distribution satisfies the properties of a * real-valued uniform distribution between {@code min} and {@code max}. * * @param min the minimum bounds of the uniform distribution * @param max the maximum bounds of the uniform distribution * @param statistics the captures statistics of a sampled distribution */ private void testUniformDistribution(double min, double max, DescriptiveStatistics statistics) { Assert.assertEquals((min + max) / 2.0, statistics.getMean(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(Math.pow(max - min, 2.0) / 12.0, statistics.getVariance(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(0.0, statistics.getSkewness(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(-6.0 / 5.0, statistics.getKurtosis(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(min, statistics.getMin(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(max, statistics.getMax(), TestThresholds.STATISTICS_EPS); } /** * Asserts that the statistical distribution satisfies the properties of an * integer-valued uniform distribution between {@code min} and {@code max}. * * @param min the minimum bounds of the uniform distribution * @param max the maximum bounds of the uniform distribution * @param statistics the captures statistics of a sampled distribution */ private void testUniformDistribution(int min, int max, DescriptiveStatistics statistics) { int n = max - min + 1; int nn = n * n; Assert.assertEquals((min + max) / 2.0, statistics.getMean(), TestThresholds.STATISTICS_EPS); Assert.assertEquals((nn - 1) / 12.0, statistics.getVariance(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(0.0, statistics.getSkewness(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(-(6.0 * (nn + 1)) / (5.0 * (nn - 1)), statistics.getKurtosis(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(min, statistics.getMin(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(max, statistics.getMax(), TestThresholds.STATISTICS_EPS); } /** * Asserts that the statistical distribution satisfies the properties of a * Gaussian distribution with the specified mean and standard deviation. * * @param mu the mean value of the distribution * @param sigma the standard deviation of the distribution * @param statistics the captures statistics of a sampled distribution */ private void testGaussianDistribution(double mu, double sigma, DescriptiveStatistics statistics) { Assert.assertEquals(mu, statistics.getMean(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(sigma * sigma, statistics.getVariance(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(0.0, statistics.getSkewness(), TestThresholds.STATISTICS_EPS); Assert.assertEquals(0.0, statistics.getKurtosis(), TestThresholds.STATISTICS_EPS); } /** * Asserts that the array is a valid permutation of {@code n} integers. * * @param n the size of the permutation * @param array the permutation */ private void testPermutation(int n, Integer[] array) { Assert.assertEquals(n, array.length); for (int i = 0; i < n; i++) { boolean found = false; for (int j = 0; j < array.length; j++) { if (array[j] == i) { found = true; break; } } Assert.assertTrue(found); } } /** * Asserts that the array is a valid permutation of {@code n} boolean * values. * * @param n the number of {@code true} values * @param array the permutation */ private void testPermutation(int n, boolean[] array) { int count = 0; for (int i = 0; i < array.length; i++) { count += array[i] ? 1 : 0; } Assert.assertEquals(n, count); } }