package com.asteria.utility; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; /** * The {@link ThreadLocalRandom} wrapper that provides additional functionality * for generating pseudo-random numbers. In order to avoid sharing instances of * this class across multiple threads, this should only be instantiated locally * unless certain that it will never be accessed by another thread. * * @author lare96 <http://github.com/lare96> * @author Ryley Kimmel <ryley.kimmel@live.com> */ public final class RandomGen { /** * The backing {@link ThreadLocalRandom} that will pseudorandomly generate * numbers. It is generally preferred to use this over {@link Random} * because although {@code Random} is thread safe; the same seed is shared * concurrently, which leads to contention between multiple threads and * overhead as a result of that. Surprisingly because of the way that * {@code ThreadLocalRandom} works, even in completely single-threaded * situations it runs up to three times faster than {@code Random}. * * @see <a * href="http://java-performance.info/java-util-random-java-util-concurrent-threadlocalrandom-multithreaded-environments/">java.util.Random * and java.util.concurrent.ThreadLocalRandom in multithreaded * environments</a> */ private final ThreadLocalRandom random = ThreadLocalRandom.current(); /** * Gets the backing {@link ThreadLocalRandom}. * * @return the backing random instance. */ public ThreadLocalRandom get() { return random; } /** * Returns a pseudo-random {@code int} value between inclusive {@code min} * and inclusive {@code max}. * * @param min * the minimum inclusive number. * @param max * the maximum inclusive number. * @return the pseudo-random {@code int}. * @throws IllegalArgumentException * if {@code max - min + 1} is less than {@code 0}. * @see {@link #exclusive(int)}. */ public int inclusive(int min, int max) { if (max < min) { max = min + 1; } return random.nextInt((max - min) + 1) + min; } /** * Returns a pseudo-random {@code int} value between inclusive {@code 0} and * inclusive {@code range}. * * @param range * the maximum inclusive number. * @return the pseudo-random {@code int}. * @throws IllegalArgumentException * if {@code max - min + 1} is less than {@code 0}. * @see {@link #exclusive(int)}. */ public int inclusive(int range) { return inclusive(0, range); } /** * Returns a pseudo-random {@code int} value between inclusive {@code min} * and inclusive {@code max} excluding the specified numbers within the * {@code excludes} array. * * @param min * the minimum inclusive number. * @param max * the maximum inclusive number. * @return the pseudo-random {@code int}. * @throws IllegalArgumentException * if {@code max - min + 1} is less than {@code 0}. * @see {@link #inclusive(int, int)}. */ public int inclusiveExcludes(int min, int max, int... exclude) { Arrays.sort(exclude); int result = inclusive(min, max); while (Arrays.binarySearch(exclude, result) >= 0) { result = inclusive(min, max); } return result; } /** * Returns a pseudo-random {@code float} between inclusive {@code 0} and * exclusive {@code range}. * * @param range * The exclusive range. * @return The pseudo-random {@code float}. * @throws IllegalArgumentException * If the specified range is less than {@code 0}. */ public float floatRandom(float range) { if (range < 0F) throw new IllegalArgumentException("range <= 0"); return random.nextFloat() * range; } /** * Pseudo-randomly retrieves an index from {@code array}. * * @param array * the array to retrieve an index from. * @return the element retrieved from the array. */ public int randomIndex(Object[] array) { return (int) (random.nextDouble() * array.length); } /** * Pseudo-randomly retrieves a element from {@code array}. * * @param array * the array to retrieve an element from. * @return the element retrieved from the array. */ public <T> T random(T[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code int} from this {@code array}. * * @param array * the array to retrieve an {@code int} from. * @return the {@code int} retrieved from the array. */ public int random(int[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code long} from this {@code array}. * * @param array * the array to retrieve an {@code long} from. * @return the {@code long} retrieved from the array. */ public long random(long[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code double} from this {@code array}. * * @param array * the array to retrieve an {@code double} from. * @return the {@code double} retrieved from the array. */ public double random(double[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code short} from this {@code array}. * * @param array * the array to retrieve an {@code short} from. * @return the {@code short} retrieved from the array. */ public short random(short[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code byte} from this {@code array}. * * @param array * the array to retrieve an {@code byte} from. * @return the {@code byte} retrieved from the array. */ public byte random(byte[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code float} from this {@code array}. * * @param array * the array to retrieve an {@code float} from. * @return the {@code float} retrieved from the array. */ public float random(float[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code boolean} from this {@code array}. * * @param array * the array to retrieve an {@code boolean} from. * @return the {@code boolean} retrieved from the array. */ public boolean random(boolean[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves an {@code char} from this {@code array}. * * @param array * the array to retrieve an {@code char} from. * @return the {@code char} retrieved from the array. */ public char random(char[] array) { return array[(int) (random.nextDouble() * array.length)]; } /** * Pseudo-randomly retrieves a element from {@code list}. * * @param list * the list to retrieve an element from. * @return the element retrieved from the list. */ public <T> T random(List<T> list) { return list.get((int) (random.nextDouble() * list.size())); } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code T} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public <T> T[] shuffle(T[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); T a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code int} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public int[] shuffle(int[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); int a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code long} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public long[] shuffle(long[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); long a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code double} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public double[] shuffle(double[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); double a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code short} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public short[] shuffle(short[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); short a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code byte} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public byte[] shuffle(byte[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); byte a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code float} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public float[] shuffle(float[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); float a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code boolean} array. * * @param array * the array that will be shuffled. * @return the shuffled array. */ public boolean[] shuffle(boolean[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); boolean a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * An implementation of the Fisher-Yates shuffle algorithm that will shuffle * the elements of an {@code char} array. * * @param array * the array that will be shuffled. */ public char[] shuffle(char[] array) { for (int i = array.length - 1; i > 0; i--) { int index = random.nextInt(i + 1); char a = array[index]; array[index] = array[i]; array[i] = a; } return array; } /** * Determines if a pseudorandomly generated double rounded to two decimal * places is below or equal to {@code value}. * * @param value * the value to determine this for. * @return {@code true} if successful, {@code false} otherwise. */ public boolean success(double value) { return random.nextDouble() <= value; } }