1 /* 2 * Copyright 2013 University of Glasgow. 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 package broadwick.rng; 17 18 import java.io.Serializable; 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.HashSet; 22 import java.util.List; 23 import java.util.Set; 24 import java.util.TreeSet; 25 import lombok.EqualsAndHashCode; 26 import lombok.Getter; 27 import lombok.ToString; 28 import lombok.extern.slf4j.Slf4j; 29 import org.apache.commons.math3.random.MersenneTwister; 30 import org.apache.commons.math3.random.RandomDataGenerator; 31 import org.apache.commons.math3.random.Well19937c; 32 import org.apache.commons.math3.random.Well44497b; 33 34 /** 35 * Random number generator singleton class This class is not thread safe. 36 */ 37 @EqualsAndHashCode 38 @ToString 39 @Slf4j 40 public class RNG implements Serializable { 41 42 /** 43 * Create a random number generator using the default JDK-provied PRNG. 44 */ 45 public RNG() { 46 // By default, the implementation provided in RandomDataImpl uses 47 // the JDK-provided PRNG. Like most other PRNGs, the JDK generator 48 // generates sequences of random numbers based on an initial 49 // "seed value". 50 generator = new RandomDataGenerator(); 51 } 52 53 /** 54 * Create a random number generator from a given generator using the current time as a seed. 55 * @param gen the random number generator to use. 56 */ 57 public RNG(final Generator gen) { 58 if (gen.equals(Generator.MERSENNE)) { 59 generator = new RandomDataGenerator(new MersenneTwister(System.currentTimeMillis() * Thread.currentThread().getId())); 60 name = "Mersenne Twister"; 61 } else if (gen.equals(Generator.Well19937c)) { 62 generator = new RandomDataGenerator(new Well19937c(System.currentTimeMillis() * Thread.currentThread().getId())); 63 name = "Well19937c"; 64 } else if (gen.equals(Generator.Well44497b)) { 65 generator = new RandomDataGenerator(new Well44497b(System.currentTimeMillis() * Thread.currentThread().getId())); 66 name = "Well44497b"; 67 } else { 68 // By default, the implementation provided in RandomDataImpl uses 69 // the JDK-provided PRNG. Like most other PRNGs, the JDK generator 70 // generates sequences of random numbers based on an initial 71 // "seed value". 72 generator = new RandomDataGenerator(); 73 } 74 } 75 76 /** 77 * Get a list of valid geometries for the lattice. 78 * @return List<> a list of valid lattice geometries (in uppercase) 79 */ 80 public static List<String> validGenerators() { 81 final ArrayList<String> generators = new ArrayList<>(3); 82 for (Generator value : Generator.values()) { 83 generators.add(value.name()); 84 } 85 return generators; 86 } 87 88 /** 89 * Seed the random number generator. 90 * @param seed the seed to use in the Rng 91 */ 92 public final void seed(final int seed) { 93 generator.reSeed(seed); 94 } 95 96 /** 97 * Generates a uniformly distributed random value from the open interval ( <code>0.0</code>, <code>1.0</code>) 98 * (i.e., endpoints excluded). 99 * <p> 100 * <strong>Definition</strong>: <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm"> 101 * Uniform Distribution</a> 102 * <code>0.0</code> and <code>1.0 - 0.0</code> are the <a href = 103 * "http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm"> 104 * location and scale parameters</a>, respectively.</p> 105 * @return uniformly distributed random value between lower and upper (exclusive) 106 */ 107 public final double getDouble() { 108 return generator.nextUniform(0.0, 1.0); 109 } 110 111 /** 112 * Generates a uniformly distributed random value from the open interval ( <code>lower</code>, <code>upper</code>) 113 * (i.e., endpoints excluded). 114 * <p> 115 * <strong>Definition</strong>: <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3662.htm"> 116 * Uniform Distribution</a> 117 * <code>lower</code> and <code>upper - lower</code> are the <a href = 118 * "http://www.itl.nist.gov/div898/handbook/eda/section3/eda364.htm"> 119 * location and scale parameters</a>, respectively.</p> 120 * <p> 121 * <strong>Preconditions</strong>:<ul> <li><code>lower < upper</code> (otherwise an IllegalArgumentException is 122 * thrown.)</li> </ul></p> 123 * @param lower lower endpoint of the interval of support 124 * @param upper upper endpoint of the interval of support 125 * @return uniformly distributed random value between lower and upper (exclusive) 126 */ 127 public final double getDouble(final double lower, final double upper) { 128 double lo = lower; 129 double hi = upper; 130 if (lower >= upper) { 131 hi = lower; 132 lo = upper; 133 } 134 if (lower == upper) { 135 return lower; 136 } 137 return generator.nextUniform(lo, hi); 138 } 139 140 /** 141 * Generates a uniformly distributed random integer between <code>lower</code> and <code>upper</code> (endpoints 142 * included). 143 * <p> 144 * The generated integer will be random, but not cryptographically secure. To generate cryptographically secure 145 * integer sequences, use <code>nextSecureInt</code>.</p> 146 * <p> 147 * <strong>Preconditions</strong>:<ul> 148 * <li><code>lower < upper</code> (otherwise an IllegalArgumentException is thrown.)</li> </ul></p> 149 * @param lower lower bound for generated integer 150 * @param upper upper bound for generated integer 151 * @return a random integer greater than or equal to <code>lower</code> and less than or equal * * * to 152 * <code>upper</code>. If lower == upper then lower is returned. 153 */ 154 public final int getInteger(final int lower, final int upper) { 155 int lo = lower; 156 int hi = upper; 157 if (lower >= upper) { 158 hi = lower; 159 lo = upper; 160 } 161 if (lower == upper) { 162 return lower; 163 } 164 return generator.nextInt(lo, hi); 165 } 166 167 /** 168 * Generates a random value from the Poisson distribution with the given mean. 169 * <p> 170 * <strong>Definition</strong>: <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda366j.htm"> 171 * Poisson Distribution</a> 172 * </p> 173 * <p> 174 * <strong>Preconditions</strong>: <ul> 175 * <li>The specified mean <i>must</i> be positive (otherwise an IllegalArgumentException is thrown.)</li> </ul></p> 176 * @param mean Mean of the distribution 177 * @return poisson deviate with the specified mean 178 */ 179 public final long getPoisson(final double mean) { 180 return generator.nextPoisson(mean); 181 } 182 183 /** 184 * Generates a random value from the Normal (or Gaussian) distribution with the given mean and standard deviation. 185 * <p> 186 * <strong>Definition</strong>: 187 * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm"> 188 * Normal Distribution</a></p> 189 * <p> 190 * <strong>Preconditions</strong>: <ul> 191 * <li><code>sigma > 0</code> (otherwise an IllegalArgumentException is thrown.)</li> </ul></p> 192 * @param mu Mean of the distribution 193 * @param sigma Standard deviation given as a percentage of the mean. 194 * @return random value from Gaussian distribution with mean = mu, standard deviation = sigma 195 */ 196 public final double getGaussian(final double mu, final double sigma) { 197 return generator.nextGaussian(mu, sigma); 198 } 199 200 /** 201 * Get a boolean (true/false) value. 202 * @return boolean random true/false value 203 */ 204 public final boolean getBoolean() { 205 return getDouble(0.0, 1.0) < 0.5; 206 } 207 208 /** 209 * Generates a random value from the binomial distribution with the given N and p. The returned value is a random 210 * integer drawn from <DIV ALIGN="CENTER" CLASS="mathdisplay"> <I>P</I>(<I>x</I>) = (<SUP>n</SUP><SUB>x</SUB>) 211 * <I>p</I><SUP>x</SUP>(1-<I>p</I>)<SUP>(<I>n-x</I>)</SUP> </DIV><P> 212 * </P> 213 * Successive draws from this distribution can be combined, i.e. if X ~ getBinomial(n, p) and Y ~ getBinomial(m, p) 214 * are independent binomial variables , then X + Y is again a binomial variable; its distribution is 215 * getBinomial(n+m, p) 216 * @param n the number of trials. 217 * @param p the probability of success of each trial. 218 * @return the number of successes on n trials. 219 */ 220 public final int getBinomial(final int n, final double p) { 221 int x = 0; 222 for (int i = 0; i < n; i++) { 223 if (generator.nextUniform(0.0, 1.0) < p) { 224 x++; 225 } 226 } 227 return x; 228 } 229 230 /** 231 * Randomly pick an object from an array of objects. 232 * @param <T> generic type of the array elements. 233 * @param objects an array of objects, one of whom is to be picked. 234 * @return a random element from objects. 235 */ 236 //public final <T> T newArray(Class<T>[] objects) { 237 public final <T> T selectOneOf(final T[] objects) { 238 return objects[getInteger(0, objects.length - 1)]; 239 } 240 241 /** 242 * Randomly pick an object from an array of objects. 243 * @param <T> generic type of the array elements. 244 * @param objects an array of objects, one of whom is to be picked. 245 * @param probabilities the probabilities of selecting each of the objects. 246 * @return a random element from objects. 247 */ 248 public final <T> T selectOneOf(final T[] objects, final double[] probabilities) { 249 final double r = getDouble(); 250 double cdf = 0.0; 251 for (int i = 0; i < objects.length; i++) { 252 cdf += probabilities[i]; 253 if (r <= cdf) { 254 return objects[i]; 255 } 256 } 257 return objects[objects.length]; 258 } 259 260 /** 261 * @param set a Set in which to look for a random element 262 * @param <T> generic type of the Set elements 263 * @return a random element in the Set or null if the set is empty 264 */ 265 public <T> T selectOneOf(Set<T> set) { 266 int item = getInteger(0, set.size() - 1); 267 int i = 0; 268 for (T obj : set) { 269 if (i == item) { 270 return obj; 271 } 272 i++; 273 } 274 return null; 275 } 276 277 /** 278 * Randomly pick an object from an array of objects. 279 * @param <T> The type of the elements in the collection 280 * @param objects an array of objects, one of whom is to be picked. 281 * @return a random element from objects. 282 */ 283 public final <T> T selectOneOf(final Collection<T> objects) { 284 285 final int n = getInteger(0, objects.size() - 1); 286 if (objects instanceof List) { 287 return ((List<T>) objects).get(n); 288 } else { 289 return com.google.common.collect.Iterators.get(objects.iterator(), n); 290 } 291 } 292 293 /** 294 * Randomly pick N objects from an array of objects. Note, this assumes that N much much less than the size of the 295 * array being sampled from, if this is not the case this method is VERY slow. 296 * @param <T> The type of the elements in the collection 297 * @param objects an array of objects, one of whom is to be picked. 298 * @param n the number of objects we will select. 299 * @return a random element from objects. 300 */ 301 public final <T> List<T> selectManyOf(final T[] objects, final int n) { 302 303 if (n > objects.length) { 304 throw new IllegalArgumentException(String.format("Cannot select %d elements from array of %d objects", n, objects.length)); 305 } 306 307 // this uses simple rejection sampling, a faster method (especially if n is close to objects.size() can be found 308 // at http://lemire.me/blog/archives/2013/08/16/picking-n-distinct-numbers-at-random-how-to-do-it-fast/ 309 final List<T> list = new ArrayList<>(n); 310 final Set<Integer> sampled = new TreeSet<>(); 311 for (int i = 0; i < n; i++) { 312 int index = getInteger(0, objects.length - 1); 313 while (sampled.contains(index)) { 314 index = getInteger(0, objects.length - 1); 315 } 316 sampled.add(index); 317 list.add(objects[index]); 318 } 319 320 return list; 321 } 322 323 /** 324 * Randomly pick N objects from an array of objects. Note, this assumes that N much much less than the size of the 325 * array being sampled from, if this is not the case this method is VERY slow. 326 * @param <T> The type of the elements in the collection 327 * @param objects an array of objects, one of whom is to be picked. 328 * @param n the number of objects we will select. 329 * @return a random element from objects. 330 */ 331 public final <T> List<T> selectManyOf(final Collection<T> objects, final int n) { 332 if (objects.size() < n) { 333 throw new IllegalArgumentException(String.format("Cannot select %d elements from a Collection of %d objects", n, objects.size())); 334 } 335 final Set<Integer> s = new HashSet<>(n); 336 while (s.size() < n) { 337 s.add(getInteger(0, objects.size() - 1)); 338 } 339 340 final List<T> list = new ArrayList<>(n); 341 int i = 0; 342 for (T obj : objects) { 343 if (s.contains(i)) { 344 list.add(obj); 345 } 346 i = i + 1; 347 } 348 349 if (list.size() != n) { 350 log.error("Could not correctly select correct number of objects from a collection of objects."); 351 } 352 353 return list; 354 } 355 356 /** 357 * Randomly pick N objects from an array of objects. Note, this assumes that N much much less than the size of the 358 * array being sampled from, if this is not the case this method is VERY slow. 359 * @param <T> The type of the elements in the collection 360 * @param objects an array of objects, one of whom is to be picked. 361 * @param n the number of objects we will select. 362 * @return a random element from objects. 363 */ 364 public final <T> Set<T> selectManyOf(final Set<T> objects, final int n) { 365 if (objects.size() < n) { 366 throw new IllegalArgumentException(String.format("Cannot select %d elements from a Set of %d objects", n, objects.size())); 367 } 368 369 final Set<Integer> s = new HashSet<>(n); 370 while (s.size() < n) { 371 s.add(getInteger(0, objects.size() - 1)); 372 } 373 374 final Set<T> list = new HashSet<>(n); 375 int i = 0; 376 for (T obj : objects) { 377 if (s.contains(i)) { 378 list.add(obj); 379 } 380 i = i + 1; 381 } 382 383 if (list.size() != n) { 384 log.error("Could not correctly select correct number of objects from a collection of objects."); 385 } 386 387 return list; 388 } 389 390 /** 391 * Random number generator type. 392 */ 393 private final RandomDataGenerator generator; 394 @SuppressWarnings("PMD.UnusedPrivateField") 395 @Getter 396 private String name; 397 398 /** 399 * Random number generator type. 400 */ 401 public static enum Generator { 402 403 /* 404 * This generator features an extremely long period (219937-1) and 623-dimensional equidistribution up to 32 bits accuracy. 405 * The home page for this generator is located at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html. 406 * This generator is described in a paper by Makoto Matsumoto and Takuji Nishimura in 1998: 407 * "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", 408 * ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3--30 409 */ 410 MERSENNE, 411 /* 412 * WELL19937c pseudo-random number generator from François Panneton, Pierre L'Ecuyer and Makoto Matsumoto. 413 * This generator is described in a paper by François Panneton, Pierre L'Ecuyer and Makoto Matsumoto 414 * "Improved Long-Period Generators Based on Linear Recurrences Modulo 2" ACM Transactions on Mathematical Software, 32, 1 (2006) 415 */ 416 Well19937c, 417 /* 418 * WELL44497b pseudo-random number generator from François Panneton, Pierre L'Ecuyer and Makoto Matsumoto. 419 * This generator is described in a paper by François Panneton, Pierre L'Ecuyer and Makoto Matsumoto 420 * "Improved Long-Period Generators Based on Linear Recurrences Modulo 2" ACM Transactions on Mathematical Software, 32, 1 (2006) 421 */ 422 Well44497b 423 }; 424 }