Added RandomAdaptor to complete PRNG pluggability framework, updated User Guide.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@179494 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Phil Steitz 2005-06-02 03:06:45 +00:00
parent f1a9880905
commit ad94c5d1de
4 changed files with 410 additions and 69 deletions

View File

@ -0,0 +1,121 @@
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed 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.math.random;
import java.util.Random;
/**
* Extension of <code>java.util.Random</code> wrapping a
* {@link RandomGenerator}.
*
* @since 1.1
* @version $Revision:$ $Date$
*/
public class RandomAdaptor extends Random implements RandomGenerator {
/** Wrapped randomGenerator instance */
private RandomGenerator randomGenerator = null;
/**
* Prevent instantiation without a generator argument
*/
private RandomAdaptor() { }
/**
* Construct a RandomAdaptor wrapping the supplied RandomGenerator.
*
* @param randomGenerator the wrapped generator
*/
public RandomAdaptor(RandomGenerator randomGenerator) {
this.randomGenerator = randomGenerator;
}
/**
* Factory method to create a <code>Random</code> using the supplied
* <code>RandomGenerator</code>.
*
* @param randomGenerator
* @return a Random instance wrapping the RandomGenerator
*/
public static Random createAdaptor(RandomGenerator randomGenerator) {
return new RandomAdaptor(randomGenerator);
}
/* (non-Javadoc)
* @see java.util.Random#nextBoolean()
*/
public boolean nextBoolean() {
return randomGenerator.nextBoolean();
}
/* (non-Javadoc)
* @see java.util.Random#nextBytes(byte[])
*/
public void nextBytes(byte[] bytes) {
randomGenerator.nextBytes(bytes);
}
/* (non-Javadoc)
* @see java.util.Random#nextDouble()
*/
public double nextDouble() {
return randomGenerator.nextDouble();
}
/* (non-Javadoc)
* @see java.util.Random#nextFloat()
*/
public float nextFloat() {
return randomGenerator.nextFloat();
}
/* (non-Javadoc)
* @see java.util.Random#nextGaussian()
*/
public double nextGaussian() {
return randomGenerator.nextGaussian();
}
/* (non-Javadoc)
* @see java.util.Random#nextInt()
*/
public int nextInt() {
return randomGenerator.nextInt();
}
/* (non-Javadoc)
* @see java.util.Random#nextInt(int)
*/
public int nextInt(int n) {
return randomGenerator.nextInt(n);
}
/* (non-Javadoc)
* @see java.util.Random#nextLong()
*/
public long nextLong() {
return randomGenerator.nextLong();
}
/* (non-Javadoc)
* @see java.util.Random#setSeed(long)
*/
public void setSeed(long seed) {
if (randomGenerator != null) { // required to avoid NPE in constructor
randomGenerator.setSeed(seed);
}
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed 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.math.random;
import junit.framework.Test;
import junit.framework.TestSuite;
import java.util.Random;
/**
* Test cases for the RandomAdaptor class
*
* @version $Revision:$ $Date$
*/
public class RandomAdaptorTest extends RandomDataTest {
public RandomAdaptorTest(String name) {
super(name);
}
public static Test suite() {
TestSuite suite = new TestSuite(RandomAdaptorTest.class);
suite.setName("RandomAdaptor Tests");
return suite;
}
public void testAdaptor() {
ConstantGenerator generator = new ConstantGenerator();
Random random = RandomAdaptor.createAdaptor(generator);
checkConstant(random);
RandomAdaptor randomAdaptor = new RandomAdaptor(generator);
checkConstant(randomAdaptor);
}
private void checkConstant(Random random) {
byte[] bytes = new byte[] {0};
random.nextBytes(bytes);
assertEquals(0, bytes[0]);
assertEquals(false, random.nextBoolean());
assertEquals(0, random.nextDouble(), 0);
assertEquals(0, random.nextFloat(), 0);
assertEquals(0, random.nextGaussian(), 0);
assertEquals(0, random.nextInt());
assertEquals(0, random.nextInt(1));
assertEquals(0, random.nextLong());
random.setSeed(100);
assertEquals(0, random.nextDouble(), 0);
}
/*
* "Constant" generator to test Adaptor delegation.
* "Powered by Eclipse ;-)"
*
*/
private class ConstantGenerator implements RandomGenerator {
public boolean nextBoolean() {
return false;
}
public void nextBytes(byte[] bytes) {
}
public double nextDouble() {
return 0;
}
public float nextFloat() {
return 0;
}
public double nextGaussian() {
return 0;
}
public int nextInt() {
return 0;
}
public int nextInt(int n) {
return 0;
}
public long nextLong() {
return 0;
}
public void setSeed(long seed) {
}
}
}

View File

@ -50,7 +50,8 @@
<li><a href="random.html#2.2 Random numbers">2.2 Random numbers</a></li> <li><a href="random.html#2.2 Random numbers">2.2 Random numbers</a></li>
<li><a href="random.html#2.3 Random Strings">2.3 Random Strings</a></li> <li><a href="random.html#2.3 Random Strings">2.3 Random Strings</a></li>
<li><a href="random.html#2.4 Random permutations, combinations, sampling">2.4 Random permutations, combinations, sampling</a></li> <li><a href="random.html#2.4 Random permutations, combinations, sampling">2.4 Random permutations, combinations, sampling</a></li>
<li><a href="random.html#2.5 Generating data &amp;apos;like&amp;apos; an input file">2.5 Generating data 'like' an input file</a></li> <li><a href="random.html#2.5 Generating data 'like' an input file">2.5 Generating data 'like' an input file</a></li>
<li><a href="random.html#2.6 PRNG Pluggability">2.6 PRNG Pluggability</a></li>
</ul></li> </ul></li>
<li><a href="linear.html">3. Linear Algebra</a> <li><a href="linear.html">3. Linear Algebra</a>
<ul> <ul>

View File

@ -34,95 +34,130 @@
<ul> <ul>
<li>generating random numbers</li> <li>generating random numbers</li>
<li>generating random strings</li> <li>generating random strings</li>
<li>generating cryptographically secure sequences of random numbers or strings</li> <li>generating cryptographically secure sequences of random numbers or
strings</li>
<li>generating random samples and permuations</li> <li>generating random samples and permuations</li>
<li>analyzing distributions of values in an input file and generating values "like" <li>analyzing distributions of values in an input file and generating
the values in the file</li> values "like" the values in the file</li>
<li>generating data for grouped frequency distributions or histograms</li> <li>generating data for grouped frequency distributions or
histograms</li>
</ul></p> </ul></p>
<p>
The source of random data used by the data generation utilities is
pluggable. By default, the JDK-supplied PseudoRandom Number Generator
(PRNG) is used, but alternative generators can be "plugged in" using an
adaptor framework, which provides a generic facility for replacing
<code>java.util.Random</code> with an alternative PRNG.
</p>
<p>
Sections 2.3-2.5 below show how to use the commons math API to generate
different kinds of random data. The examples all use the default
JDK-supplied PRNG. PRNG pluggability is covered in 2.6. The only
modification required to the examples to use alternative PRNGs is to
replace the argumentless constructor calls with invocations including
a <code>RandomGenerator</code> instance as a parameter.
</p>
</subsection> </subsection>
<subsection name="2.2 Random numbers" href="deviates"> <subsection name="2.2 Random numbers" href="deviates">
<p> <p>
The <a href="../apidocs/org/apache/commons/math/random/RandomData.html"> The <a href="../apidocs/org/apache/commons/math/random/RandomData.html">
org.apache.commons.math.RandomData</a> interface defines methods for generating org.apache.commons.math.RandomData</a> interface defines methods for
random sequences of numbers. The API contracts of these methods use the following concepts: generating random sequences of numbers. The API contracts of these methods
use the following concepts:
<dl> <dl>
<dt>Random sequence of numbers from a probability distribution</dt> <dt>Random sequence of numbers from a probability distribution</dt>
<dd>There is no such thing as a single "random number." What can be generated <dd>There is no such thing as a single "random number." What can be
are <i>sequences</i> of numbers that appear to be random. When using the generated are <i>sequences</i> of numbers that appear to be random. When
built-in JDK function <code>Math.random(),</code> sequences of values generated using the built-in JDK function <code>Math.random(),</code> sequences of
follow the <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm"> values generated follow the
Uniform Distribution</a>, which means that the values are evenly spread over the interval <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">
between 0 and 1, with no sub-interval having a greater probability of containing generated Uniform Distribution</a>, which means that the values are evenly spread
values than any other interval of the same length. The mathematical concept of a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda36.htm"> over the interval between 0 and 1, with no sub-interval having a greater
probability distribution</a> basically amounts to asserting that different ranges in the set probability of containing generated values than any other interval of the
of possible values for of a random variable have different probabilities of containing the value. same length. The mathematical concept of a
Commons Math supports generating random sequences from the following probability distributions. The <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda36.htm">
javadoc for the <code>nextXxx</code> methods in <code>RandomDataImpl</code> describes the algorithms used probability distribution</a> basically amounts to asserting that different
to generate random deviates from each of these distributions. ranges in the set of possible values for of a random variable have
different probabilities of containing the value. Commons Math supports
generating random sequences from the following probability distributions.
The javadoc for the <code>nextXxx</code> methods in
<code>RandomDataImpl</code> describes the algorithms used to generate
random deviates from each of these distributions.
<ul> <ul>
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">uniform distribution</a></li> <li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm">
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm">exponential distribution</a></li> uniform distribution</a></li>
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm">poisson distribution</a></li> <li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3667.htm">
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm">Gaussian distribution</a></li> exponential distribution</a></li>
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm">
poisson distribution</a></li>
<li><a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm">
Gaussian distribution</a></li>
</ul> </ul>
</dd> </dd>
<dt>Cryptographically secure random sequences</dt> <dt>Cryptographically secure random sequences</dt>
<dd>It is possible for a sequence of numbers to appear random, but nonetheless to be <dd>It is possible for a sequence of numbers to appear random, but
predictable based on the algorithm used to generate the sequence. If in addition to nonetheless to be predictable based on the algorithm used to generate the
randomness, strong unpredictability is required, it is best to use a sequence. If in addition to randomness, strong unpredictability is
required, it is best to use a
<a href="http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator"> <a href="http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator">
secure random number generator</a> to generate values (or strings). The nextSecureXxx methods secure random number generator</a> to generate values (or strings). The
in the <code>RandomDataImpl</code> implementation of the <code>RandomData</code> interface use the nextSecureXxx methods in the <code>RandomDataImpl</code> implementation of
JDK <code>SecureRandom</code> pseudo-random number generator (PRNG) the <code>RandomData</code> interface use the JDK <code>SecureRandom</code>
to generate cryptographically secure sequences. The <code>setSecureAlgorithm</code> method PRNG to generate cryptographically secure sequences. The
allows you to change the underlying PRNG. These methods are <strong>much slower</strong> than <code>setSecureAlgorithm</code> method allows you to change the underlying
the corresponding "non-secure" versions, so they should only be used when cryptographic security PRNG. These methods are <strong>much slower</strong> than the corresponding
is required.</dd> "non-secure" versions, so they should only be used when cryptographic
security is required.</dd>
<dt>Seeding pseudo-random number generators</dt> <dt>Seeding pseudo-random number generators</dt>
<dd>By default, the implementation provided in <code>RandomDataImpl</code> uses the JDK-provided <dd>By default, the implementation provided in <code>RandomDataImpl</code>
PRNG. Like other PRNGs, the JDK generator generates sequences of random numbers based on an initial uses the JDK-provided PRNG. Like most other PRNGs, the JDK generator
"seed value". For the non-secure methods, starting with the same seed always produces the same generates sequences of random numbers based on an initial "seed value".
sequence of values. Secure sequences started with the same seeds will diverge. When a new For the non-secure methods, starting with the same seed always produces the
<code>RandomDataImpl</code> is created, the underlying random number generators are same sequence of values. Secure sequences started with the same seeds will
<strong>not</strong> intialized. The first call to a data generation method, or to a diverge. When a new <code>RandomDataImpl</code> is created, the underlying
<code>reSeed()</code> method initializes the appropriate generator. If you do not explicitly random number generators are <strong>not</strong> intialized. The first
seed the generator, it is by default seeded with the current time in milliseconds. Therefore, call to a data generation method, or to a <code>reSeed()</code> method
to generate sequences of random data values, you should always instantiate <strong>one</strong> initializes the appropriate generator. If you do not explicitly seed the
<code>RandomDataImpl</code> and use it repeatedly instead of creating new instances for generator, it is by default seeded with the current time in milliseconds.
subsequent values in the sequence. For example, the following will generate a random sequence Therefore, to generate sequences of random data values, you should always
of 50 long integers between 1 and 1,000,000, using the current time in milliseconds as the seed instantiate <strong>one</strong> <code>RandomDataImpl</code> and use it
for the JDK PRNG: repeatedly instead of creating new instances for subsequent values in the
sequence. For example, the following will generate a random sequence of 50
long integers between 1 and 1,000,000, using the current time in
milliseconds as the seed for the JDK PRNG:
<source> <source>
RandomDataImpl randomData = new RandomDataImpl(); RandomDataImpl randomData = new RandomDataImpl();
for (int i = 0; i &lt; 1000; i++) { for (int i = 0; i &lt; 1000; i++) {
value = randomData.nextLong(1, 1000000); value = randomData.nextLong(1, 1000000);
} }
</source> </source>
The following will not in general produce a good random sequence, since the PRNG is reseeded The following will not in general produce a good random sequence, since the
each time through the loop with the current time in milliseconds: PRNG is reseeded each time through the loop with the current time in
milliseconds:
<source> <source>
for (int i = 0; i &lt; 1000; i++) { for (int i = 0; i &lt; 1000; i++) {
RandomDataImpl randomData = new RandomDataImpl(); RandomDataImpl randomData = new RandomDataImpl();
value = randomData.nextLong(1, 1000000); value = randomData.nextLong(1, 1000000);
} }
</source> </source>
The following will produce the same random sequence each time it is executed: The following will produce the same random sequence each time it is
executed:
<source> <source>
RandomDataImpl randomData = new RandomDataImpl(); RandomDataImpl randomData = new RandomDataImpl();
randomData.reSeed(1000); randomData.reSeed(1000);
for (int i = 0; i = 1000; i++) { for (int i = 0; i = 1000; i++) {
value = randomData.nextLong(1, 1000000); value = randomData.nextLong(1, 1000000);
} }
</source> </source>
The following will produce a different random sequence each time it is executed. The following will produce a different random sequence each time it is
executed.
<source> <source>
RandomDataImpl randomData = new RandomDataImpl(); RandomDataImpl randomData = new RandomDataImpl();
randomData.reSeedSecure(1000); randomData.reSeedSecure(1000);
for (int i = 0; i &lt; 1000; i++) { for (int i = 0; i &lt; 1000; i++) {
value = randomData.nextSecureLong(1, 1000000); value = randomData.nextSecureLong(1, 1000000);
} }
</source> </source>
</dd></dl> </dd></dl>
</p> </p>
@ -228,6 +263,87 @@
</p> </p>
</subsection> </subsection>
<subsection name="2.6 PRNG Pluggability" href="pluggability">
<p>
To enable alternative PRNGs to be "plugged in" to the commons-math data
generation utilities and to provide a generic means to replace
<code>java.util.Random</code> in applications, a random generator
adaptor framework has been added to commons-math. The
<a href="../apidocs/org/apache/commons/math/random/RandomGenerator.html">
org.apache.commons.math.RandomGenerator</a> interface abstracts the public
interface of <code>java.util.Random</code> and any implementation of this
interface can be used as the source of random data for the commons-math
data generation classes. An abstract superclass,
<a href="../apidocs/org/apache/commons/math/random/AbstractRandomGenerator.html">
org.apache.commons.math.AbstractRandomGenerator</a> is provided to make
implementation easier. This class provides default implementations of
"derived" data generation methods based on the primitive,
<code>nextDouble().</code> To support generic replacement of
<code>java.util.Random</code>, the
<a href="../apidocs/org/apache/commons/math/random/RandomAdaptor.html">
org.apache.commons.math.RandomAdaptor</a> class is provided, which
extends <code>java.util.Random</code> and wraps and delegates calls to
a <code>RandomGenerator</code> instance.
</p>
<p>
Examples:
<dl>
<dt>Create a RandomGenerator based on RngPack's Mersenne Twister</dt>
<dd>To create a RandomGenerator using the RngPack Mersenne Twister PRNG
as the source of randomness, extend <code>AbstractRandomGenerator</code>
overriding the derived methods that the RngPack implementation provides:
<source>
import edu.cornell.lassp.houle.RngPack.RanMT;
/**
* AbstractRandomGenerator based on RngPack RanMT generator.
*/
public class RngPackGenerator extends AbstractRandomGenerator {
private RanMT random = new RanMT();
public void setSeed(long seed) {
random = new RanMT(seed);
}
public double nextDouble() {
return random.raw();
}
public double nextGaussian() {
return random.gaussian();
}
public int nextInt(int n) {
return random.choose(n);
}
public boolean nextBoolean() {
return random.coin();
}
}
</source>
</dd>
<dt>Use the Mersenne Twister RandomGenerator in place of
<code>java.util.Random</code> in <code>RandomData</code></dt>
<dd>
<source>
RandomData randomData = new RandomDataImpl(new RngPackGenerator());
</source>
</dd>
<dt>Create an adaptor instance based on the Mersenne Twister generator
that can be used in place of a <code>Random</code></dt>
<dd>
<source>
RandomGenerator generator = new RngPackGenerator();
Random random = RandomAdaptor.createAdaptor(generator);
// random can now be used in place of a Random instance, data generation
// calls will be delegated to the wrapped Mersenne Twister
</source>
</dd>
</dl>
</p>
</subsection>
</section> </section>
</body> </body>