001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.math3.random; 019 020import java.io.Serializable; 021import java.security.MessageDigest; 022import java.security.NoSuchAlgorithmException; 023import java.security.NoSuchProviderException; 024import java.security.SecureRandom; 025import java.util.Collection; 026 027import org.apache.commons.math3.distribution.BetaDistribution; 028import org.apache.commons.math3.distribution.BinomialDistribution; 029import org.apache.commons.math3.distribution.CauchyDistribution; 030import org.apache.commons.math3.distribution.ChiSquaredDistribution; 031import org.apache.commons.math3.distribution.ExponentialDistribution; 032import org.apache.commons.math3.distribution.FDistribution; 033import org.apache.commons.math3.distribution.GammaDistribution; 034import org.apache.commons.math3.distribution.HypergeometricDistribution; 035import org.apache.commons.math3.distribution.PascalDistribution; 036import org.apache.commons.math3.distribution.PoissonDistribution; 037import org.apache.commons.math3.distribution.TDistribution; 038import org.apache.commons.math3.distribution.WeibullDistribution; 039import org.apache.commons.math3.distribution.ZipfDistribution; 040import org.apache.commons.math3.distribution.UniformIntegerDistribution; 041import org.apache.commons.math3.exception.MathInternalError; 042import org.apache.commons.math3.exception.NotANumberException; 043import org.apache.commons.math3.exception.NotFiniteNumberException; 044import org.apache.commons.math3.exception.NotPositiveException; 045import org.apache.commons.math3.exception.NotStrictlyPositiveException; 046import org.apache.commons.math3.exception.NumberIsTooLargeException; 047import org.apache.commons.math3.exception.OutOfRangeException; 048import org.apache.commons.math3.exception.util.LocalizedFormats; 049import org.apache.commons.math3.util.MathArrays; 050 051/** 052 * Implements the {@link RandomData} interface using a {@link RandomGenerator} 053 * instance to generate non-secure data and a {@link java.security.SecureRandom} 054 * instance to provide data for the <code>nextSecureXxx</code> methods. If no 055 * <code>RandomGenerator</code> is provided in the constructor, the default is 056 * to use a {@link Well19937c} generator. To plug in a different 057 * implementation, either implement <code>RandomGenerator</code> directly or 058 * extend {@link AbstractRandomGenerator}. 059 * <p> 060 * Supports reseeding the underlying pseudo-random number generator (PRNG). The 061 * <code>SecurityProvider</code> and <code>Algorithm</code> used by the 062 * <code>SecureRandom</code> instance can also be reset. 063 * </p> 064 * <p> 065 * For details on the default PRNGs, see {@link java.util.Random} and 066 * {@link java.security.SecureRandom}. 067 * </p> 068 * <p> 069 * <strong>Usage Notes</strong>: 070 * <ul> 071 * <li> 072 * Instance variables are used to maintain <code>RandomGenerator</code> and 073 * <code>SecureRandom</code> instances used in data generation. Therefore, to 074 * generate a random sequence of values or strings, you should use just 075 * <strong>one</strong> <code>RandomDataImpl</code> instance repeatedly.</li> 076 * <li> 077 * The "secure" methods are *much* slower. These should be used only when a 078 * cryptographically secure random sequence is required. A secure random 079 * sequence is a sequence of pseudo-random values which, in addition to being 080 * well-dispersed (so no subsequence of values is an any more likely than other 081 * subsequence of the the same length), also has the additional property that 082 * knowledge of values generated up to any point in the sequence does not make 083 * it any easier to predict subsequent values.</li> 084 * <li> 085 * When a new <code>RandomDataImpl</code> is created, the underlying random 086 * number generators are <strong>not</strong> initialized. If you do not 087 * explicitly seed the default non-secure generator, it is seeded with the 088 * current time in milliseconds plus the system identity hash code on first use. 089 * The same holds for the secure generator. If you provide a <code>RandomGenerator</code> 090 * to the constructor, however, this generator is not reseeded by the constructor 091 * nor is it reseeded on first use.</li> 092 * <li> 093 * The <code>reSeed</code> and <code>reSeedSecure</code> methods delegate to the 094 * corresponding methods on the underlying <code>RandomGenerator</code> and 095 * <code>SecureRandom</code> instances. Therefore, <code>reSeed(long)</code> 096 * fully resets the initial state of the non-secure random number generator (so 097 * that reseeding with a specific value always results in the same subsequent 098 * random sequence); whereas reSeedSecure(long) does <strong>not</strong> 099 * reinitialize the secure random number generator (so secure sequences started 100 * with calls to reseedSecure(long) won't be identical).</li> 101 * <li> 102 * This implementation is not synchronized. The underlying <code>RandomGenerator</code> 103 * or <code>SecureRandom</code> instances are not protected by synchronization and 104 * are not guaranteed to be thread-safe. Therefore, if an instance of this class 105 * is concurrently utilized by multiple threads, it is the responsibility of 106 * client code to synchronize access to seeding and data generation methods. 107 * </li> 108 * </ul> 109 * </p> 110 * @since 3.1 111 */ 112public class RandomDataGenerator implements RandomData, Serializable { 113 114 /** Serializable version identifier */ 115 private static final long serialVersionUID = -626730818244969716L; 116 117 /** underlying random number generator */ 118 private RandomGenerator rand = null; 119 120 /** underlying secure random number generator */ 121 private RandomGenerator secRand = null; 122 123 /** 124 * Construct a RandomDataGenerator, using a default random generator as the source 125 * of randomness. 126 * 127 * <p>The default generator is a {@link Well19937c} seeded 128 * with {@code System.currentTimeMillis() + System.identityHashCode(this))}. 129 * The generator is initialized and seeded on first use.</p> 130 */ 131 public RandomDataGenerator() { 132 } 133 134 /** 135 * Construct a RandomDataGenerator using the supplied {@link RandomGenerator} as 136 * the source of (non-secure) random data. 137 * 138 * @param rand the source of (non-secure) random data 139 * (may be null, resulting in the default generator) 140 */ 141 public RandomDataGenerator(RandomGenerator rand) { 142 this.rand = rand; 143 } 144 145 /** 146 * {@inheritDoc} 147 * <p> 148 * <strong>Algorithm Description:</strong> hex strings are generated using a 149 * 2-step process. 150 * <ol> 151 * <li>{@code len / 2 + 1} binary bytes are generated using the underlying 152 * Random</li> 153 * <li>Each binary byte is translated into 2 hex digits</li> 154 * </ol> 155 * </p> 156 * 157 * @param len the desired string length. 158 * @return the random string. 159 * @throws NotStrictlyPositiveException if {@code len <= 0}. 160 */ 161 public String nextHexString(int len) throws NotStrictlyPositiveException { 162 if (len <= 0) { 163 throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len); 164 } 165 166 // Get a random number generator 167 RandomGenerator ran = getRandomGenerator(); 168 169 // Initialize output buffer 170 StringBuilder outBuffer = new StringBuilder(); 171 172 // Get int(len/2)+1 random bytes 173 byte[] randomBytes = new byte[(len / 2) + 1]; 174 ran.nextBytes(randomBytes); 175 176 // Convert each byte to 2 hex digits 177 for (int i = 0; i < randomBytes.length; i++) { 178 Integer c = Integer.valueOf(randomBytes[i]); 179 180 /* 181 * Add 128 to byte value to make interval 0-255 before doing hex 182 * conversion. This guarantees <= 2 hex digits from toHexString() 183 * toHexString would otherwise add 2^32 to negative arguments. 184 */ 185 String hex = Integer.toHexString(c.intValue() + 128); 186 187 // Make sure we add 2 hex digits for each byte 188 if (hex.length() == 1) { 189 hex = "0" + hex; 190 } 191 outBuffer.append(hex); 192 } 193 return outBuffer.toString().substring(0, len); 194 } 195 196 /** {@inheritDoc} */ 197 public int nextInt(final int lower, final int upper) throws NumberIsTooLargeException { 198 return new UniformIntegerDistribution(getRandomGenerator(), lower, upper).sample(); 199 } 200 201 /** {@inheritDoc} */ 202 public long nextLong(final long lower, final long upper) throws NumberIsTooLargeException { 203 if (lower >= upper) { 204 throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, 205 lower, upper, false); 206 } 207 final long max = (upper - lower) + 1; 208 if (max <= 0) { 209 // the range is too wide to fit in a positive long (larger than 2^63); as it covers 210 // more than half the long range, we use directly a simple rejection method 211 final RandomGenerator rng = getRandomGenerator(); 212 while (true) { 213 final long r = rng.nextLong(); 214 if (r >= lower && r <= upper) { 215 return r; 216 } 217 } 218 } else if (max < Integer.MAX_VALUE){ 219 // we can shift the range and generate directly a positive int 220 return lower + getRandomGenerator().nextInt((int) max); 221 } else { 222 // we can shift the range and generate directly a positive long 223 return lower + nextLong(getRandomGenerator(), max); 224 } 225 } 226 227 /** 228 * Returns a pseudorandom, uniformly distributed {@code long} value 229 * between 0 (inclusive) and the specified value (exclusive), drawn from 230 * this random number generator's sequence. 231 * 232 * @param rng random generator to use 233 * @param n the bound on the random number to be returned. Must be 234 * positive. 235 * @return a pseudorandom, uniformly distributed {@code long} 236 * value between 0 (inclusive) and n (exclusive). 237 * @throws IllegalArgumentException if n is not positive. 238 */ 239 private static long nextLong(final RandomGenerator rng, final long n) throws IllegalArgumentException { 240 if (n > 0) { 241 final byte[] byteArray = new byte[8]; 242 long bits; 243 long val; 244 do { 245 rng.nextBytes(byteArray); 246 bits = 0; 247 for (final byte b : byteArray) { 248 bits = (bits << 8) | (((long) b) & 0xffL); 249 } 250 bits &= 0x7fffffffffffffffL; 251 val = bits % n; 252 } while (bits - val + (n - 1) < 0); 253 return val; 254 } 255 throw new NotStrictlyPositiveException(n); 256 } 257 258 /** 259 * {@inheritDoc} 260 * <p> 261 * <strong>Algorithm Description:</strong> hex strings are generated in 262 * 40-byte segments using a 3-step process. 263 * <ol> 264 * <li> 265 * 20 random bytes are generated using the underlying 266 * <code>SecureRandom</code>.</li> 267 * <li> 268 * SHA-1 hash is applied to yield a 20-byte binary digest.</li> 269 * <li> 270 * Each byte of the binary digest is converted to 2 hex digits.</li> 271 * </ol> 272 * </p> 273 * @throws NotStrictlyPositiveException if {@code len <= 0} 274 */ 275 public String nextSecureHexString(int len) throws NotStrictlyPositiveException { 276 if (len <= 0) { 277 throw new NotStrictlyPositiveException(LocalizedFormats.LENGTH, len); 278 } 279 280 // Get SecureRandom and setup Digest provider 281 final RandomGenerator secRan = getSecRan(); 282 MessageDigest alg = null; 283 try { 284 alg = MessageDigest.getInstance("SHA-1"); 285 } catch (NoSuchAlgorithmException ex) { 286 // this should never happen 287 throw new MathInternalError(ex); 288 } 289 alg.reset(); 290 291 // Compute number of iterations required (40 bytes each) 292 int numIter = (len / 40) + 1; 293 294 StringBuilder outBuffer = new StringBuilder(); 295 for (int iter = 1; iter < numIter + 1; iter++) { 296 byte[] randomBytes = new byte[40]; 297 secRan.nextBytes(randomBytes); 298 alg.update(randomBytes); 299 300 // Compute hash -- will create 20-byte binary hash 301 byte[] hash = alg.digest(); 302 303 // Loop over the hash, converting each byte to 2 hex digits 304 for (int i = 0; i < hash.length; i++) { 305 Integer c = Integer.valueOf(hash[i]); 306 307 /* 308 * Add 128 to byte value to make interval 0-255 This guarantees 309 * <= 2 hex digits from toHexString() toHexString would 310 * otherwise add 2^32 to negative arguments 311 */ 312 String hex = Integer.toHexString(c.intValue() + 128); 313 314 // Keep strings uniform length -- guarantees 40 bytes 315 if (hex.length() == 1) { 316 hex = "0" + hex; 317 } 318 outBuffer.append(hex); 319 } 320 } 321 return outBuffer.toString().substring(0, len); 322 } 323 324 /** {@inheritDoc} */ 325 public int nextSecureInt(final int lower, final int upper) throws NumberIsTooLargeException { 326 return new UniformIntegerDistribution(getSecRan(), lower, upper).sample(); 327 } 328 329 /** {@inheritDoc} */ 330 public long nextSecureLong(final long lower, final long upper) throws NumberIsTooLargeException { 331 if (lower >= upper) { 332 throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, 333 lower, upper, false); 334 } 335 final RandomGenerator rng = getSecRan(); 336 final long max = (upper - lower) + 1; 337 if (max <= 0) { 338 // the range is too wide to fit in a positive long (larger than 2^63); as it covers 339 // more than half the long range, we use directly a simple rejection method 340 while (true) { 341 final long r = rng.nextLong(); 342 if (r >= lower && r <= upper) { 343 return r; 344 } 345 } 346 } else if (max < Integer.MAX_VALUE){ 347 // we can shift the range and generate directly a positive int 348 return lower + rng.nextInt((int) max); 349 } else { 350 // we can shift the range and generate directly a positive long 351 return lower + nextLong(rng, max); 352 } 353 } 354 355 /** 356 * {@inheritDoc} 357 * <p> 358 * <strong>Algorithm Description</strong>: 359 * <ul><li> For small means, uses simulation of a Poisson process 360 * using Uniform deviates, as described 361 * <a href="http://irmi.epfl.ch/cmos/Pmmi/interactive/rng7.htm"> here.</a> 362 * The Poisson process (and hence value returned) is bounded by 1000 * mean.</li> 363 * 364 * <li> For large means, uses the rejection algorithm described in <br/> 365 * Devroye, Luc. (1981).<i>The Computer Generation of Poisson Random Variables</i> 366 * <strong>Computing</strong> vol. 26 pp. 197-207.</li></ul></p> 367 * @throws NotStrictlyPositiveException if {@code len <= 0} 368 */ 369 public long nextPoisson(double mean) throws NotStrictlyPositiveException { 370 return new PoissonDistribution(getRandomGenerator(), mean, 371 PoissonDistribution.DEFAULT_EPSILON, 372 PoissonDistribution.DEFAULT_MAX_ITERATIONS).sample(); 373 } 374 375 /** {@inheritDoc} */ 376 public double nextGaussian(double mu, double sigma) throws NotStrictlyPositiveException { 377 if (sigma <= 0) { 378 throw new NotStrictlyPositiveException(LocalizedFormats.STANDARD_DEVIATION, sigma); 379 } 380 return sigma * getRandomGenerator().nextGaussian() + mu; 381 } 382 383 /** 384 * {@inheritDoc} 385 * 386 * <p> 387 * <strong>Algorithm Description</strong>: Uses the Algorithm SA (Ahrens) 388 * from p. 876 in: 389 * [1]: Ahrens, J. H. and Dieter, U. (1972). Computer methods for 390 * sampling from the exponential and normal distributions. 391 * Communications of the ACM, 15, 873-882. 392 * </p> 393 */ 394 public double nextExponential(double mean) throws NotStrictlyPositiveException { 395 return new ExponentialDistribution(getRandomGenerator(), mean, 396 ExponentialDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 397 } 398 399 /** 400 * <p>Generates a random value from the 401 * {@link org.apache.commons.math3.distribution.GammaDistribution Gamma Distribution}.</p> 402 * 403 * <p>This implementation uses the following algorithms: </p> 404 * 405 * <p>For 0 < shape < 1: <br/> 406 * Ahrens, J. H. and Dieter, U., <i>Computer methods for 407 * sampling from gamma, beta, Poisson and binomial distributions.</i> 408 * Computing, 12, 223-246, 1974.</p> 409 * 410 * <p>For shape >= 1: <br/> 411 * Marsaglia and Tsang, <i>A Simple Method for Generating 412 * Gamma Variables.</i> ACM Transactions on Mathematical Software, 413 * Volume 26 Issue 3, September, 2000.</p> 414 * 415 * @param shape the median of the Gamma distribution 416 * @param scale the scale parameter of the Gamma distribution 417 * @return random value sampled from the Gamma(shape, scale) distribution 418 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 419 * {@code scale <= 0}. 420 */ 421 public double nextGamma(double shape, double scale) throws NotStrictlyPositiveException { 422 return new GammaDistribution(getRandomGenerator(),shape, scale, 423 GammaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 424 } 425 426 /** 427 * Generates a random value from the {@link HypergeometricDistribution Hypergeometric Distribution}. 428 * 429 * @param populationSize the population size of the Hypergeometric distribution 430 * @param numberOfSuccesses number of successes in the population of the Hypergeometric distribution 431 * @param sampleSize the sample size of the Hypergeometric distribution 432 * @return random value sampled from the Hypergeometric(numberOfSuccesses, sampleSize) distribution 433 * @throws NumberIsTooLargeException if {@code numberOfSuccesses > populationSize}, 434 * or {@code sampleSize > populationSize}. 435 * @throws NotStrictlyPositiveException if {@code populationSize <= 0}. 436 * @throws NotPositiveException if {@code numberOfSuccesses < 0}. 437 */ 438 public int nextHypergeometric(int populationSize, int numberOfSuccesses, int sampleSize) throws NotPositiveException, NotStrictlyPositiveException, NumberIsTooLargeException { 439 return new HypergeometricDistribution(getRandomGenerator(),populationSize, 440 numberOfSuccesses, sampleSize).sample(); 441 } 442 443 /** 444 * Generates a random value from the {@link PascalDistribution Pascal Distribution}. 445 * 446 * @param r the number of successes of the Pascal distribution 447 * @param p the probability of success of the Pascal distribution 448 * @return random value sampled from the Pascal(r, p) distribution 449 * @throws NotStrictlyPositiveException if the number of successes is not positive 450 * @throws OutOfRangeException if the probability of success is not in the 451 * range {@code [0, 1]}. 452 */ 453 public int nextPascal(int r, double p) throws NotStrictlyPositiveException, OutOfRangeException { 454 return new PascalDistribution(getRandomGenerator(), r, p).sample(); 455 } 456 457 /** 458 * Generates a random value from the {@link TDistribution T Distribution}. 459 * 460 * @param df the degrees of freedom of the T distribution 461 * @return random value from the T(df) distribution 462 * @throws NotStrictlyPositiveException if {@code df <= 0} 463 */ 464 public double nextT(double df) throws NotStrictlyPositiveException { 465 return new TDistribution(getRandomGenerator(), df, 466 TDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 467 } 468 469 /** 470 * Generates a random value from the {@link WeibullDistribution Weibull Distribution}. 471 * 472 * @param shape the shape parameter of the Weibull distribution 473 * @param scale the scale parameter of the Weibull distribution 474 * @return random value sampled from the Weibull(shape, size) distribution 475 * @throws NotStrictlyPositiveException if {@code shape <= 0} or 476 * {@code scale <= 0}. 477 */ 478 public double nextWeibull(double shape, double scale) throws NotStrictlyPositiveException { 479 return new WeibullDistribution(getRandomGenerator(), shape, scale, 480 WeibullDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 481 } 482 483 /** 484 * Generates a random value from the {@link ZipfDistribution Zipf Distribution}. 485 * 486 * @param numberOfElements the number of elements of the ZipfDistribution 487 * @param exponent the exponent of the ZipfDistribution 488 * @return random value sampled from the Zipf(numberOfElements, exponent) distribution 489 * @exception NotStrictlyPositiveException if {@code numberOfElements <= 0} 490 * or {@code exponent <= 0}. 491 */ 492 public int nextZipf(int numberOfElements, double exponent) throws NotStrictlyPositiveException { 493 return new ZipfDistribution(getRandomGenerator(), numberOfElements, exponent).sample(); 494 } 495 496 /** 497 * Generates a random value from the {@link BetaDistribution Beta Distribution}. 498 * 499 * @param alpha first distribution shape parameter 500 * @param beta second distribution shape parameter 501 * @return random value sampled from the beta(alpha, beta) distribution 502 */ 503 public double nextBeta(double alpha, double beta) { 504 return new BetaDistribution(getRandomGenerator(), alpha, beta, 505 BetaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 506 } 507 508 /** 509 * Generates a random value from the {@link BinomialDistribution Binomial Distribution}. 510 * 511 * @param numberOfTrials number of trials of the Binomial distribution 512 * @param probabilityOfSuccess probability of success of the Binomial distribution 513 * @return random value sampled from the Binomial(numberOfTrials, probabilityOfSuccess) distribution 514 */ 515 public int nextBinomial(int numberOfTrials, double probabilityOfSuccess) { 516 return new BinomialDistribution(getRandomGenerator(), numberOfTrials, probabilityOfSuccess).sample(); 517 } 518 519 /** 520 * Generates a random value from the {@link CauchyDistribution Cauchy Distribution}. 521 * 522 * @param median the median of the Cauchy distribution 523 * @param scale the scale parameter of the Cauchy distribution 524 * @return random value sampled from the Cauchy(median, scale) distribution 525 */ 526 public double nextCauchy(double median, double scale) { 527 return new CauchyDistribution(getRandomGenerator(), median, scale, 528 CauchyDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 529 } 530 531 /** 532 * Generates a random value from the {@link ChiSquaredDistribution ChiSquare Distribution}. 533 * 534 * @param df the degrees of freedom of the ChiSquare distribution 535 * @return random value sampled from the ChiSquare(df) distribution 536 */ 537 public double nextChiSquare(double df) { 538 return new ChiSquaredDistribution(getRandomGenerator(), df, 539 ChiSquaredDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 540 } 541 542 /** 543 * Generates a random value from the {@link FDistribution F Distribution}. 544 * 545 * @param numeratorDf the numerator degrees of freedom of the F distribution 546 * @param denominatorDf the denominator degrees of freedom of the F distribution 547 * @return random value sampled from the F(numeratorDf, denominatorDf) distribution 548 * @throws NotStrictlyPositiveException if 549 * {@code numeratorDf <= 0} or {@code denominatorDf <= 0}. 550 */ 551 public double nextF(double numeratorDf, double denominatorDf) throws NotStrictlyPositiveException { 552 return new FDistribution(getRandomGenerator(), numeratorDf, denominatorDf, 553 FDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY).sample(); 554 } 555 556 /** 557 * {@inheritDoc} 558 * 559 * <p> 560 * <strong>Algorithm Description</strong>: scales the output of 561 * Random.nextDouble(), but rejects 0 values (i.e., will generate another 562 * random double if Random.nextDouble() returns 0). This is necessary to 563 * provide a symmetric output interval (both endpoints excluded). 564 * </p> 565 * @throws NumberIsTooLargeException if {@code lower >= upper} 566 * @throws NotFiniteNumberException if one of the bounds is infinite 567 * @throws NotANumberException if one of the bounds is NaN 568 */ 569 public double nextUniform(double lower, double upper) 570 throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException { 571 return nextUniform(lower, upper, false); 572 } 573 574 /** 575 * {@inheritDoc} 576 * 577 * <p> 578 * <strong>Algorithm Description</strong>: if the lower bound is excluded, 579 * scales the output of Random.nextDouble(), but rejects 0 values (i.e., 580 * will generate another random double if Random.nextDouble() returns 0). 581 * This is necessary to provide a symmetric output interval (both 582 * endpoints excluded). 583 * </p> 584 * 585 * @throws NumberIsTooLargeException if {@code lower >= upper} 586 * @throws NotFiniteNumberException if one of the bounds is infinite 587 * @throws NotANumberException if one of the bounds is NaN 588 */ 589 public double nextUniform(double lower, double upper, boolean lowerInclusive) 590 throws NumberIsTooLargeException, NotFiniteNumberException, NotANumberException { 591 592 if (lower >= upper) { 593 throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND, 594 lower, upper, false); 595 } 596 597 if (Double.isInfinite(lower)) { 598 throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, lower); 599 } 600 if (Double.isInfinite(upper)) { 601 throw new NotFiniteNumberException(LocalizedFormats.INFINITE_BOUND, upper); 602 } 603 604 if (Double.isNaN(lower) || Double.isNaN(upper)) { 605 throw new NotANumberException(); 606 } 607 608 final RandomGenerator generator = getRandomGenerator(); 609 610 // ensure nextDouble() isn't 0.0 611 double u = generator.nextDouble(); 612 while (!lowerInclusive && u <= 0.0) { 613 u = generator.nextDouble(); 614 } 615 616 return u * upper + (1.0 - u) * lower; 617 } 618 619 /** 620 * {@inheritDoc} 621 * 622 * This method calls {@link MathArrays#shuffle(int[],RandomGenerator) 623 * MathArrays.shuffle} in order to create a random shuffle of the set 624 * of natural numbers {@code { 0, 1, ..., n - 1 }}. 625 * 626 * @throws NumberIsTooLargeException if {@code k > n}. 627 * @throws NotStrictlyPositiveException if {@code k <= 0}. 628 */ 629 public int[] nextPermutation(int n, int k) 630 throws NumberIsTooLargeException, NotStrictlyPositiveException { 631 if (k > n) { 632 throw new NumberIsTooLargeException(LocalizedFormats.PERMUTATION_EXCEEDS_N, 633 k, n, true); 634 } 635 if (k <= 0) { 636 throw new NotStrictlyPositiveException(LocalizedFormats.PERMUTATION_SIZE, 637 k); 638 } 639 640 int[] index = MathArrays.natural(n); 641 MathArrays.shuffle(index, getRandomGenerator()); 642 643 // Return a new array containing the first "k" entries of "index". 644 return MathArrays.copyOf(index, k); 645 } 646 647 /** 648 * {@inheritDoc} 649 * 650 * This method calls {@link #nextPermutation(int,int) nextPermutation(c.size(), k)} 651 * in order to sample the collection. 652 */ 653 public Object[] nextSample(Collection<?> c, int k) throws NumberIsTooLargeException, NotStrictlyPositiveException { 654 655 int len = c.size(); 656 if (k > len) { 657 throw new NumberIsTooLargeException(LocalizedFormats.SAMPLE_SIZE_EXCEEDS_COLLECTION_SIZE, 658 k, len, true); 659 } 660 if (k <= 0) { 661 throw new NotStrictlyPositiveException(LocalizedFormats.NUMBER_OF_SAMPLES, k); 662 } 663 664 Object[] objects = c.toArray(); 665 int[] index = nextPermutation(len, k); 666 Object[] result = new Object[k]; 667 for (int i = 0; i < k; i++) { 668 result[i] = objects[index[i]]; 669 } 670 return result; 671 } 672 673 674 675 /** 676 * Reseeds the random number generator with the supplied seed. 677 * <p> 678 * Will create and initialize if null. 679 * </p> 680 * 681 * @param seed the seed value to use 682 */ 683 public void reSeed(long seed) { 684 getRandomGenerator().setSeed(seed); 685 } 686 687 /** 688 * Reseeds the secure random number generator with the current time in 689 * milliseconds. 690 * <p> 691 * Will create and initialize if null. 692 * </p> 693 */ 694 public void reSeedSecure() { 695 getSecRan().setSeed(System.currentTimeMillis()); 696 } 697 698 /** 699 * Reseeds the secure random number generator with the supplied seed. 700 * <p> 701 * Will create and initialize if null. 702 * </p> 703 * 704 * @param seed the seed value to use 705 */ 706 public void reSeedSecure(long seed) { 707 getSecRan().setSeed(seed); 708 } 709 710 /** 711 * Reseeds the random number generator with 712 * {@code System.currentTimeMillis() + System.identityHashCode(this))}. 713 */ 714 public void reSeed() { 715 getRandomGenerator().setSeed(System.currentTimeMillis() + System.identityHashCode(this)); 716 } 717 718 /** 719 * Sets the PRNG algorithm for the underlying SecureRandom instance using 720 * the Security Provider API. The Security Provider API is defined in <a 721 * href = 722 * "http://java.sun.com/j2se/1.3/docs/guide/security/CryptoSpec.html#AppA"> 723 * Java Cryptography Architecture API Specification & Reference.</a> 724 * <p> 725 * <strong>USAGE NOTE:</strong> This method carries <i>significant</i> 726 * overhead and may take several seconds to execute. 727 * </p> 728 * 729 * @param algorithm the name of the PRNG algorithm 730 * @param provider the name of the provider 731 * @throws NoSuchAlgorithmException if the specified algorithm is not available 732 * @throws NoSuchProviderException if the specified provider is not installed 733 */ 734 public void setSecureAlgorithm(String algorithm, String provider) 735 throws NoSuchAlgorithmException, NoSuchProviderException { 736 secRand = RandomGeneratorFactory.createRandomGenerator(SecureRandom.getInstance(algorithm, provider)); 737 } 738 739 /** 740 * Returns the RandomGenerator used to generate non-secure random data. 741 * <p> 742 * Creates and initializes a default generator if null. Uses a {@link Well19937c} 743 * generator with {@code System.currentTimeMillis() + System.identityHashCode(this))} 744 * as the default seed. 745 * </p> 746 * 747 * @return the Random used to generate random data 748 * @since 3.2 749 */ 750 public RandomGenerator getRandomGenerator() { 751 if (rand == null) { 752 initRan(); 753 } 754 return rand; 755 } 756 757 /** 758 * Sets the default generator to a {@link Well19937c} generator seeded with 759 * {@code System.currentTimeMillis() + System.identityHashCode(this))}. 760 */ 761 private void initRan() { 762 rand = new Well19937c(System.currentTimeMillis() + System.identityHashCode(this)); 763 } 764 765 /** 766 * Returns the SecureRandom used to generate secure random data. 767 * <p> 768 * Creates and initializes if null. Uses 769 * {@code System.currentTimeMillis() + System.identityHashCode(this)} as the default seed. 770 * </p> 771 * 772 * @return the SecureRandom used to generate secure random data, wrapped in a 773 * {@link RandomGenerator}. 774 */ 775 private RandomGenerator getSecRan() { 776 if (secRand == null) { 777 secRand = RandomGeneratorFactory.createRandomGenerator(new SecureRandom()); 778 secRand.setSeed(System.currentTimeMillis() + System.identityHashCode(this)); 779 } 780 return secRand; 781 } 782}