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 | } |