| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| RandomDataImpl |
|
| 3.217391304347826;3.217 |
| 1 | /* |
|
| 2 | * Copyright 2003-2004 The Apache Software Foundation. |
|
| 3 | * |
|
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
| 5 | * you may not use this file except in compliance with the License. |
|
| 6 | * You may obtain a copy of the License at |
|
| 7 | * |
|
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
| 9 | * |
|
| 10 | * Unless required by applicable law or agreed to in writing, software |
|
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 13 | * See the License for the specific language governing permissions and |
|
| 14 | * limitations under the License. |
|
| 15 | */ |
|
| 16 | ||
| 17 | package org.apache.commons.math.random; |
|
| 18 | ||
| 19 | import java.io.Serializable; |
|
| 20 | import java.security.MessageDigest; |
|
| 21 | import java.security.SecureRandom; |
|
| 22 | import java.security.NoSuchAlgorithmException; |
|
| 23 | import java.security.NoSuchProviderException; |
|
| 24 | import java.util.Collection; |
|
| 25 | ||
| 26 | /** |
|
| 27 | * Implements the {@link RandomData} interface using a {@link RandomGenerator} |
|
| 28 | * instance to generate non-secure data and a |
|
| 29 | * {@link java.security.SecureRandom} instance to provide data for the |
|
| 30 | * <code>nextSecureXxx</code> methods. If no <code>RandomGenerator</code> |
|
| 31 | * is provided in the constructor, the default is to use a generator based on |
|
| 32 | * {@link java.util.Random}. To plug in a different implementation, |
|
| 33 | * either implement <code>RandomGenerator</code> directly or extend |
|
| 34 | * {@link AbstractRandomGenerator}. |
|
| 35 | * <p> |
|
| 36 | * Supports reseeding the underlying pseudo-random number generator (PRNG). |
|
| 37 | * The <code>SecurityProvider</code> and <code>Algorithm</code> |
|
| 38 | * used by the <code>SecureRandom</code> instance can also be reset. |
|
| 39 | * <p> |
|
| 40 | * For details on the default PRNGs, see {@link java.util.Random} and |
|
| 41 | * {@link java.security.SecureRandom}. |
|
| 42 | * <p> |
|
| 43 | * <strong>Usage Notes</strong>: <ul> |
|
| 44 | * <li> |
|
| 45 | * Instance variables are used to maintain <code>RandomGenerator</code> and |
|
| 46 | * <code>SecureRandom</code> instances used in data generation. Therefore, |
|
| 47 | * to generate a random sequence of values or strings, you should use just |
|
| 48 | * <strong>one</strong> <code>RandomDataImpl</code> instance repeatedly.</li> |
|
| 49 | * <li> |
|
| 50 | * The "secure" methods are *much* slower. These should be used only when a |
|
| 51 | * cryptographically secure random sequence is required. A secure random |
|
| 52 | * sequence is a sequence of pseudo-random values which, in addition to being |
|
| 53 | * well-dispersed (so no subsequence of values is an any more likely than other |
|
| 54 | * subsequence of the the same length), also has the additional property that |
|
| 55 | * knowledge of values generated up to any point in the sequence does not make |
|
| 56 | * it any easier to predict subsequent values.</li> |
|
| 57 | * <li> |
|
| 58 | * When a new <code>RandomDataImpl</code> is created, the underlying random |
|
| 59 | * number generators are <strong>not</strong> intialized. If you do not |
|
| 60 | * explicitly seed the default non-secure generator, it is seeded with the current time |
|
| 61 | * in milliseconds on first use. The same holds for the secure generator. |
|
| 62 | * If you provide a <code>RandomGenerator</code> to the constructor, however, |
|
| 63 | * this generator is not reseeded by the constructor nor is it reseeded on |
|
| 64 | * first use. </li> |
|
| 65 | * <li> |
|
| 66 | * The <code>reSeed</code> and <code>reSeedSecure</code> methods delegate |
|
| 67 | * to the corresponding methods on the underlying <code>RandomGenerator</code> |
|
| 68 | * and<code>SecureRandom</code> instances. Therefore, |
|
| 69 | * <code>reSeed(long)</code> fully resets the initial state of the non-secure |
|
| 70 | * random number generator (so that reseeding with a specific value always |
|
| 71 | * results in the same subsequent random sequence); whereas reSeedSecure(long) |
|
| 72 | * does <strong>not</strong> reinitialize the secure random number generator |
|
| 73 | * (so secure sequences started with calls to reseedSecure(long) won't be |
|
| 74 | * identical).</li> |
|
| 75 | * <li> |
|
| 76 | * This implementation is not synchronized. |
|
| 77 | * </ul> |
|
| 78 | * |
|
| 79 | * @version $Revision$ $Date: 2005-05-21 22:25:44 -0700 (Sat, 21 May 2005) $ |
|
| 80 | */ |
|
| 81 | public class RandomDataImpl implements RandomData, Serializable { |
|
| 82 | ||
| 83 | /** Serializable version identifier */ |
|
| 84 | static final long serialVersionUID = -626730818244969716L; |
|
| 85 | ||
| 86 | /** underlying random number generator */ |
|
| 87 | 210 | private RandomGenerator rand = null; |
| 88 | ||
| 89 | /** underlying secure random number generator */ |
|
| 90 | 210 | private SecureRandom secRand = null; |
| 91 | ||
| 92 | /** |
|
| 93 | * Construct a RandomDataImpl. |
|
| 94 | */ |
|
| 95 | 180 | public RandomDataImpl() { |
| 96 | 180 | } |
| 97 | ||
| 98 | /** |
|
| 99 | * Construct a RandomDataImpl using the supplied {@link RandomGenerator} |
|
| 100 | * as the source of (non-secure) random data. |
|
| 101 | * |
|
| 102 | * @param rand the source of (non-secure) random data |
|
| 103 | * @since 1.1 |
|
| 104 | */ |
|
| 105 | public RandomDataImpl(RandomGenerator rand) { |
|
| 106 | 30 | super(); |
| 107 | 30 | this.rand = rand; |
| 108 | 30 | } |
| 109 | ||
| 110 | /** |
|
| 111 | * <strong>Algorithm Description:</strong> hex strings are generated |
|
| 112 | * using a 2-step process. <ol> |
|
| 113 | * <li> |
|
| 114 | * len/2+1 binary bytes are generated using the underlying Random</li> |
|
| 115 | * <li> |
|
| 116 | * Each binary byte is translated into 2 hex digits</li></ol> |
|
| 117 | * @param len the desired string length. |
|
| 118 | * @return the random string. |
|
| 119 | */ |
|
| 120 | public String nextHexString(int len) { |
|
| 121 | 6030 | if (len <= 0) { |
| 122 | 18 | throw new IllegalArgumentException("length must be positive"); |
| 123 | } |
|
| 124 | ||
| 125 | //Get a random number generator |
|
| 126 | 6012 | RandomGenerator ran = getRan(); |
| 127 | ||
| 128 | //Initialize output buffer |
|
| 129 | 6012 | StringBuffer outBuffer = new StringBuffer(); |
| 130 | ||
| 131 | //Get int(len/2)+1 random bytes |
|
| 132 | 6012 | byte[] randomBytes = new byte[(len / 2) + 1]; |
| 133 | 6012 | ran.nextBytes(randomBytes); |
| 134 | ||
| 135 | //Convert each byte to 2 hex digits |
|
| 136 | 312030 | for (int i = 0; i < randomBytes.length; i++) { |
| 137 | 306018 | Integer c = new Integer(randomBytes[i]); |
| 138 | ||
| 139 | /* Add 128 to byte value to make interval 0-255 before |
|
| 140 | * doing hex conversion. |
|
| 141 | * This guarantees <= 2 hex digits from toHexString() |
|
| 142 | * toHexString would otherwise add 2^32 to negative arguments. |
|
| 143 | */ |
|
| 144 | 306018 | String hex = Integer.toHexString(c.intValue() + 128); |
| 145 | ||
| 146 | // Make sure we add 2 hex digits for each byte |
|
| 147 | 306018 | if (hex.length() == 1) { |
| 148 | 19321 | hex = "0" + hex; |
| 149 | } |
|
| 150 | 306018 | outBuffer.append(hex); |
| 151 | } |
|
| 152 | 6012 | return outBuffer.toString().substring(0, len); |
| 153 | } |
|
| 154 | ||
| 155 | /** |
|
| 156 | * Generate a random int value uniformly distributed between |
|
| 157 | * <code>lower</code> and <code>upper</code>, inclusive. |
|
| 158 | * |
|
| 159 | * @param lower the lower bound. |
|
| 160 | * @param upper the upper bound. |
|
| 161 | * @return the random integer. |
|
| 162 | */ |
|
| 163 | public int nextInt(int lower, int upper) { |
|
| 164 | 24044 | if (lower >= upper) { |
| 165 | 4 | throw new IllegalArgumentException |
| 166 | ("upper bound must be > lower bound"); |
|
| 167 | } |
|
| 168 | 24040 | RandomGenerator rand = getRan(); |
| 169 | 24040 | return lower + (int) (rand.nextDouble() * (upper - lower + 1)); |
| 170 | } |
|
| 171 | ||
| 172 | /** |
|
| 173 | * Generate a random long value uniformly distributed between |
|
| 174 | * <code>lower</code> and <code>upper</code>, inclusive. |
|
| 175 | * |
|
| 176 | * @param lower the lower bound. |
|
| 177 | * @param upper the upper bound. |
|
| 178 | * @return the random integer. |
|
| 179 | */ |
|
| 180 | public long nextLong(long lower, long upper) { |
|
| 181 | 4016 | if (lower >= upper) { |
| 182 | 4 | throw new IllegalArgumentException |
| 183 | ("upper bound must be > lower bound"); |
|
| 184 | } |
|
| 185 | 4012 | RandomGenerator rand = getRan(); |
| 186 | 4012 | return lower + (long) (rand.nextDouble() * (upper - lower + 1)); |
| 187 | } |
|
| 188 | ||
| 189 | /** |
|
| 190 | * <strong>Algorithm Description:</strong> hex strings are generated in |
|
| 191 | * 40-byte segments using a 3-step process. <ol> |
|
| 192 | * <li> |
|
| 193 | * 20 random bytes are generated using the underlying |
|
| 194 | * <code>SecureRandom</code>.</li> |
|
| 195 | * <li> |
|
| 196 | * SHA-1 hash is applied to yield a 20-byte binary digest.</li> |
|
| 197 | * <li> |
|
| 198 | * Each byte of the binary digest is converted to 2 hex digits.</li></ol> |
|
| 199 | * |
|
| 200 | * @param len the length of the generated string |
|
| 201 | * @return the random string |
|
| 202 | */ |
|
| 203 | public String nextSecureHexString(int len) { |
|
| 204 | 6054 | if (len <= 0) { |
| 205 | 18 | throw new IllegalArgumentException("length must be positive"); |
| 206 | } |
|
| 207 | ||
| 208 | // Get SecureRandom and setup Digest provider |
|
| 209 | 6036 | SecureRandom secRan = getSecRan(); |
| 210 | 6036 | MessageDigest alg = null; |
| 211 | try { |
|
| 212 | 6036 | alg = MessageDigest.getInstance("SHA-1"); |
| 213 | 0 | } catch (NoSuchAlgorithmException ex) { |
| 214 | 0 | return null; // gulp FIXME? -- this *should* never fail. |
| 215 | 6036 | } |
| 216 | 6036 | alg.reset(); |
| 217 | ||
| 218 | //Compute number of iterations required (40 bytes each) |
|
| 219 | 6036 | int numIter = (len / 40) + 1; |
| 220 | ||
| 221 | 6036 | StringBuffer outBuffer = new StringBuffer(); |
| 222 | 24096 | for (int iter = 1; iter < numIter + 1; iter++) { |
| 223 | 18060 | byte[] randomBytes = new byte[40]; |
| 224 | 18060 | secRan.nextBytes(randomBytes); |
| 225 | 18060 | alg.update(randomBytes); |
| 226 | ||
| 227 | //Compute hash -- will create 20-byte binary hash |
|
| 228 | 18060 | byte hash[] = alg.digest(); |
| 229 | ||
| 230 | //Loop over the hash, converting each byte to 2 hex digits |
|
| 231 | 379260 | for (int i = 0; i < hash.length; i++) { |
| 232 | 361200 | Integer c = new Integer(hash[i]); |
| 233 | ||
| 234 | /* Add 128 to byte value to make interval 0-255 |
|
| 235 | * This guarantees <= 2 hex digits from toHexString() |
|
| 236 | * toHexString would otherwise add 2^32 to negative |
|
| 237 | * arguments |
|
| 238 | */ |
|
| 239 | 361200 | String hex = Integer.toHexString(c.intValue() + 128); |
| 240 | ||
| 241 | //Keep strings uniform length -- guarantees 40 bytes |
|
| 242 | 361200 | if (hex.length() == 1) { |
| 243 | 22478 | hex = "0" + hex; |
| 244 | } |
|
| 245 | 361200 | outBuffer.append(hex); |
| 246 | } |
|
| 247 | } |
|
| 248 | 6036 | return outBuffer.toString().substring(0, len); |
| 249 | } |
|
| 250 | ||
| 251 | /** |
|
| 252 | * Generate a random int value uniformly distributed between |
|
| 253 | * <code>lower</code> and <code>upper</code>, inclusive. This algorithm |
|
| 254 | * uses a secure random number generator. |
|
| 255 | * |
|
| 256 | * @param lower the lower bound. |
|
| 257 | * @param upper the upper bound. |
|
| 258 | * @return the random integer. |
|
| 259 | */ |
|
| 260 | public int nextSecureInt(int lower, int upper) { |
|
| 261 | 6006 | if (lower >= upper) { |
| 262 | 6 | throw new IllegalArgumentException |
| 263 | ("lower bound must be < upper bound"); |
|
| 264 | } |
|
| 265 | 6000 | SecureRandom sec = getSecRan(); |
| 266 | 6000 | return lower + (int) (sec.nextDouble() * (upper - lower + 1)); |
| 267 | } |
|
| 268 | ||
| 269 | /** |
|
| 270 | * Generate a random long value uniformly distributed between |
|
| 271 | * <code>lower</code> and <code>upper</code>, inclusive. This algorithm |
|
| 272 | * uses a secure random number generator. |
|
| 273 | * |
|
| 274 | * @param lower the lower bound. |
|
| 275 | * @param upper the upper bound. |
|
| 276 | * @return the random integer. |
|
| 277 | */ |
|
| 278 | public long nextSecureLong(long lower, long upper) { |
|
| 279 | 6018 | if (lower >= upper) { |
| 280 | 6 | throw new IllegalArgumentException |
| 281 | ("lower bound must be < upper bound"); |
|
| 282 | } |
|
| 283 | 6012 | SecureRandom sec = getSecRan(); |
| 284 | 6012 | return lower + (long) (sec.nextDouble() * (upper - lower + 1)); |
| 285 | } |
|
| 286 | ||
| 287 | /** |
|
| 288 | * Generates a random long value from the Poisson distribution with the |
|
| 289 | * given mean. |
|
| 290 | * <p> |
|
| 291 | * <strong>Algorithm Description</strong>: |
|
| 292 | * Uses simulation of a Poisson process using Uniform deviates, as |
|
| 293 | * described |
|
| 294 | * <a href="http://irmi.epfl.ch/cmos/Pmmi/interactive/rng7.htm"> |
|
| 295 | * here.</a> |
|
| 296 | * <p> |
|
| 297 | * The Poisson process (and hence value returned) is bounded by |
|
| 298 | * 1000 * mean. |
|
| 299 | * |
|
| 300 | * @param mean mean of the Poisson distribution. |
|
| 301 | * @return the random Poisson value. |
|
| 302 | */ |
|
| 303 | public long nextPoisson(double mean) { |
|
| 304 | 60018 | if (mean <= 0) { |
| 305 | 18 | throw new IllegalArgumentException("Poisson mean must be > 0"); |
| 306 | } |
|
| 307 | 60000 | double p = Math.exp(-mean); |
| 308 | 60000 | long n = 0; |
| 309 | 60000 | double r = 1.0d; |
| 310 | 60000 | double rnd = 1.0d; |
| 311 | 60000 | RandomGenerator rand = getRan(); |
| 312 | 299635 | while (n < 1000 * mean) { |
| 313 | 299635 | rnd = rand.nextDouble(); |
| 314 | 299635 | r = r * rnd; |
| 315 | 299635 | if (r >= p) { |
| 316 | 239635 | n++; |
| 317 | } else { |
|
| 318 | 60000 | return n; |
| 319 | } |
|
| 320 | } |
|
| 321 | 0 | return n; |
| 322 | } |
|
| 323 | ||
| 324 | /** |
|
| 325 | * Generate a random value from a Normal (a.k.a. Gaussian) distribution |
|
| 326 | * with the given mean, <code>mu</code> and the given standard deviation, |
|
| 327 | * <code>sigma</code>. |
|
| 328 | * |
|
| 329 | * @param mu the mean of the distribution |
|
| 330 | * @param sigma the standard deviation of the distribution |
|
| 331 | * @return the random Normal value |
|
| 332 | */ |
|
| 333 | public double nextGaussian(double mu, double sigma) { |
|
| 334 | 74486 | if (sigma <= 0) { |
| 335 | 6 | throw new IllegalArgumentException("Gaussian std dev must be > 0"); |
| 336 | } |
|
| 337 | 74480 | RandomGenerator rand = getRan(); |
| 338 | 74480 | return sigma * rand.nextGaussian() + mu; |
| 339 | } |
|
| 340 | ||
| 341 | /** |
|
| 342 | * Returns a random value from an Exponential distribution with the given |
|
| 343 | * mean. |
|
| 344 | * <p> |
|
| 345 | * <strong>Algorithm Description</strong>: Uses the |
|
| 346 | * <a href="http://www.jesus.ox.ac.uk/~clifford/a5/chap1/node5.html"> |
|
| 347 | * Inversion Method</a> to generate exponentially distributed random values |
|
| 348 | * from uniform deviates. |
|
| 349 | * |
|
| 350 | * @param mean the mean of the distribution |
|
| 351 | * @return the random Exponential value |
|
| 352 | */ |
|
| 353 | public double nextExponential(double mean) { |
|
| 354 | 60014 | if (mean < 0.0) { |
| 355 | 6 | throw new IllegalArgumentException |
| 356 | ("Exponential mean must be >= 0"); |
|
| 357 | } |
|
| 358 | 60008 | RandomGenerator rand = getRan(); |
| 359 | 60008 | double unif = rand.nextDouble(); |
| 360 | 60008 | while (unif == 0.0d) { |
| 361 | 0 | unif = rand.nextDouble(); |
| 362 | } |
|
| 363 | 60008 | return -mean * Math.log(unif); |
| 364 | } |
|
| 365 | ||
| 366 | /** |
|
| 367 | * <strong>Algorithm Description</strong>: scales the output of |
|
| 368 | * Random.nextDouble(), but rejects 0 values (i.e., will generate another |
|
| 369 | * random double if Random.nextDouble() returns 0). |
|
| 370 | * This is necessary to provide a symmetric output interval |
|
| 371 | * (both endpoints excluded). |
|
| 372 | * |
|
| 373 | * @param lower the lower bound. |
|
| 374 | * @param upper the upper bound. |
|
| 375 | * @return a uniformly distributed random value from the interval (lower, upper) |
|
| 376 | */ |
|
| 377 | public double nextUniform(double lower, double upper) { |
|
| 378 | 6032 | if (lower >= upper) { |
| 379 | 12 | throw new IllegalArgumentException |
| 380 | ("lower bound must be <= upper bound"); |
|
| 381 | } |
|
| 382 | 6020 | RandomGenerator rand = getRan(); |
| 383 | ||
| 384 | // ensure nextDouble() isn't 0.0 |
|
| 385 | 6020 | double u = rand.nextDouble(); |
| 386 | 6020 | while(u <= 0.0){ |
| 387 | 0 | u = rand.nextDouble(); |
| 388 | } |
|
| 389 | ||
| 390 | 6020 | return lower + u * (upper - lower); |
| 391 | } |
|
| 392 | ||
| 393 | /** |
|
| 394 | * Returns the RandomGenerator used to generate non-secure |
|
| 395 | * random data. |
|
| 396 | * <p> |
|
| 397 | * Creates and initializes a default generator if null. |
|
| 398 | * |
|
| 399 | * @return the Random used to generate random data |
|
| 400 | * @since 1.1 |
|
| 401 | */ |
|
| 402 | private RandomGenerator getRan() { |
|
| 403 | 234572 | if (rand == null) { |
| 404 | 66 | rand = new JDKRandomGenerator(); |
| 405 | 66 | rand.setSeed(System.currentTimeMillis()); |
| 406 | } |
|
| 407 | 234572 | return rand; |
| 408 | } |
|
| 409 | ||
| 410 | /** |
|
| 411 | * Returns the SecureRandom used to generate secure random data. |
|
| 412 | * <p> |
|
| 413 | * Creates and initializes if null. |
|
| 414 | * |
|
| 415 | * @return the SecureRandom used to generate secure random data |
|
| 416 | */ |
|
| 417 | private SecureRandom getSecRan() { |
|
| 418 | 18048 | if (secRand == null) { |
| 419 | 18 | secRand = new SecureRandom(); |
| 420 | 18 | secRand.setSeed(System.currentTimeMillis()); |
| 421 | } |
|
| 422 | 18048 | return secRand; |
| 423 | } |
|
| 424 | ||
| 425 | /** |
|
| 426 | * Reseeds the random number generator with the supplied seed. |
|
| 427 | * <p> |
|
| 428 | * Will create and initialize if null. |
|
| 429 | * |
|
| 430 | * @param seed the seed value to use |
|
| 431 | */ |
|
| 432 | public void reSeed(long seed) { |
|
| 433 | 18 | if (rand == null) { |
| 434 | 10 | rand = new JDKRandomGenerator(); |
| 435 | } |
|
| 436 | 18 | rand.setSeed(seed); |
| 437 | 18 | } |
| 438 | ||
| 439 | /** |
|
| 440 | * Reseeds the secure random number generator with the current time |
|
| 441 | * in milliseconds. |
|
| 442 | * <p> |
|
| 443 | * Will create and initialize if null. |
|
| 444 | */ |
|
| 445 | public void reSeedSecure() { |
|
| 446 | 12 | if (secRand == null) { |
| 447 | 6 | secRand = new SecureRandom(); |
| 448 | } |
|
| 449 | 12 | secRand.setSeed(System.currentTimeMillis()); |
| 450 | 12 | } |
| 451 | ||
| 452 | /** |
|
| 453 | * Reseeds the secure random number generator with the supplied seed. |
|
| 454 | * <p> |
|
| 455 | * Will create and initialize if null. |
|
| 456 | * |
|
| 457 | * @param seed the seed value to use |
|
| 458 | */ |
|
| 459 | public void reSeedSecure(long seed) { |
|
| 460 | 18 | if (secRand == null) { |
| 461 | 12 | secRand = new SecureRandom(); |
| 462 | } |
|
| 463 | 18 | secRand.setSeed(seed); |
| 464 | 18 | } |
| 465 | ||
| 466 | /** |
|
| 467 | * Reseeds the random number generator with the current time |
|
| 468 | * in milliseconds. |
|
| 469 | */ |
|
| 470 | public void reSeed() { |
|
| 471 | 12 | if (rand == null) { |
| 472 | 6 | rand = new JDKRandomGenerator(); |
| 473 | } |
|
| 474 | 12 | rand.setSeed(System.currentTimeMillis()); |
| 475 | 12 | } |
| 476 | ||
| 477 | /** |
|
| 478 | * Sets the PRNG algorithm for the underlying SecureRandom instance |
|
| 479 | * using the Security Provider API. The Security Provider API is defined in |
|
| 480 | * <a href="http://java.sun.com/j2se/1.3/docs/guide/security/CryptoSpec.html#AppA"> |
|
| 481 | * Java Cryptography Architecture API Specification & Reference.</a> |
|
| 482 | * <p> |
|
| 483 | * <strong>USAGE NOTE:</strong> This method carries <i>significant</i> |
|
| 484 | * overhead and may take several seconds to execute. |
|
| 485 | * </p> |
|
| 486 | * |
|
| 487 | * @param algorithm the name of the PRNG algorithm |
|
| 488 | * @param provider the name of the provider |
|
| 489 | * @throws NoSuchAlgorithmException if the specified algorithm |
|
| 490 | * is not available |
|
| 491 | * @throws NoSuchProviderException if the specified provider |
|
| 492 | * is not installed |
|
| 493 | */ |
|
| 494 | public void setSecureAlgorithm(String algorithm, String provider) |
|
| 495 | throws NoSuchAlgorithmException, NoSuchProviderException { |
|
| 496 | 18 | secRand = SecureRandom.getInstance(algorithm, provider); |
| 497 | 6 | } |
| 498 | ||
| 499 | /** |
|
| 500 | * Uses a 2-cycle permutation shuffle to generate a random permutation. |
|
| 501 | * The shuffling process is described |
|
| 502 | * <a href="http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node83.html"> |
|
| 503 | * here</a>. |
|
| 504 | * @param n the population size. |
|
| 505 | * @param k the number to choose. |
|
| 506 | * @return the random permutation. |
|
| 507 | */ |
|
| 508 | public int[] nextPermutation(int n, int k) { |
|
| 509 | 9696 | if (k > n) { |
| 510 | 0 | throw new IllegalArgumentException |
| 511 | ("permutation k exceeds n"); |
|
| 512 | } |
|
| 513 | 9696 | if (k == 0) { |
| 514 | 0 | throw new IllegalArgumentException |
| 515 | ("permutation k must be > 0"); |
|
| 516 | } |
|
| 517 | ||
| 518 | 9696 | int[] index = getNatural(n); |
| 519 | 9696 | shuffle(index, n - k); |
| 520 | 9696 | int[] result = new int[k]; |
| 521 | 33348 | for (int i = 0; i < k; i++) { |
| 522 | 23652 | result[i] = index[n - i - 1]; |
| 523 | } |
|
| 524 | ||
| 525 | 9696 | return result; |
| 526 | } |
|
| 527 | ||
| 528 | /** |
|
| 529 | * Uses a 2-cycle permutation shuffle to generate a random permutation. |
|
| 530 | * <strong>Algorithm Description</strong>: Uses a 2-cycle permutation |
|
| 531 | * shuffle to generate a random permutation of <code>c.size()</code> and |
|
| 532 | * then returns the elements whose indexes correspond to the elements of |
|
| 533 | * the generated permutation. |
|
| 534 | * This technique is described, and proven to generate random samples, |
|
| 535 | * <a href="http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node83.html"> |
|
| 536 | * here</a> |
|
| 537 | * @param c Collection to sample from. |
|
| 538 | * @param k sample size. |
|
| 539 | * @return the random sample. |
|
| 540 | */ |
|
| 541 | public Object[] nextSample(Collection c, int k) { |
|
| 542 | 6018 | int len = c.size(); |
| 543 | 6018 | if (k > len) { |
| 544 | 6 | throw new IllegalArgumentException |
| 545 | ("sample size exceeds collection size"); |
|
| 546 | } |
|
| 547 | 6012 | if (k == 0) { |
| 548 | 6 | throw new IllegalArgumentException |
| 549 | ("sample size must be > 0"); |
|
| 550 | } |
|
| 551 | ||
| 552 | 6006 | Object[] objects = c.toArray(); |
| 553 | 6006 | int[] index = nextPermutation(len, k); |
| 554 | 6006 | Object[] result = new Object[k]; |
| 555 | 18012 | for (int i = 0; i < k; i++) { |
| 556 | 12006 | result[i] = objects[index[i]]; |
| 557 | } |
|
| 558 | 6006 | return result; |
| 559 | } |
|
| 560 | ||
| 561 | //------------------------Private methods---------------------------------- |
|
| 562 | ||
| 563 | /** |
|
| 564 | * Uses a 2-cycle permutation shuffle to randomly re-order the last elements |
|
| 565 | * of list. |
|
| 566 | * |
|
| 567 | * @param list list to be shuffled |
|
| 568 | * @param end element past which shuffling begins |
|
| 569 | */ |
|
| 570 | private void shuffle(int[] list, int end) { |
|
| 571 | 9696 | int target = 0; |
| 572 | 33348 | for (int i = list.length - 1 ; i >= end; i--) { |
| 573 | 23652 | if (i == 0) { |
| 574 | 3616 | target = 0; |
| 575 | } else { |
|
| 576 | 20036 | target = nextInt(0, i); |
| 577 | } |
|
| 578 | 23652 | int temp = list[target]; |
| 579 | 23652 | list[target] = list[i]; |
| 580 | 23652 | list[i] = temp; |
| 581 | } |
|
| 582 | 9696 | } |
| 583 | ||
| 584 | /** |
|
| 585 | * Returns an array representing n. |
|
| 586 | * |
|
| 587 | * @param n the natural number to represent |
|
| 588 | * @return array with entries = elements of n |
|
| 589 | */ |
|
| 590 | private int[] getNatural(int n) { |
|
| 591 | 9696 | int[] natural = new int[n]; |
| 592 | 51708 | for (int i = 0; i < n; i++) { |
| 593 | 42012 | natural[i] = i; |
| 594 | } |
|
| 595 | 9696 | return natural; |
| 596 | } |
|
| 597 | } |