package squidpony.squidmath; import squidpony.ArrayTools; import squidpony.annotation.GwtIncompatible; import java.io.Serializable; import java.util.*; /** * A wrapper class for working with random number generators in a more friendly way. * <p> * Includes methods for getting values between two numbers and for getting * random elements from a collection or array. There are methods to shuffle * a collection and to get a random ordering that can be applied as one shuffle * across multiple collections, such as via {@link OrderedMap#reorder(int...)}, * {@link ArrayTools#reorder(ArrayList, int...)}, and so on. You can construct * an RNG with all sorts of RandomnessSource implementations, and choosing them * is usually not a big concern because the default works very well. * <br> * But if you do want advice on what RandomnessSource to use... LightRNG is the * default, and is very fast, but relative to many of the others it has a * significantly shorter period (the amount of random numbers it will go through * before repeating the sequence), at {@code pow(2, 64)} as opposed to XorRNG and * XoRoRNG's {@code pow(2, 128)}. LightRNG also allows the current RNG state * to be retrieved and altered with {@code getState()} and {@code setState()}. For * most cases, you should decide between LightRNG, XoRoRNG, and other * RandomnessSource implementations based on your needs for period length and state * manipulation (LightRNG is also used internally by almost all {@link StatefulRNG} * objects). You might want significantly less predictable random results, which * {@link IsaacRNG} and {@link Isaac32RNG} can provide, along with a large period. * You may want a very long period of random numbers, which would suggest * {@link LongPeriodRNG} as the best choice. You may want better performance on * 32-bit machines or especially on GWT (which has to emulate Java's behavior with * 64-bit longs), which would mean {@link PintRNG} (for generating only ints via * {@link PintRNG#next(int)}, since its {@link PintRNG#nextLong()} method is very * slow) or {@link FlapRNG} (for generating ints and longs at relatively good speed * using mainly int math; also capable of the state changing that LightRNG can do). * {@link ThunderRNG} is the fastest generator we have, and has a decent period when * considering all bits, but if you only consider the less-significant bits then it * has a very poor period. This bad behavior is similar to how linear congruential * generators act, such as {@link java.util.Random}, which simply truncates off the * lower bits. * * @author Eben Howard - http://squidpony.com - howard@squidpony.com * @author Tommy Ettinger * @author smelC */ public class RNG implements Serializable { /** * A very small multiplier used to reduce random numbers to from the {@code [0.0,9007199254740991.0)} range to the * {@code [0.0,1.0)} range. Equivalent to {@code 1.0 / (1 << 53)}, if that number makes more sense to you, but the * source uses the hexadecimal double literal {@code 0x1p-53}. The hex literals are a nice "hidden feature" of Java * 5 onward, and allow exact declaration of floating-point numbers without precision loss from decimal conversion. */ protected static final double DOUBLE_UNIT = 0x1p-53; // more people should know about hex double literals! /** * A very small multiplier used to reduce random numbers to from the {@code [0.0,16777216.0)} range to the * {@code [0.0,1.0)} range. Equivalent to {@code 1.0f / (1 << 24)}, if that number makes more sense to you, but the * source uses the hexadecimal double literal {@code 0x1p-24f}. The hex literals are a nice "hidden feature" of Java * 5 onward, and allow exact declaration of floating-point numbers without precision loss from decimal conversion. */ protected static final float FLOAT_UNIT = 0x1p-24f; protected RandomnessSource random; protected double nextNextGaussian; protected boolean haveNextNextGaussian = false; protected Random ran = null; private static final long serialVersionUID = 2352426757973945149L; /** * Default constructor; uses SplitMix64, which is of high quality, but low period (which rarely matters for games), * and has good speed, tiny state size, and excellent 64-bit number generation. * <br> * Compatibility note: previous versions of SquidLib used Mersenne Twister by default. Due to the incompatibility * of the threads used by this Mersenne Twister implementation with GWT and HTML5 applications, the randomness * algorithm has been changed to a faster, more compatible algorithm, though it does suffer from a much lower * period. If you need drastically larger periods than 2^64, you can pass a LongPeriodRNG (or MersenneTwister on * targets other than HTML) object to the constructor that takes a RandomnessSource. If you don't know what the * period of a PRNG is, you probably don't need to worry about it; it's mainly relevant to heavily multi-threaded * applications anyway. The addition of LongPeriodRNG on March 21, 2016 should help to take the part of a fast, * large-period RNG, which MersenneTwister is unable to act as on GWT. The default may change again some time after * May 1, 2016, now that we have XoRoRNG, which is approximately as fast as LightRNG and has a substantially better * period (pow(2, 128) - 1). */ public RNG() { this(new LightRNG()); } /** * Seeded constructor; uses LightRNG, which is of high quality, but low period (which rarely matters for games), * and has good speed, tiny state size, and excellent 64-bit number generation. */ public RNG(long seed) { this(new LightRNG(seed)); } /** * String-seeded constructor; uses a platform-independent hash of the String (it does not use String.hashCode) as a * seed for LightRNG, which is of high quality, but low period (which rarely matters for games), and has good speed, * tiny state size, and excellent 64-bit number generation. */ public RNG(String seedString) { this(new LightRNG(CrossHash.hash(seedString))); } /** * Uses the provided source of randomness for all calculations. This * constructor should be used if an alternate RandomnessSource other than LightRNG is desirable. * * @param random the source of pseudo-randomness, such as a MersenneTwister or SobolQRNG object */ public RNG(RandomnessSource random) { this.random = random; } /** * A subclass of java.util.Random that uses a RandomnessSource supplied by the user instead of the default. * * @author Tommy Ettinger */ public static class CustomRandom extends Random { private static final long serialVersionUID = 8211985716129281944L; private final RandomnessSource randomnessSource; /** * Creates a new random number generator. This constructor uses * a LightRNG with a random seed. */ public CustomRandom() { randomnessSource = new LightRNG(); } /** * Creates a new random number generator. This constructor uses * the seed of the given RandomnessSource if it has been seeded. * * @param randomnessSource a way to get random bits, supplied by RNG */ public CustomRandom(RandomnessSource randomnessSource) { this.randomnessSource = randomnessSource; } /** * Generates the next pseudorandom number. Subclasses should * override this, as this is used by all other methods. * <p> * <p>The general contract of {@code next} is that it returns an * {@code int} value and if the argument {@code bits} is between * {@code 1} and {@code 32} (inclusive), then that many low-order * bits of the returned value will be (approximately) independently * chosen bit values, each of which is (approximately) equally * likely to be {@code 0} or {@code 1}. The method {@code next} is * implemented by class {@code Random} by atomically updating the seed to * <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre> * and returning * <pre>{@code (int)(seed >>> (48 - bits))}.</pre> * * This is a linear congruential pseudorandom number generator, as * defined by D. H. Lehmer and described by Donald E. Knuth in * <i>The Art of Computer Programming,</i> Volume 3: * <i>Seminumerical Algorithms</i>, section 3.2.1. * * @param bits random bits * @return the next pseudorandom value from this random number * generator's sequence * @since 1.1 */ @Override protected int next(int bits) { return randomnessSource.next(bits); } } /** * @return a Random instance that can be used for legacy compatibility */ public Random asRandom() { if (ran == null) { ran = new CustomRandom(random); } return ran; } /** * Returns a value from an even distribution from min (inclusive) to max * (exclusive). * * @param min the minimum bound on the return value (inclusive) * @param max the maximum bound on the return value (exclusive) * @return the found value */ public double between(double min, double max) { return min + (max - min) * nextDouble(); } /** * Returns a value between min (inclusive) and max (exclusive). * <p> * The inclusive and exclusive behavior is to match the behavior of the * similar method that deals with floating point values. * * @param min the minimum bound on the return value (inclusive) * @param max the maximum bound on the return value (exclusive) * @return the found value */ public int between(int min, int max) { return nextInt(max - min) + min; } /** * Returns a value between min (inclusive) and max (exclusive). * <p> * The inclusive and exclusive behavior is to match the behavior of the * similar method that deals with floating point values. * * @param min the minimum bound on the return value (inclusive) * @param max the maximum bound on the return value (exclusive) * @return the found value */ public long between(long min, long max) { return nextLong(max - min) + min; } /** * Returns the average of a number of randomly selected numbers from the * provided range, with min being inclusive and max being exclusive. It will * sample the number of times passed in as the third parameter. * <p> * The inclusive and exclusive behavior is to match the behavior of the * similar method that deals with floating point values. * <p> * This can be used to weight RNG calls to the average between min and max. * * @param min the minimum bound on the return value (inclusive) * @param max the maximum bound on the return value (exclusive) * @param samples the number of samples to take * @return the found value */ public int betweenWeighted(int min, int max, int samples) { int sum = 0; for (int i = 0; i < samples; i++) { sum += between(min, max); } return Math.round((float) sum / samples); } /** * Returns a random element from the provided array and maintains object * type. * * @param <T> the type of the returned object * @param array the array to get an element from * @return the randomly selected element */ public <T> T getRandomElement(T[] array) { if (array.length < 1) { return null; } return array[nextInt(array.length)]; } /** * Returns a random element from the provided list. If the list is empty * then null is returned. * * @param <T> the type of the returned object * @param list the list to get an element from * @return the randomly selected element */ public <T> T getRandomElement(List<T> list) { if (list.size() <= 0) { return null; } return list.get(nextInt(list.size())); } /** * Returns a random element from the provided ShortSet. If the set is empty * then an exception is thrown. * <p> * <p> * Requires iterating through a random amount of the elements in set, so performance depends on the size of set but * is likely to be decent. This is mostly meant for internal use, the same as ShortSet. * </p> * * @param set the ShortSet to get an element from * @return the randomly selected element */ public short getRandomElement(ShortSet set) { if (set.size <= 0) { throw new UnsupportedOperationException("ShortSet cannot be empty when getting a random element"); } int n = nextInt(set.size); short s = 0; ShortSet.ShortSetIterator ssi = set.iterator(); while (n-- >= 0 && ssi.hasNext) s = ssi.next(); ssi.reset(); return s; } /** * Returns a random element from the provided Collection, which should have predictable iteration order if you want * predictable behavior for identical RNG seeds, though it will get a random element just fine for any Collection * (just not predictably in all cases). If you give this a Set, it should be a LinkedHashSet or some form of sorted * Set like TreeSet if you want predictable results. Any List or Queue should be fine. Map does not implement * Collection, thank you very much Java library designers, so you can't actually pass a Map to this, though you can * pass the keys or values. If coll is empty, returns null. * <p> * <p> * Requires iterating through a random amount of coll's elements, so performance depends on the size of coll but is * likely to be decent, as long as iteration isn't unusually slow. This replaces {@code getRandomElement(Queue)}, * since Queue implements Collection and the older Queue-using implementation was probably less efficient. * </p> * * @param <T> the type of the returned object * @param coll the Collection to get an element from; remember, Map does not implement Collection * @return the randomly selected element */ public <T> T getRandomElement(Collection<T> coll) { if (coll.size() <= 0) { return null; } int n = nextInt(coll.size()); T t = null; Iterator<T> it = coll.iterator(); while (n-- >= 0 && it.hasNext()) t = it.next(); return t; } /* * Returns a random elements from the provided queue. If the queue is empty * then null is returned. * * <p> * Requires iterating through a random amount of the elements in set, so * performance depends on the size of set but is likely to be decent. This * is mostly meant for internal use, the same as ShortSet. * </p> * * @param <T> the type of the returned object * @param list the list to get an element from * @return the randomly selected element */ /* public <T> T getRandomElement(Queue<T> list) { if (list.isEmpty()) { return null; } return new ArrayList<>(list).get(nextInt(list.size())); }*/ /** * Given a {@link List} l, this selects a random element of l to be the first value in the returned list l2. It * retains the order of elements in l after that random element and makes them follow the first element in l2, and * loops around to use elements from the start of l after it has placed the last element of l into l2. * <br> * Essentially, it does what it says on the tin. It randomly rotates the List l. * <br> * If you only need to iterate through a collection starting at a random point, the method getRandomStartIterable() * should have better performance. * * @param l A {@link List} that will not be modified by this method. All elements of this parameter will be * shared with the returned List. * @param <T> No restrictions on type. Changes to elements of the returned List will be reflected in the parameter. * @return A shallow copy of {@code l} that has been rotated so its first element has been randomly chosen * from all possible elements but order is retained. Will "loop around" to contain element 0 of l after the last * element of l, then element 1, etc. */ @GwtIncompatible /* Because of Collections.rotate */ public <T> List<T> randomRotation(final List<T> l) { final int sz = l.size(); if (sz == 0) return Collections.<T>emptyList(); /* * Collections.rotate should prefer the best-performing way to rotate l, * which would be an in-place modification for ArrayLists and an append * to a sublist for Lists that don't support efficient random access. */ List<T> l2 = new ArrayList<>(l); Collections.rotate(l2, nextInt(sz)); return l2; } /** * Get an Iterable that starts at a random location in list and continues on through list in its current order. * Loops around to the beginning after it gets to the end, stops when it returns to the starting location. * <br> * You should not modify {@code list} while you use the returned reference. And there'll be no * ConcurrentModificationException to detect such erroneous uses. * * @param list A list <b>with a constant-time {@link List#get(int)} method</b> (otherwise performance degrades). * @return An {@link Iterable} that iterates over {@code list} but start at * a random index. If the chosen index is {@code i}, the iterator * will return: * {@code list[i]; list[i+1]; ...; list[list.length() - 1]; list[0]; list[i-1]} */ public <T> Iterable<T> getRandomStartIterable(final List<T> list) { final int sz = list.size(); if (sz == 0) return Collections.<T>emptyList(); /* * Here's a tricky bit: Defining 'start' here means that every Iterator * returned by the returned Iterable will have the same iteration order. * In other words, if you use more than once the returned Iterable, * you'll will see elements in the same order every time, which is * desirable. */ final int start = nextInt(sz); return new Iterable<T>() { @Override public Iterator<T> iterator() { return new Iterator<T>() { int next = -1; @Override public boolean hasNext() { return next != start; } @Override public T next() { if (next == start) throw new NoSuchElementException("Iteration terminated; check hasNext() before next()"); if (next == -1) /* First call */ next = start; final T result = list.get(next); if (next == sz - 1) /* * Reached the list's end, let's continue from the list's * left. */ next = 0; else next++; return result; } @Override public void remove() { throw new UnsupportedOperationException("Remove is not supported from a randomStartIterable"); } @Override public String toString() { return "RandomStartIterator at index " + next; } }; } }; } /** * Use that to get random cells in a rectangular map. * * @param width The map's width (bounds the x-coordinate in returned coords). * @param height The map's height (bounds the y-coordinate in returned coords). * @param size The number of elements in the returned iterable or anything * negative for no bound (in which case the iterator is infinite, it's * up to you to bound your iteration). * @return An iterable that returns random cells in the rectangle (0,0) * (inclusive) .. (width, height) (exclusive). */ public Iterable<Coord> getRandomCellsIterable(final int width, final int height, final int size) { return new Iterable<Coord>() { @Override public Iterator<Coord> iterator() { return new Iterator<Coord>() { /** * The number of elements returned so far */ int returned = 0; @Override public boolean hasNext() { return size < 0 || returned < size; } @Override public Coord next() { if (!hasNext()) throw new NoSuchElementException(); returned++; return nextCoord(width, height); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } /** * Gets an array of unique Coords, from (startX,startY) inclusive to (startX+width,startY+height) exclusive, in a * random order, with the array containing {@code width * height} items. * * @param startX the inclusive starting x position * @param startY the inclusive starting y position * @param width the width of the space to place Coords in * @param height the height of the space to place Coords in * @return an array containing {@code width * height} Coord items in random order, inside the given bounds */ public Coord[] getRandomUniqueCells(final int startX, final int startY, final int width, final int height) { if (width <= 0 || height <= 0) return new Coord[0]; return getRandomUniqueCells(startX, startY, width, height, new Coord[width * height]); } /** * Gets an array of unique Coords, from (startX,startY) inclusive to (startX+width,startY+height) exclusive, in a * random order, with the array containing {@code Math.min(width * height, size)} items. If size is less than width * times height, then not all Coords in the space will be used. * * @param startX the inclusive starting x position * @param startY the inclusive starting y position * @param width the width of the space to place Coords in * @param height the height of the space to place Coords in * @param size the size of the array to return; only matters if it is smaller than {@code width * height} * @return an array containing {@code Math.min(width * height, size)} Coord items in random order, inside the given bounds */ public Coord[] getRandomUniqueCells(final int startX, final int startY, final int width, final int height, final int size) { if (width <= 0 || height <= 0 || size <= 0) return new Coord[0]; return getRandomUniqueCells(startX, startY, width, height, new Coord[Math.min(width * height, size)]); } /** * Assigns to dest an array of unique Coords, from (startX,startY) inclusive to (startX+width,startY+height) * exclusive, in a random order, with dest after this is called containing the lesser of {@code width * height} or * {@code dest.length} items. This will not allocate a new array for dest, but will create a temporary int array for * handling the shuffle. * * @param startX the inclusive starting x position * @param startY the inclusive starting y position * @param width the width of the space to place Coords in * @param height the height of the space to place Coords in * @param dest a Coord array that will be modified to contain randomly-ordered Coords, but will not be resized * @return dest, now with up to its first {@code width * height} items assigned to random Coords inside the given bounds */ public Coord[] getRandomUniqueCells(final int startX, final int startY, final int width, final int height, final Coord[] dest) { if (width <= 0 || height <= 0 || dest == null || dest.length <= 0) return dest; int[] o = randomOrdering(width * height); for (int i = 0; i < o.length && i < dest.length; i++) { dest[i] = Coord.get(startX + o[i] / width, startY + o[i] % width); } return dest; } /** * Shuffle an array using the Fisher-Yates algorithm and returns a shuffled copy. * Not GWT-compatible; use the overload that takes two arrays if you use GWT. * <br> * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle * * @param elements an array of T; will not be modified * @param <T> can be any non-primitive type. * @return a shuffled copy of elements */ @GwtIncompatible public <T> T[] shuffle(T[] elements) { int n = elements.length; T[] array = Arrays.copyOf(elements, n); for (int i = 0; i < n; i++) { int r = i + nextInt(n - i); T t = array[r]; array[r] = array[i]; array[i] = t; } return array; } /** * Shuffles an array in place using the Fisher-Yates algorithm. * If you don't want the array modified, use {@link #shuffle(Object[], Object[])}. * Unlike {@link #shuffle(Object[])}, this is GWT-compatible. * <br> * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle * * @param elements an array of T; <b>will</b> be modified * @param <T> can be any non-primitive type. * @return elements after shuffling it in-place */ public <T> T[] shuffleInPlace(T[] elements) { for (int i = elements.length - 1; i > 0; i--) { int r = nextInt(i + 1); T t = elements[r]; elements[r] = elements[i]; elements[i] = t; } return elements; } /** * Shuffle an array using the "inside-out" Fisher-Yates algorithm. DO NOT give the same array for both elements and * dest, since the prior contents of dest are rearranged before elements is used, and if they refer to the same * array, then you can end up with bizarre bugs where one previously-unique item shows up dozens of times. If * possible, create a new array with the same length as elements and pass it in as dest; the returned value can be * assigned to whatever you want and will have the same items as the newly-formed array. * <br> * https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm * * @param elements an array of T; will not be modified * @param <T> can be any non-primitive type. * @param dest Where to put the shuffle. If it does not have the same length as {@code elements}, this will use the * randomPortion method of this class to fill the smaller dest. MUST NOT be the same array as elements! * @return {@code dest} after modifications */ /* This method has this prototype to be compatible with GWT. */ public <T> T[] shuffle(T[] elements, T[] dest) { if (dest.length != elements.length) return randomPortion(elements, dest); for (int i = 0; i < elements.length; i++) { int r = nextInt(i + 1); if (r != i) dest[i] = dest[r]; dest[r] = elements[i]; } return dest; } /** * Shuffles a {@link Collection} of T using the Fisher-Yates algorithm and returns an ArrayList of T. * * @param elements a Collection of T; will not be modified * @param <T> can be any non-primitive type. * @return a shuffled ArrayList containing the whole of elements in pseudo-random order. */ public <T> ArrayList<T> shuffle(Collection<T> elements) { return shuffle(elements, null); } /** * Shuffles a {@link Collection} of T using the Fisher-Yates algorithm. The result * is allocated if {@code buf} is null or if {@code buf} isn't empty, * otherwise {@code elements} is poured into {@code buf}. * * @param elements a Collection of T; will not be modified * @param <T> can be any non-primitive type. * @return a shuffled ArrayList containing the whole of elements in pseudo-random order. */ public <T> ArrayList<T> shuffle(Collection<T> elements, /*@Nullable*/ ArrayList<T> buf) { final ArrayList<T> al; if (buf == null || !buf.isEmpty()) al = new ArrayList<>(elements); else { al = buf; al.addAll(elements); } int n = al.size(); for (int i = 0; i < n; i++) { Collections.swap(al, i + nextInt(n - i), i); } return al; } /** * Generates a random permutation of the range from 0 (inclusive) to length (exclusive). * Useful for passing to OrderedMap or OrderedSet's reorder() methods. * * @param length the size of the ordering to produce * @return a random ordering containing all ints from 0 to length (exclusive) */ public int[] randomOrdering(int length) { if (length <= 0) return new int[0]; int[] dest = new int[length]; for (int i = 0; i < length; i++) { int r = nextInt(i + 1); if (r != i) dest[i] = dest[r]; dest[r] = i; } return dest; } /** * Generates a random permutation of the range from 0 (inclusive) to length (exclusive) and stores it in * the dest parameter, avoiding allocations. * Useful for passing to OrderedMap or OrderedSet's reorder() methods. * * @param length the size of the ordering to produce * @param dest the destination array; will be modified * @return dest, filled with a random ordering containing all ints from 0 to length (exclusive) */ public int[] randomOrdering(int length, int[] dest) { if (dest == null) return null; for (int i = 0; i < length && i < dest.length; i++) { int r = nextIntHasty(i + 1); if (r != i) dest[i] = dest[r]; dest[r] = i; } return dest; } /** * Gets a random portion of data (an array), assigns that portion to output (an array) so that it fills as much as * it can, and then returns output. Will only use a given position in the given data at most once; does this by * generating random indices for data's elements, but only as much as needed, assigning the copied section to output * and not modifying data. * <br> * Based on http://stackoverflow.com/a/21460179 , credit to Vincent van der Weele; modifications were made to avoid * copying or creating a new generic array (a problem on GWT). * * @param data an array of T; will not be modified. * @param output an array of T that will be overwritten; should always be instantiated with the portion length * @param <T> can be any non-primitive type. * @return an array of T that has length equal to output's length and may contain unchanged elements (null if output * was empty) if data is shorter than output */ public <T> T[] randomPortion(T[] data, T[] output) { /* int length = data.length; int[] mapping = new int[length]; for (int i = 0; i < length; i++) { mapping[i] = i; } for (int i = 0; i < output.length && length > 0; i++) { int r = nextInt(length); output[i] = data[mapping[r]]; mapping[r] = length-1; } */ int length = data.length; int n = Math.min(length, output.length); int[] mapping = ArrayTools.range(n); for (int i = 0; i < n; i++) { int r = nextInt(length); output[i] = data[mapping[r]]; mapping[r] = mapping[--length]; } return output; } /** * Gets a random portion of a List and returns it as a new List. Will only use a given position in the given * List at most once; does this by shuffling a copy of the List and getting a section of it. * * @param data a List of T; will not be modified. * @param count the non-negative number of elements to randomly take from data * @param <T> can be any non-primitive type * @return a List of T that has length equal to the smaller of count or data.length */ public <T> List<T> randomPortion(List<T> data, int count) { return shuffle(data).subList(0, Math.min(count, data.size())); } /** * Gets a random subrange of the non-negative ints from start (inclusive) to end (exclusive), using count elements. * May return an empty array if the parameters are invalid (end is less than/equal to start, or start is negative). * * @param start the start of the range of numbers to potentially use (inclusive) * @param end the end of the range of numbers to potentially use (exclusive) * @param count the total number of elements to use; will be less if the range is smaller than count * @return an int array that contains at most one of each number in the range */ public int[] randomRange(int start, int end, int count) { if (end <= start || start < 0) return new int[0]; int n = end - start; int[] data = new int[n]; for (int e = start, i = 0; e < end; e++) { data[i++] = e; } for (int i = 0; i < n; i++) { int r = i + nextInt(n - i), t = data[r]; data[r] = data[i]; data[i] = t; } int[] array = new int[Math.min(count, n)]; System.arraycopy(data, 0, array, 0, Math.min(count, n)); return array; } /** * @return a value from the gaussian distribution */ public synchronized double nextGaussian() { if (haveNextNextGaussian) { haveNextNextGaussian = false; return nextNextGaussian; } else { double v1, v2, s; do { v1 = 2 * nextDouble() - 1; // between -1 and 1 v2 = 2 * nextDouble() - 1; // between -1 and 1 s = v1 * v1 + v2 * v2; } while (s >= 1 || s == 0); double multiplier = Math.sqrt(-2 * Math.log(s) / s); nextNextGaussian = v2 * multiplier; haveNextNextGaussian = true; return v1 * multiplier; } } /** * This returns a maximum of 0.9999999999999999 because that is the largest * Double value that is less than 1.0 * * @return a value between 0 (inclusive) and 0.9999999999999999 (inclusive) */ public double nextDouble() { return (random.nextLong() & 0x1fffffffffffffL) * DOUBLE_UNIT; // consider changing to this in a future version; it will break compatibility but should be fast/correct //return Double.longBitsToDouble(0x3FF0000000000000L | random.nextLong() >>> 12) - 1.0; } /** * This returns a random double between 0.0 (inclusive) and max (exclusive). * * @return a value between 0 (inclusive) and max (exclusive) */ public double nextDouble(double max) { return nextDouble() * max; } /** * This returns a maximum of 0.99999994 because that is the largest Float * value that is less than 1.0f * * @return a value between 0 (inclusive) and 0.99999994 (inclusive) */ public float nextFloat() { return next(24) * FLOAT_UNIT; } /** * Get a random bit of state, interpreted as true or false with approximately equal likelihood. * * @return a random boolean. */ public boolean nextBoolean() { return nextLong() < 0L; } /** * Get a random long between Long.MIN_VALUE to Long.MAX_VALUE (both inclusive). * * @return a 64-bit random long. */ public long nextLong() { return random.nextLong(); } /** * Returns a random long below the given bound, or 0 if the bound is 0 or * negative. * * @param bound the upper bound (exclusive) * @return the found number */ public long nextLong(final long bound) { if (bound <= 0) return 0; long threshold = (0x7fffffffffffffffL - bound + 1) % bound; for (; ; ) { long bits = random.nextLong() & 0x7fffffffffffffffL; if (bits >= threshold) return bits % bound; } } /** * Returns a random non-negative integer below the given bound, or 0 if the bound is 0 or * negative. * * @param bound the upper bound (exclusive) * @return the found number */ public int nextInt(final int bound) { if (bound <= 0) return 0; int threshold = (0x7fffffff - bound + 1) % bound; for (; ; ) { int bits = random.next(31); if (bits >= threshold) return bits % bound; } } /** * Returns a random non-negative integer below the given bound, or 0 if the bound is 0. * Uses an aggressively optimized technique that has some bias, but mostly for values of * bound over 1 billion. This method is considered "hasty" since it should be faster than * nextInt() but gives up some statistical quality to do so. It also has undefined behavior * if bound is negative, though it will probably produce a negative number (just how * negative is an open question). * <br> * Credit goes to Daniel Lemire, http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ * * @param bound the upper bound (exclusive); behavior is undefined if bound is negative * @return the found number */ public int nextIntHasty(final int bound) { return (int) ((bound * (random.nextLong() & 0x7FFFFFFFL)) >> 31); } /** * Generates random bytes and places them into the given byte array, modifying it in-place. * The number of random bytes produced is equal to the length of the byte array. Unlike the * method in java.util.Random, this generates 8 bytes at a time, which can be more efficient * with many RandomnessSource types than the JDK's method that generates 4 bytes at a time. * <br> * Adapted from code in the JavaDocs of {@link Random#nextBytes(byte[])}. * <br> * @param bytes the byte array to fill with random bytes; cannot be null, will be modified * @throws NullPointerException if the byte array is null */ public void nextBytes(final byte[] bytes) { for (int i = 0; i < bytes.length; ) for (long r = random.nextLong(), n = Math.min(bytes.length - i, 8); n-- > 0; r >>>= 8) bytes[i++] = (byte) r; } /** * Gets a random Coord that has x between 0 (inclusive) and width (exclusive) and y between 0 (inclusive) * and height (exclusive). This makes one call to randomLong to generate (more than) 31 random bits for * each axis, and should be very fast. Remember that Coord values are cached in a pool that starts able to * hold up to 255 x and 255 y for positive values, and the pool should be grown with the static method * Coord.expandPool() in order to efficiently use larger Coord values. If width and height are very large, * greater than 100,000 for either, this particular method may show bias toward certain positions due to * the "hasty" technique used to reduce the random numbers to the given size, but because most maps in * tile-based games are relatively small, this technique should be fine. * <br> * Credit goes to Daniel Lemire, http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ * * @param width the upper bound (exclusive) for x coordinates * @param height the upper bound (exclusive) for y coordinates * @return a random Coord between (0,0) inclusive and (width,height) exclusive */ public Coord nextCoord(int width, int height) { final long n = random.nextLong(); return Coord.get((int) ((width * (n >>> 33)) >> 31), (int) ((height * (n & 0x7FFFFFFFL)) >> 31)); } /** * Get a random integer between Integer.MIN_VALUE to Integer.MAX_VALUE (both inclusive). * * @return a 32-bit random int. */ public int nextInt() { return next(32); } /** * Get up to 32 bits (inclusive) of random state from the RandomnessSource. * * @param bits 1 to 32 * @return a random number that fits in the specified number of bits. */ public int next(int bits) { return random.next(bits); } public RandomnessSource getRandomness() { return random; } public void setRandomness(RandomnessSource random) { this.random = random; } /** * Creates a copy of this RNG; it will generate the same random numbers, given the same calls in order, as this RNG * at the point copy() is called. The copy will not share references with this RNG. * * @return a copy of this RNG */ public RNG copy() { return new RNG(random.copy()); } /** * Generates a random 64-bit long with a number of '1' bits (Hamming weight) approximately equal to bitCount. * For example, calling this with a parameter of 32 will be equivalent to calling nextLong() on this object's * RandomnessSource (it doesn't consider overridden nextLong() methods, where present, on subclasses of RNG). * Calling this with a parameter of 16 will have on average 16 of the 64 bits in the returned long set to '1', * distributed pseudo-randomly, while a parameter of 47 will have on average 47 bits set. This can be useful for * certain code that uses bits to represent data but needs a different ratio of set bits to unset bits than 1:1. * <br> * Implementors should limit any overriding method to calling and returning super(), potentially storing any extra * information they need to internally, but should not change the result. This works based on a delicate balance of * the RandomnessSource producing bits with an even 50% chance of being set, regardless of position, and RNG * subclasses that alter the odds won't work as expected here, particularly if those subclasses use doubles * internally (which almost always produce less than 64 random bits). You should definitely avoid using certain * RandomnessSources that aren't properly pseudo-random, such as any QRNG class (SobolQRNG and VanDerCorputQRNG, * pretty much), since these won't fill all 64 bits with equal likelihood. * * @param bitCount an int, only considered if between 0 and 64, that is the average number of bits to set * @return a 64-bit long that, on average, should have bitCount bits set to 1, potentially anywhere in the long */ public long approximateBits(int bitCount) { if (bitCount <= 0) return 0L; if (bitCount >= 64) return -1L; if (bitCount == 32) return random.nextLong(); boolean high = bitCount > 32; int altered = (high ? 64 - bitCount : bitCount), lsb = Integer.lowestOneBit(altered); long data = random.nextLong(); for (int i = lsb << 1; i <= 16; i <<= 1) { if ((altered & i) == 0) data &= random.nextLong(); else data |= random.nextLong(); } return high ? ~(random.nextLong() & data) : (random.nextLong() & data); } /** * Gets a somewhat-random long with exactly 32 bits set; in each pair of bits starting at bit 0 and bit 1, then bit * 2 and bit 3, up to bit 62 and bit 3, one bit will be 1 and one bit will be 0 in each pair. * * @return a random long with 32 "1" bits, distributed so exactly one bit is "1" for each pair of bits */ public long randomInterleave() { long bits = nextLong() & 0xFFFFFFFFL, ib = ~bits & 0xFFFFFFFFL; bits |= (bits << 16); ib |= (ib << 16); bits &= 0x0000FFFF0000FFFFL; ib &= 0x0000FFFF0000FFFFL; bits |= (bits << 8); ib |= (ib << 8); bits &= 0x00FF00FF00FF00FFL; ib &= 0x00FF00FF00FF00FFL; bits |= (bits << 4); ib |= (ib << 4); bits &= 0x0F0F0F0F0F0F0F0FL; ib &= 0x0F0F0F0F0F0F0F0FL; bits |= (bits << 2); ib |= (ib << 2); bits &= 0x3333333333333333L; ib &= 0x3333333333333333L; bits |= (bits << 1); ib |= (ib << 1); bits &= 0x5555555555555555L; ib &= 0x5555555555555555L; return (bits | (ib << 1)); } @Override public String toString() { return "RNG with Randomness Source " + random; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof RNG)) return false; RNG rng = (RNG) o; return random.equals(rng.random); } @Override public int hashCode() { return random.hashCode(); } }