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