MATH-1348

Subclass of "java.util.Random".
This commit is contained in:
Gilles 2016-03-28 03:25:05 +02:00
parent e366894658
commit 3411f29e23
2 changed files with 261 additions and 0 deletions

View File

@ -0,0 +1,148 @@
/*
* 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.io.ObjectOutputStream;
import java.io.IOException;
import org.apache.commons.math4.exception.MathInternalError;
import org.apache.commons.math4.exception.MathUnsupportedOperationException;
import org.apache.commons.math4.rng.UniformRandomProvider;
import org.apache.commons.math4.distribution.RealDistribution;
import org.apache.commons.math4.distribution.NormalDistribution;
/**
* Extension of {@link java.util.Random} that delegates the number
* generation to a {@link UniformRandomProvider}.
*
* <p>
* This class allows usage of JDK utilities that take an instance
* of type {@code Random} as argument.
* <br>
* Other than for this specific purpose, usage of this class
* is best avoided; indeed, because of the following limitations:
* <ul>
* <li>
* {@code MathUnsupportedOperationException} will be raised if
* serialization is attempted.
* </li>
* <li>
* Reseeding is not supported.
* </li>
* </ul>
* an instance of this class cannot be a substitute for an instance
* of the parent class if those functionalities are required.
* </p>
*
* @since 4.0
*/
public final class JDKRandomAdaptor extends Random {
/** Serial version identifier. */
private static final long serialVersionUID = 666L;
/** Delegate. */
private final transient UniformRandomProvider rng;
/** Cf. "nextGaussian()" method. */
private final transient RealDistribution.Sampler gauss;
/**
* Creates an adaptor.
*
* @param rng Generator.
*/
public JDKRandomAdaptor(UniformRandomProvider rng) {
super(0L);
this.rng = rng;
gauss = new NormalDistribution().createSampler(rng);
}
/** {@inheritDoc} */
@Override
public boolean nextBoolean() {
return rng.nextBoolean();
}
/** {@inheritDoc} */
@Override
public void nextBytes(byte[] bytes) {
rng.nextBytes(bytes);
}
/** {@inheritDoc} */
@Override
public double nextDouble() {
return rng.nextDouble();
}
/** {@inheritDoc} */
@Override
public float nextFloat() {
return rng.nextFloat();
}
/** {@inheritDoc} */
@Override
public double nextGaussian() {
return gauss.sample();
}
/** {@inheritDoc} */
@Override
public int nextInt() {
return rng.nextInt();
}
/** {@inheritDoc} */
@Override
public int nextInt(int n) {
return rng.nextInt(n);
}
/** {@inheritDoc} */
@Override
public long nextLong() {
return rng.nextLong();
}
/** {@inheritDoc} */
@Override
protected int next(int bits) {
// Should never happen: it means that some methods were not overridden.
throw new MathInternalError();
}
/**
* Seeding is not supported.
*
* @param seed Ignored.
*/
@Override
public void setSeed(long seed) {
// Cannot throw because the constructor of "Random" calls it.
// throw new MathUnsupportedOperationException();
}
/**
* @param out Ignored.
* @throws IOException Ignored.
* @throws MathUnsupportedOperationException if called.
*/
private void writeObject(ObjectOutputStream out)
throws IOException {
throw new MathUnsupportedOperationException();
}
}

View File

@ -0,0 +1,113 @@
/*
* 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 org.apache.commons.math4.exception.MathUnsupportedOperationException;
import org.apache.commons.math4.rng.UniformRandomProvider;
import org.apache.commons.math4.rng.RandomSource;
import org.apache.commons.math4.distribution.RealDistribution;
import org.apache.commons.math4.distribution.NormalDistribution;
import org.apache.commons.math4.TestUtils;
import org.junit.Assert;
import org.junit.Test;
/**
* Test cases for {@link JDKRandomAdaptor}.
*/
public class JDKRandomAdaptorTest {
@Test
public void testUniform() {
final RandomSource source = RandomSource.WELL_19937_C;
final long seed = RandomSource.createLong(); // Random seed.
final UniformRandomProvider reference = RandomSource.create(source, seed);
final Random random = new JDKRandomAdaptor(RandomSource.create(source, seed));
final int n = 3; // Check several times, reusing the same RNG.
for (int i = 0; i < n; i++) {
checkUniform(reference, random);
}
}
@Test
public void testGaussian() {
final RandomSource source = RandomSource.WELL_19937_C;
final long seed = RandomSource.createLong(); // Random seed.
final UniformRandomProvider reference = RandomSource.create(source, seed);
final RealDistribution.Sampler s = new NormalDistribution(0, 1).createSampler(reference);
final Random random = new JDKRandomAdaptor(RandomSource.create(source, seed));
final int n = 11; // Check several times, reusing the same RNG.
for (int i = 0; i < n; i++) {
Assert.assertEquals(s.sample(), random.nextGaussian(), 0);
}
}
@Test
public void testSeedIsIgnored() {
final RandomSource source = RandomSource.WELL_19937_C;
final long seed = RandomSource.createLong(); // Random seed.
Random random;
random = new JDKRandomAdaptor(RandomSource.create(source, seed));
final double withoutReseed = random.nextDouble();
// Same RNG.
random = new JDKRandomAdaptor(RandomSource.create(source, seed));
final long differentSeed = seed + 1;
random.setSeed(differentSeed); // Is seeding ignored?
final double withReseed = random.nextDouble();
Assert.assertEquals(withoutReseed, withReseed, 0);
}
@Test(expected=MathUnsupportedOperationException.class)
public void testSerializeIsNotSupported() {
TestUtils.serializeAndRecover(new JDKRandomAdaptor(RandomSource.create(RandomSource.WELL_512_A)));
}
/**
* Check uniform random generator.
*
* @param rand1 Reference generator.
* @param rand2 Generator under test.
*/
private void checkUniform(UniformRandomProvider rand1,
Random rand2) {
final int len = 11;
final byte[] bytes1 = new byte[len];
final byte[] bytes2 = new byte[len];
rand1.nextBytes(bytes1);
rand2.nextBytes(bytes2);
Assert.assertArrayEquals(bytes1, bytes2);
Assert.assertEquals(rand1.nextBoolean(), rand2.nextBoolean());
Assert.assertEquals(rand1.nextInt(), rand2.nextInt());
Assert.assertEquals(rand1.nextInt(len), rand2.nextInt(len));
Assert.assertEquals(rand1.nextLong(), rand2.nextLong());
Assert.assertEquals(rand1.nextDouble(), rand2.nextDouble(), 0);
Assert.assertEquals(rand1.nextFloat(), rand2.nextFloat(), 0);
}
}