From ba74c2510f0d1812b7a5eeef55a1f29ea0e06e09 Mon Sep 17 00:00:00 2001 From: "Mark R. Diggory" Date: Wed, 4 Jun 2003 02:45:49 +0000 Subject: [PATCH] PR: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=20442 Submitted by: Phil Steitz git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/math/trunk@140885 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/commons/math/RandomData.java | 212 +++++++++----- .../apache/commons/math/RandomDataImpl.java | 266 +++++++++--------- .../apache/commons/math/RandomDataTest.java | 24 +- 3 files changed, 288 insertions(+), 214 deletions(-) diff --git a/src/java/org/apache/commons/math/RandomData.java b/src/java/org/apache/commons/math/RandomData.java index 7b93df866..938e64a23 100644 --- a/src/java/org/apache/commons/math/RandomData.java +++ b/src/java/org/apache/commons/math/RandomData.java @@ -58,196 +58,260 @@ import java.util.Collection; /** * Random data generation utilities * @author Phil Steitz - * @version $Revision: 1.2 $ $Date: 2003/05/29 19:45:35 $ + * @version $Revision: 1.3 $ $Date: 2003/06/04 02:45:49 $ */ public interface RandomData { /** * Generates a random string of hex characters of length - * len.
+ * len. + *

* The generated string will be random, but not cryptographically * secure. To generate cryptographically secure strings, use * nextSecureHexString + *

+ * Preconditions:

+ * * @param len the length of the string to be generated - * @throws IllegalArgumentException if len is not positive. + * @return random string of hex characters of length len */ - public String nextHexString(int len); + String nextHexString(int len); /** * Generates a uniformly distributed random integer between - * lower and upper (endpoints included).
+ * lower and upper (endpoints included). + *

* The generated integer will be random, but not cryptographically secure. * To generate cryptographically secure integer sequences, use * nextSecureInt. + *

+ * Preconditions:

+ * * @param lower lower bound for generated integer * @param upper upper bound for generated integer - * @exception IllegalArgumentException thrown if - * lower is not strictly less than upper. * @return a random integer greater than or equal to lower * and less than or equal to upper. */ - public int nextInt(int lower, int upper); + int nextInt(int lower, int upper); /** - * Generates a uniformly distributed random long integer between < - * code>lower and upper (endpoints included). + * Generates a uniformly distributed random long integer between + * lower and upper (endpoints included). + *

* The generated long integer values will be random, but not - * cryptographically secure.
+ * cryptographically secure. * To generate cryptographically secure sequences of longs, use * nextSecureLong + *

+ * Preconditions:

+ * * @param lower lower bound for generated integer * @param upper upper bound for generated integer - * @exception IllegalArgumentException Thrown if lower > upper * @return a random integer greater than or equal to lower * and less than or equal to upper. */ - public long nextLong(long lower, long upper); + long nextLong(long lower, long upper); /** - * Generates a random string of hex characters from a secure random sequence. + * Generates a random string of hex characters from a secure random + * sequence. + *

* If cryptographic security is not required, * use nextHexString(). + *

+ * Preconditions:

* @param len length of return string - * @exception IllegalArgumentException thrown if len <= 0 * @return the random hex string */ - public String nextSecureHexString(int len); + String nextSecureHexString(int len); /** * Generates a uniformly distributed random integer between * lower and upper (endpoints included) - * from a secure random sequence.
- * The generated sequence will be cryptographically secure.
- * If cryptographic security is not required, nextInt - * should be used.
- * Definition(secure random sequence): - * http://www.wikipedia.org/wiki/Cryptographically_secure_pseudo-random_number_generator
+ * from a secure random sequence. + *

+ * Sequences of integers generated using this method will be + * cryptographically secure. If cryptographic security is not required, + * nextInt should be used instead of this method. + *

+ * Definition: + * + * Secure Random Sequence + *

+ * Preconditions:

+ * * @param lower lower bound for generated integer * @param upper upper bound for generated integer - * @exception IllegalArgumentException thrown if - * lower is not strictly less than upper. * @return a random integer greater than or equal to lower * and less than or equal to upper. */ - public int nextSecureInt(int lower, int upper); + int nextSecureInt(int lower, int upper); /** * Generates a random long integer between lower - * and upper (endpoints included).
- * The generated long sequence will be cryptographically secure.
- * If cryptographic security is not required, - * use nextLong
+ * and upper (endpoints included).

+ * Sequences of long values generated using this method will be + * cryptographically secure. If cryptographic security is not required, + * nextLong should be used instead of this method. + *

* Definition: - * + * * Secure Random Sequence + *

+ * Preconditions:

+ * * @param lower lower bound for generated integer * @param upper upper bound for generated integer - * @exception IllegalArgumentException thrown if - * lower is not strictly less than upper. * @return a long integer greater than or equal to lower * and less than or equal to upper. */ - public long nextSecureLong(long lower, long upper); + long nextSecureLong(long lower, long upper); /** * Generates a random value from the Poisson distribution with - * the given mean.
+ * the given mean. + *

* Definition: - * - * Poisson Distribution
+ * + * Poisson Distribution + *

* Preconditions:

* @param mean Mean of the distribution - * @returns long - * @throws IllegalArgumentException if mean <= 0 + * @return poisson deviate with the specified mean */ - public long nextPoisson(double mean); + long nextPoisson(double mean); /** * Generates a random value from the - * Normal (a.k.a. Gaussian) distribution with the given mean - * and standard deviation.
+ * Normal (or Gaussian) distribution with the given mean + * and standard deviation. + *

* Definition: - * - * Normal Distribution
+ * + * Normal Distribution + *

* Preconditions:

* @param mu Mean of the distribution * @param sigma Standard deviation of the distribution * @return random value from Gaussian distribution with mean = mu, * standard deviation = sigma - * @throws IllegalArgumentExcption if sigma <= 0 */ - public double nextGaussian(double mu,double sigma); + double nextGaussian(double mu, double sigma); /** * Generates a random value from the exponential distribution - * with expected value = mean
+ * with expected value = mean. + *

* Definition: - * - * Exponential Distribution
+ * + * Exponential Distribution + *

* Preconditions:

- * @param mu Mean of the distribution + * @param mean Mean of the distribution * @return random value from exponential distribution */ - public double nextExponential(double mean); + double nextExponential(double mean); /** * Generates a uniformly distributed random value from the open interval - * (lower,upper) (i.e., endpoints excluded) + * (lower,upper) (i.e., endpoints excluded). + *

* Definition: - * - * Uniform Distribution lower and upper - lower - * are the - * - * location and scale parameters, respectively
+ * + * Uniform Distribution lower and + * upper - lower are the + * + * location and scale parameters, respectively. + *

+ * Preconditions:

+ * * @param lower lower endpoint of the interval of support * @param upper upper endpoint of the interval of support * @return uniformly distributed random value between lower * and upper (exclusive) - * @exception IllegalArgumentException thrown if - * lower is not strictly less than upper. */ - public double nextUniform(double lower, double upper); + double nextUniform(double lower, double upper); /** * Generates an integer array of length k whose entries - * are selected randomly, without repetition, from the integers - * {0, ... , n-1} -- i.e., generated arrays represent permutations - * of n taken k at a time.

- * + * are selected randomly, without repetition, from the integers + * 0 through n-1 (inclusive). + *

+ * Generated arrays represent permutations + * of n taken k at a time. + *

* Preconditions:

+ * If the preconditions are not met, an IllegalArgumentException is + * thrown. * * @param n domain of the permutation * @param k size of the permutation * @return random k-permutation of n */ - public int[] nextPermutation(int n, int k); + int[] nextPermutation(int n, int k); /** * Returns an array of k objects selected randomly - * from the Collection c. Sampling from c + * from the Collection c. + *

+ * Sampling from c * is without replacement; but if c contains identical * objects, the sample may include repeats. If all elements of * c are distinct, the resulting object array represents a - * + * * Simple Random Sample of size - * k from the elements of c.

- * + * k from the elements of c. + *

* Preconditions:

+ * If the preconditions are not met, an IllegalArgumentException is + * thrown. * * @param c collection to be sampled * @param k size of the sample * @return random sample of k elements from c */ - public Object[] nextSample(Collection c, int k); + Object[] nextSample(Collection c, int k); } diff --git a/src/java/org/apache/commons/math/RandomDataImpl.java b/src/java/org/apache/commons/math/RandomDataImpl.java index d0502b87b..2f7e34e7c 100644 --- a/src/java/org/apache/commons/math/RandomDataImpl.java +++ b/src/java/org/apache/commons/math/RandomDataImpl.java @@ -64,42 +64,50 @@ import java.util.Collection; /** * Implements the RandomData interface using * java.util.Random and - * java.util.Random.SecureRandom instances to generate data. + * java.util.Random.SecureRandom instances to generate data. + *

* Supports reseeding the underlying - * PRNG. - * The SecurityProvider and Algorithm - * used by the SecureRandom instance can also be reset.

+ * + * PRNG. The SecurityProvider and Algorithm + * used by the SecureRandom instance can also be reset. + *

* For details on the PRNGs, see the JDK documentation for * java.util.Random and - * java.util.Random.SecureRandom

+ * java.util.Random.SecureRandom + *

* Usage Notes:

- *

+ *
  • + * The reSeed and reSeedSecure methods delegate + * to the corresponding methods on the underlying Random and + * SecureRandom instances. Therefore, the contracts of these + * methods are as defined in the JDK documentation. In particular, + * reSeed(long) fully resets the initial state of the non-secure + * random number generator (so that reseeding with a specific value always + * results in the same subsequent random sequence); whereas reSeedSecure(long) + * does not reinitialize the secure random number generator + * (so secure sequences started with calls to reseedSecure(long) won't be + * identical).
  • * * @author Phil Steitz - * @version $Revision: 1.2 $ $Date: 2003/05/29 19:45:35 $ + * @version $Revision: 1.3 $ $Date: 2003/06/04 02:45:49 $ */ -public class RandomDataImpl implements RandomData{ +public class RandomDataImpl implements RandomData { /** underlying random number generator */ private Random rand = null; @@ -107,20 +115,19 @@ public class RandomDataImpl implements RandomData{ /** underlying secure random number generator */ private SecureRandom secRand = null; - public RandomDataImpl(){ + /** + * Construct a RandomDataImpl. + */ + public RandomDataImpl() { } /** - * Generates a random string of hex characters - * If cryptographic security is required, use - * nextSecureHexString().
    * Algorithm Description: hex strings are generated * using a 2-step process.
      - *
    1. len/2+1 binary bytes are generated using the underlying Random
    2. - *
    3. Each binary byte is translated into 2 hex digits
    - * @param len length of return string - * @exception IllegalArgumentException thrown if len <= 0 - * @return the random hex string + *
  • + * len/2+1 binary bytes are generated using the underlying Random
  • + *
  • + * Each binary byte is translated into 2 hex digits
  • */ public String nextHexString(int len) { if (len <= 0) { @@ -146,23 +153,24 @@ public class RandomDataImpl implements RandomData{ * This guarantees <= 2 hex digits from toHexString() * toHexString would otherwise add 2^32 to negative arguments. */ - String hex = Integer.toHexString(c.intValue()+128); + String hex = Integer.toHexString(c.intValue() + 128); // Make sure we add 2 hex digits for each byte - if (hex.length() == 1) hex = "0" + hex; + if (hex.length() == 1) { + hex = "0" + hex; + } outBuffer.append(hex); } return outBuffer.toString().substring(0, len); } - - + public int nextInt(int lower, int upper) { if (lower >= upper) { throw new IllegalArgumentException ("upper bound must be > lower bound"); } Random rand = getRan(); - return lower + (int)(Math.random() * (upper-lower+1)); + return lower + (int) (Math.random() * (upper - lower + 1)); } public long nextLong(long lower, long upper) { @@ -171,23 +179,22 @@ public class RandomDataImpl implements RandomData{ ("upper bound must be > lower bound"); } Random rand = getRan(); - return lower + (long)(rand.nextDouble() * (upper-lower+1)); + return lower + (long) (rand.nextDouble() * (upper - lower + 1)); } /** - * Generates a random string of hex characters from a secure random sequence. - * If cryptographic security is not required, - * use nextHexString().
    - * Algorithm Description: hex strings are generated in 40-byte - * segments using a 3-step process.
      - *
    1. 20 random bytes are generated using the underlying SecureRandom
    2. - *
    3. SHA-1 hash is applied to yield a 20-byte binary digest
    4. - *
    5. Each byte of the binary digest is converted to 2 hex digits

    - * TODO: find external reference or provide justification for the claim that this - * yields a cryptographically secure sequence of hex strings.

    - * @param len length of return string - * @exception IllegalArgumentException thrown if len <= 0 - * @return the random hex string + * Algorithm Description: hex strings are generated in + * 40-byte segments using a 3-step process.
      + *
    1. + * 20 random bytes are generated using the underlying + * SecureRandom.
    2. + *
    3. + * SHA-1 hash is applied to yield a 20-byte binary digest.
    4. + *
    5. + * Each byte of the binary digest is converted to 2 hex digits
    + *

    + * TODO: find external reference or provide justification for the claim + * that this yields a cryptographically secure sequence of hex strings. */ public String nextSecureHexString(int len) { if (len <= 0) { @@ -200,7 +207,7 @@ public class RandomDataImpl implements RandomData{ try { alg = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException ex) { - return null; // gulp FIXME? -- this *should* never fail. OK to swallow???? + return null; // gulp FIXME? -- this *should* never fail. } alg.reset(); @@ -225,48 +232,43 @@ public class RandomDataImpl implements RandomData{ * toHexString would otherwise add 2^32 to negative * arguments */ - String hex = Integer.toHexString(c.intValue()+128); + String hex = Integer.toHexString(c.intValue() + 128); //Keep strings uniform length -- guarantees 40 bytes - if (hex.length() == 1) hex = "0" + hex; + if (hex.length() == 1) { + hex = "0" + hex; + } outBuffer.append(hex); } } return outBuffer.toString().substring(0, len); } - + public int nextSecureInt(int lower, int upper) { if (lower >= upper) { throw new IllegalArgumentException ("lower bound must be < upper bound"); } SecureRandom sec = getSecRan(); - return lower + (int)(sec.nextDouble() * (upper-lower+1)); + return lower + (int) (sec.nextDouble() * (upper - lower + 1)); } - - + public long nextSecureLong(long lower, long upper) { if (lower >= upper) { throw new IllegalArgumentException ("lower bound must be < upper bound"); } SecureRandom sec = getSecRan(); - return lower + (long)(sec.nextDouble() * (upper-lower+1)); + return lower + (long) (sec.nextDouble() * (upper - lower + 1)); } /** - * Generates a random value from the Poisson distribution with - * the given mean.
    - * Definition: - * - * Poisson Distribution
    * Algorithm Description: - * Uses simulation of a Poisson process using Uniform deviates, as described - * + * Uses simulation of a Poisson process using Uniform deviates, as + * described + * * here - * @param mean Mean of the distribution - * @returns long - * @throws IllegalArgumentException if mean <= 0 + * */ public long nextPoisson(double mean) { double p = Math.exp(-mean); @@ -287,59 +289,38 @@ public class RandomDataImpl implements RandomData{ } } - public double nextGaussian(double mu,double sigma) { + public double nextGaussian(double mu, double sigma) { if (sigma <= 0) { throw new IllegalArgumentException("Gaussian std dev must be > 0"); } Random rand = getRan(); - return sigma*rand.nextGaussian() + mu; + return sigma * rand.nextGaussian() + mu; } /** - * Generates a random value from the exponential distribution - * with expected value = mean
    - * Definition: - * - * Exponential Distribution
    - * Preconditions:

    * Algorithm Description: Uses the - * + * * Inversion Method to generate exponential from uniform deviates. - * @param mu Mean of the distribution - * @return random value from exponential distribution */ public double nextExponential(double mean) { - if (mean < 0.0) throw new IllegalArgumentException - ("Exponential mean must be >= 0"); + if (mean < 0.0) { + throw new IllegalArgumentException + ("Exponential mean must be >= 0"); + } Random rand = getRan(); double unif = rand.nextDouble(); while (unif == 0.0d) { unif = rand.nextDouble(); } - return -mean*Math.log(unif); + return -mean * Math.log(unif); } /** - * Generates a uniformly distributed random value from the open interval - * (lower,upper) (i.e., endpoints excluded) - * Definition: - * - * Uniform Distribution lower and 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 endpoint of the interval of support - * @param upper upper endpoint of the interval of support - * @return uniformly distributed random value between lower - * and upper (exclusive) - * @exception IllegalArgumentException thrown if - * lower is not strictly less than upper. + * random double if Random.nextDouble() returns 0). + * This is necessary to provide a symmetric output interval + * (both endpoints excluded). */ public double nextUniform(double lower, double upper) { if (lower >= upper) { @@ -347,16 +328,18 @@ public class RandomDataImpl implements RandomData{ ("lower bound must be <= upper bound"); } Random rand = getRan(); - double result = lower + rand.nextDouble()*(upper-lower); + double result = lower + rand.nextDouble() * (upper - lower); while (result == lower) { - result = lower + rand.nextDouble()*(upper-lower); + result = lower + rand.nextDouble() * (upper - lower); } return result; } /** - * Returns the static Random used to generate random data.
    - * Creates and initializes if null + * Returns the static Random used to generate random data. + *

    + * Creates and initializes if null. + * * @return the static Random used to generate random data */ private Random getRan() { @@ -368,8 +351,10 @@ public class RandomDataImpl implements RandomData{ } /** - * Returns the static SecureRandom used to generate secure random data.
    + * Returns the static SecureRandom used to generate secure random data. + *

    * Creates and initializes if null. + * * @return the static SecureRandom used to generate secure random data */ private SecureRandom getSecRan() { @@ -381,8 +366,10 @@ public class RandomDataImpl implements RandomData{ } /** - * Reseeds the random number generator with the supplied seed. Will - * create and initialize if null. + * Reseeds the random number generator with the supplied seed. + *

    + * Will create and initialize if null. + * * @param seed the seed value to use */ public void reSeed(long seed) { @@ -394,18 +381,22 @@ public class RandomDataImpl implements RandomData{ /** * Reseeds the secure random number generator with the current time - * in milliseconds. Will create and initialize if null. + * in milliseconds. + *

    + * Will create and initialize if null. */ public void reSeedSecure() { - if (rand == null) { - rand = new Random(); + if (secRand == null) { + secRand = new SecureRandom(); } - rand.setSeed(System.currentTimeMillis()); + secRand.setSeed(System.currentTimeMillis()); } /** * Reseeds the secure random number generator with the supplied seed. + *

    * Will create and initialize if null. + * * @param seed the seed value to use */ public void reSeedSecure(long seed) { @@ -417,7 +408,7 @@ public class RandomDataImpl implements RandomData{ /** * Reseeds the random number generator with the current time - * in milliseconds + * in milliseconds. */ public void reSeed() { if (rand == null) { @@ -429,18 +420,23 @@ public class RandomDataImpl implements RandomData{ /** * Sets the PRNG algorithm for the underlying SecureRandom instance * using the Security Provider API, as defined in - * - * Java Cryptography Architecture API Specification & Reference

    - * USAGE NOTE: This method carries significant overhead - * and may take several seconds to execute.

    + * + * Java Cryptography Architecture API Specification & Reference. + *

    + * USAGE NOTE: This method carries significant + * overhead and may take several seconds to execute. + * * @param algorithm the name of the PRNG algorithm * @param provider the name of the provider - * @throws NoSuchAlgorithmException if the specified algorithm is not available - * @throws NoSuchProviderException if the specified provider is not installed + * @throws NoSuchAlgorithmException if the specified algorithm + * is not available + * @throws NoSuchProviderException if the specified provider + * is not installed */ public void setSecureAlgorithm(String algorithm, String provider) - throws NoSuchAlgorithmException,NoSuchProviderException { - secRand = SecureRandom.getInstance(algorithm,provider); + throws NoSuchAlgorithmException, NoSuchProviderException { + secRand = SecureRandom.getInstance(algorithm, provider); } /** @@ -460,22 +456,22 @@ public class RandomDataImpl implements RandomData{ } int[] index = getNatural(n); - shuffle(index,n-k); + shuffle(index, n - k); int[] result = new int[k]; for (int i = 0; i < k; i++) { - result[i] = index[n-i-1]; + result[i] = index[n - i - 1]; } return result; } /** - * Uses a 2-cycle permutation shuffle to generate a random - * permutation of c.size() and then returns the - * elements whose indexes correspond to the elements of the - * generated permutation. This technique is described, and - * proven to generate random samples, - * + * Algorithm Description: Uses a 2-cycle permutation + * shuffle to generate a random permutation of c.size() and + * then returns the elements whose indexes correspond to the elements of + * the generated permutation. + * This technique is described, and proven to generate random samples, + * * here */ public Object[] nextSample(Collection c, int k) { @@ -490,9 +486,9 @@ public class RandomDataImpl implements RandomData{ } Object[] objects = c.toArray(); - int[] index = nextPermutation(len,k); + int[] index = nextPermutation(len, k); Object[] result = new Object[k]; - for (int i = 0; i < k; i ++) { + for (int i = 0; i < k; i++) { result[i] = objects[index[i]]; } return result; @@ -501,19 +497,19 @@ public class RandomDataImpl implements RandomData{ //------------------------Private methods---------------------------------- /** - * Uses a 2-cycle permutation shuffle to randomly re-order the last - * end elements of list + * Algorithm Description: Uses a 2-cycle permutation + * shuffle to randomly re-order the last end elements of list. * * @param list list to be shuffled * @end element past which shuffling begins */ private void shuffle(int[] list, int end) { int target = 0; - for (int i = list.length-1 ; i >= end; i--) { + for (int i = list.length - 1 ; i >= end; i--) { if (i == 0) { target = 0; } else { - target = nextInt(0,i); + target = nextInt(0, i); } int temp = list[target]; list[target] = list[i]; @@ -522,7 +518,7 @@ public class RandomDataImpl implements RandomData{ } /** - * Returns an array representing n + * Returns an array representing n. * * @param n the natural number to represent * @return array with entries = elements of n diff --git a/src/test/org/apache/commons/math/RandomDataTest.java b/src/test/org/apache/commons/math/RandomDataTest.java index 3caa2a45d..161352de8 100644 --- a/src/test/org/apache/commons/math/RandomDataTest.java +++ b/src/test/org/apache/commons/math/RandomDataTest.java @@ -69,7 +69,7 @@ import org.apache.commons.math.stat.UnivariateImpl; * Test cases for the RandomData class. * * @author Phil Steitz - * @version $Revision: 1.4 $ $Date: 2003/05/29 20:35:45 $ + * @version $Revision: 1.5 $ $Date: 2003/06/04 02:45:49 $ */ public final class RandomDataTest extends TestCase { @@ -462,9 +462,9 @@ public final class RandomDataTest extends TestCase { assertTrue("same seeds", !hex.equals(randomData.nextSecureHexString(40))); - /* TODO: probably should remove this test as the package grows, - * since it takes about 4 seconds - */ + /* remove this test back soon, + * since it takes about 4 seconds */ + randomData.setSecureAlgorithm("SHA1PRNG","SUN"); assertTrue("different seeds", !hex.equals(randomData.nextSecureHexString(40))); @@ -480,7 +480,21 @@ public final class RandomDataTest extends TestCase { fail("expecting NoSuchProviderException"); } catch (NoSuchProviderException ex) { ; - } + } + + // test reseeding without first using the generators + RandomDataImpl rd = new RandomDataImpl(); + rd.reSeed(100); + double ret = rd.nextLong(1,2); + RandomDataImpl rd2 = new RandomDataImpl(); + rd2.reSeedSecure(2000); + ret = rd2.nextSecureLong(1,2); + rd = new RandomDataImpl(); + rd.reSeed(); + ret = rd.nextLong(1,2); + rd2 = new RandomDataImpl(); + rd2.reSeedSecure(); + ret = rd2.nextSecureLong(1,2); } /** tests for nextSample() sampling from Collection */