package org.teachingextensions.logo.utils.MazeUtils; /****************************************************************************** * Compilation: javac StdRandom.java * Execution: java StdRandom * Dependencies: StdOut.java * * A library of static methods to generate pseudo-random numbers from * different distributions (bernoulli, uniform, gaussian, discrete, * and exponential). Also includes a method for shuffling an array. * * * % java StdRandom 5 * seed = 1316600602069 * 59 16.81826 true 8.83954 0 * 32 91.32098 true 9.11026 0 * 35 10.11874 true 8.95396 3 * 92 32.88401 true 8.87089 0 * 72 92.55791 true 9.46241 0 * * % java StdRandom 5 * seed = 1316600616575 * 96 60.17070 true 8.72821 0 * 79 32.01607 true 8.58159 0 * 81 59.49065 true 9.10423 1 * 96 51.65818 true 9.02102 0 * 99 17.55771 true 8.99762 0 * * % java StdRandom 5 1316600616575 * seed = 1316600616575 * 96 60.17070 true 8.72821 0 * 79 32.01607 true 8.58159 0 * 81 59.49065 true 9.10423 1 * 96 51.65818 true 9.02102 0 * 99 17.55771 true 8.99762 0 * * * Remark * ------ * - Relies on randomness of nextDouble() method in java.util.Random * to generate pseudorandom numbers in [0, 1). * * - This library allows you to set and get the pseudorandom number seed. * * - See http://www.honeylocust.com/RngPack/ for an industrial * strength random number generator in Java. * ******************************************************************************/ import java.util.Random; /** * The {@code StdRandom} class provides static methods for generating * random number from various discrete and continuous distributions, * including Bernoulli, uniform, Gaussian, exponential, pareto, * Poisson, and Cauchy. It also provides method for shuffling an * array or subarray. * <p> * For additional documentation, * see <a href="http://introcs.cs.princeton.edu/22library">Section 2.2</a> of * <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdRandom { private static Random random; // pseudo-random number generator private static long seed; // pseudo-random number generator seed // static initializer static { // this is how the seed was set in Java 1.4 seed = System.currentTimeMillis(); random = new Random(seed); } // don't instantiate private StdRandom() { } /** * Sets the seed of the pseudorandom number generator. * This method enables you to produce the same sequence of "random" * number for each execution of the program. * Ordinarily, you should call this method at most once per program. * * @param s the seed */ public static void setSeed(long s) { seed = s; random = new Random(seed); } /** * Returns the seed of the pseudorandom number generator. * * @return the seed */ public static long getSeed() { return seed; } /** * Returns a random real number uniformly in [0, 1). * * @return a random real number uniformly in [0, 1) */ public static double uniform() { return random.nextDouble(); } /** * Returns a random integer uniformly in [0, n). * * @param n number of possible integers * @return a random integer uniformly between 0 (inclusive) and <tt>N</tt> (exclusive) * @throws IllegalArgumentException if <tt>n <= 0</tt> */ public static int uniform(int n) { if (n <= 0) throw new IllegalArgumentException("Parameter N must be positive"); return random.nextInt(n); } /////////////////////////////////////////////////////////////////////////// // STATIC METHODS BELOW RELY ON JAVA.UTIL.RANDOM ONLY INDIRECTLY VIA // THE STATIC METHODS ABOVE. /////////////////////////////////////////////////////////////////////////// /** * Returns a random real number uniformly in [0, 1). * * @return a random real number uniformly in [0, 1) * @deprecated Replaced by {@link #uniform()}. */ @Deprecated public static double random() { return uniform(); } /** * Returns a random integer uniformly in [a, b). * * @param a the left endpoint * @param b the right endpoint * @return a random integer uniformly in [a, b) * @throws IllegalArgumentException if <tt>b <= a</tt> * @throws IllegalArgumentException if <tt>b - a >= Integer.MAX_VALUE</tt> */ public static int uniform(int a, int b) { if (b <= a) throw new IllegalArgumentException("Invalid range"); if ((long) b - a >= Integer.MAX_VALUE) throw new IllegalArgumentException("Invalid range"); return a + uniform(b - a); } /** * Returns a random real number uniformly in [a, b). * * @param a the left endpoint * @param b the right endpoint * @return a random real number uniformly in [a, b) * @throws IllegalArgumentException unless <tt>a < b</tt> */ public static double uniform(double a, double b) { if (!(a < b)) throw new IllegalArgumentException("Invalid range"); return a + uniform() * (b - a); } /** * Returns a random boolean from a Bernoulli distribution with success * probability <em>p</em>. * * @param p the probability of returning <tt>true</tt> * @return <tt>true</tt> with probability <tt>p</tt> and * <tt>false</tt> with probability <tt>p</tt> * @throws IllegalArgumentException unless <tt>p >= 0.0</tt> and <tt>p <= 1.0</tt> */ public static boolean bernoulli(double p) { if (!(p >= 0.0 && p <= 1.0)) throw new IllegalArgumentException("Probability must be between 0.0 and 1.0"); return uniform() < p; } /** * Returns a random boolean from a Bernoulli distribution with success * probability 1/2. * * @return <tt>true</tt> with probability 1/2 and * <tt>false</tt> with probability 1/2 */ public static boolean bernoulli() { return bernoulli(0.5); } /** * Returns a random real number from a standard Gaussian distribution. * * @return a random real number from a standard Gaussian distribution * (mean 0 and standard deviation 1). */ public static double gaussian() { // use the polar form of the Box-Muller transform double r, x, y; do { x = uniform(-1.0, 1.0); y = uniform(-1.0, 1.0); r = x * x + y * y; } while (r >= 1 || r == 0); return x * Math.sqrt(-2 * Math.log(r) / r); // Remark: y * Math.sqrt(-2 * Math.log(r) / r) // is an independent random gaussian } /** * Returns a random real number from a Gaussian distribution with mean μ * and standard deviation σ. * * @param mu the mean * @param sigma the standard deviation * @return a real number distributed according to the Gaussian distribution * with mean <tt>mu</tt> and standard deviation <tt>sigma</tt> */ public static double gaussian(double mu, double sigma) { return mu + sigma * gaussian(); } /** * Returns a random integer from a geometric distribution with success * probability <em>p</em>. * * @param p the parameter of the geometric distribution * @return a random integer from a geometric distribution with success * probability <tt>p</tt> * @throws IllegalArgumentException unless <tt>p >= 0.0</tt> and <tt>p <= 1.0</tt> */ public static int geometric(double p) { if (!(p >= 0.0 && p <= 1.0)) throw new IllegalArgumentException("Probability must be between 0.0 and 1.0"); // using algorithm given by Knuth return (int) Math.ceil(Math.log(uniform()) / Math.log(1.0 - p)); } /** * Returns a random integer from a Poisson distribution with mean λ. * * @param lambda the mean of the Poisson distribution * @return a random integer from a Poisson distribution with mean <tt>lambda</tt> * @throws IllegalArgumentException unless <tt>lambda > 0.0</tt> and not infinite */ public static int poisson(double lambda) { if (!(lambda > 0.0)) throw new IllegalArgumentException("Parameter lambda must be positive"); if (Double.isInfinite(lambda)) throw new IllegalArgumentException("Parameter lambda must not be infinite"); // using algorithm given by Knuth // see http://en.wikipedia.org/wiki/Poisson_distribution int k = 0; double p = 1.0; double L = Math.exp(-lambda); do { k++; p *= uniform(); } while (p >= L); return k - 1; } /** * Returns a random real number from the standard Pareto distribution. * * @return a random real number from the standard Pareto distribution */ public static double pareto() { return pareto(1.0); } /** * Returns a random real number from a Pareto distribution with * shape parameter α. * * @param alpha shape parameter * @return a random real number from a Pareto distribution with shape * parameter <tt>alpha</tt> * @throws IllegalArgumentException unless <tt>alpha > 0.0</tt> */ public static double pareto(double alpha) { if (!(alpha > 0.0)) throw new IllegalArgumentException("Shape parameter alpha must be positive"); return Math.pow(1 - uniform(), -1.0 / alpha) - 1.0; } /** * Returns a random real number from the Cauchy distribution. * * @return a random real number from the Cauchy distribution. */ public static double cauchy() { return Math.tan(Math.PI * (uniform() - 0.5)); } /** * Returns a random integer from the specified discrete distribution. * * @param a the probability of occurrence of each integer * @return a random integer from a discrete distribution: * <tt>i</tt> with probability <tt>a[i]</tt> * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> * @throws IllegalArgumentException if sum of array entries is not (very nearly) equal to <tt>1.0</tt> * @throws IllegalArgumentException unless <tt>a[i] >= 0.0</tt> for each index <tt>i</tt> */ public static int discrete(double[] a) { if (a == null) throw new NullPointerException("argument array is null"); double EPSILON = 1E-14; double sum = 0.0; for (int i = 0; i < a.length; i++) { if (!(a[i] >= 0.0)) throw new IllegalArgumentException("array entry " + i + " must be nonnegative: " + a[i]); sum = sum + a[i]; } if (sum > 1.0 + EPSILON || sum < 1.0 - EPSILON) throw new IllegalArgumentException("sum of array entries does not approximately equal 1.0: " + sum); // the for loop may not return a value when both r is (nearly) 1.0 and when the // cumulative sum is less than 1.0 (as a result of floating-point roundoff error) while (true) { double r = uniform(); sum = 0.0; for (int i = 0; i < a.length; i++) { sum = sum + a[i]; if (sum > r) return i; } } } /** * Returns a random real number from an exponential distribution * with rate λ. * * @param lambda the rate of the exponential distribution * @return a random real number from an exponential distribution with * rate <tt>lambda</tt> * @throws IllegalArgumentException unless <tt>lambda > 0.0</tt> */ public static double exp(double lambda) { if (!(lambda > 0.0)) throw new IllegalArgumentException("Rate lambda must be positive"); return -Math.log(1 - uniform()) / lambda; } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> */ public static void shuffle(Object[] a) { if (a == null) throw new NullPointerException("argument array is null"); int N = a.length; for (int i = 0; i < N; i++) { int r = i + uniform(N - i); // between i and N-1 Object temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> */ public static void shuffle(double[] a) { if (a == null) throw new NullPointerException("argument array is null"); int N = a.length; for (int i = 0; i < N; i++) { int r = i + uniform(N - i); // between i and N-1 double temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> */ public static void shuffle(int[] a) { if (a == null) throw new NullPointerException("argument array is null"); int N = a.length; for (int i = 0; i < N; i++) { int r = i + uniform(N - i); // between i and N-1 int temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> * @throws IndexOutOfBoundsException unless <tt>(0 <= lo) && (lo <= hi) && (hi < a.length)</tt> * */ public static void shuffle(Object[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi - i + 1); // between i and hi Object temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> * @throws IndexOutOfBoundsException unless <tt>(0 <= lo) && (lo <= hi) && (hi < a.length)</tt> */ public static void shuffle(double[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi - i + 1); // between i and hi double temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if <tt>a</tt> is <tt>null</tt> * @throws IndexOutOfBoundsException unless <tt>(0 <= lo) && (lo <= hi) && (hi < a.length)</tt> */ public static void shuffle(int[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi - i + 1); // between i and hi int temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Unit test. */ public static void main(String[] args) { int N = Integer.parseInt(args[0]); if (args.length == 2) StdRandom.setSeed(Long.parseLong(args[1])); double[] t = {.5, .3, .1, .1}; StdOut.println("seed = " + StdRandom.getSeed()); for (int i = 0; i < N; i++) { StdOut.printf("%2d ", uniform(100)); StdOut.printf("%8.5f ", uniform(10.0, 99.0)); StdOut.printf("%5b ", bernoulli(.5)); StdOut.printf("%7.5f ", gaussian(9.0, .2)); StdOut.printf("%2d ", discrete(t)); StdOut.println(); } String[] a = "A B C D E F G".split(" "); for (String s : a) StdOut.print(s + " "); StdOut.println(); } }