//package gr.forth.ics.util;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Random;
/**
* A random choice maker, where each choice is associated with a probability. This implementation
* is based on the fast alias method, where for each random choice two random
* numbers are generated and only a single table lookup performed.
*
* @param the type of the choices to be made
* @see L. Devroye, Non-Uniform Random Variate Generation, 1986, p. 107
* @author Andreou Dimitris, email: jim.andreou (at) gmail (dot) com
*/
public class RandomChooser {
private final double[] probs;
private final int[] indexes;
private final List events;
private final Random random;
private RandomChooser(List weights, List events, Random random) {
double sum = 0.0;
for (double prob : weights) sum += prob;
this.probs = new double[weights.size()];
for (int i = 0; i < weights.size(); i++) {
probs[i] = weights.get(i) * weights.size() / sum; //average = 1.0
}
Deque smaller = new ArrayDeque(weights.size() / 2 + 2);
Deque greater = new ArrayDeque(weights.size() / 2 + 2);
for (int i = 0; i < probs.length; i++) {
if (probs[i] < 1.0) {
smaller.push(i);
} else {
greater.push(i);
}
}
indexes = new int[weights.size()];
while (!smaller.isEmpty()) {
Integer i = smaller.pop();
Integer k = greater.peek();
indexes[i] = k;
probs[k] -= (1 - probs[i]);
if (probs[k] < 1.0) {
greater.pop();
if (greater.isEmpty()) break;
smaller.push(k);
}
}
this.events = events;
this.random = random;
}
/**
* Returns a random choice.
*
* @return a random choice
* @see RandomChooserBuilder about how to configure the available choices
*/
public T choose() {
int index = random.nextInt(probs.length);
double x = random.nextDouble();
return x < probs[index] ? events.get(index) : events.get(indexes[index]);
}
/**
* Creates a builder of a {@link RandomChooser} instance. The builder is responsible
* for configuring the choices and probabilities of the random chooser.
*
* @param the type of the choices that will be randomly made
* @return a builder of a {@code RandomChooser} object
*/
public static RandomChooserBuilder newInstance() {
return new RandomChooserBuilder();
}
/**
* A builder of {@link RandomChooser}.
*
* @param the type of the choices that the created {@code RandomChooser} will make
*/
public static class RandomChooserBuilder {
private final List probs = new ArrayList();
private final List events = new ArrayList();
private Random random = new Random(0);
private RandomChooserBuilder() { }
/**
* Adds the possibility of a given choice, weighted by a relative probability.
* (Relative means that it is not needed that all probabilities have sum {@code 1.0}).
*
* @param choice a possible choice
* @param prob the relative probability of the choice; must be {@code >= 0}
* @return this
*/
public RandomChooserBuilder choice(T choice, double prob) {
Args.gte(prob, 0.0);
Args.notNull(choice);
probs.add(prob);
events.add(choice);
return this;
}
/**
* Specifies the random number generator to be used by the created {@link RandomChooser}.
*
* @param random the random number generator to use
* @return this
*/
public RandomChooserBuilder setRandom(Random random) {
this.random = random;
return this;
}
/**
* Builds a {@link RandomChooser} instance, ready to make random choices based on the
* probabilities configured by this builder.
*
* @return a {@code RandomChooser}
*/
public RandomChooser build() {
if (probs.isEmpty()) {
throw new IllegalStateException("No choice was defined");
}
return new RandomChooser(
new ArrayList(probs),
new ArrayList(events),
random);
}
}
}
class Args {
private static final String GT = " must be greater than ";
private static final String GTE = " must be greater or equal to ";
private static final String LT = " must be less than ";
private static final String LTE = " must be less or equal to ";
private static final String EQUALS = " must be equal to ";
public static void doesNotContainNull(Iterable> iterable) {
notNull(iterable);
for (Object o : iterable) {
notNull("Iterable contains null", o);
}
}
public static void isTrue(boolean condition) {
isTrue("Condition failed", condition);
}
public static void isTrue(String msg, boolean condition) {
if (!condition) {
throw new RuntimeException(msg);
}
}
public static void notNull(Object o) {
notNull(null, o);
}
public static void notNull(String arg, Object o) {
if (arg == null) {
arg = "Argument";
}
if (o == null) {
throw new IllegalArgumentException(arg + " is null");
}
}
public static void notNull(Object... args) {
notNull(null, args);
}
public static void notNull(String message, Object... args) {
if (message == null) {
message = "Some argument";
}
for (Object o : args) {
notNull(message, o);
}
}
public static void notEmpty(Iterable> iter) {
notEmpty(null, iter);
}
public static void notEmpty(String arg, Iterable> iter) {
if (arg == null) {
arg = "Iterable";
}
notNull(iter);
if (iter.iterator().hasNext()) return;
throw new IllegalArgumentException(arg + " is empty");
}
public static void hasNoNull(Iterable> iter) {
hasNoNull(null, iter);
}
public static void hasNoNull(String arg, Iterable> iter) {
notNull(iter);
if (arg == null) {
arg = "Iterable";
}
for (Object o : iter) {
if (o == null) {
throw new IllegalArgumentException(arg + " contains null");
}
}
}
public static void equals(int value, int expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(long value, long expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(double value, double expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(float value, float expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(char value, char expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(short value, short expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(byte value, byte expected) {
if (value == expected) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void equals(Object value, Object expected) {
if (value == expected || value.equals(expected)) return;
throw new IllegalArgumentException(value + EQUALS + expected);
}
public static void gt(int value, int from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(int value, int from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(int value, int from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(int value, int from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(long value, long from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(long value, long from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(long value, long from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(long value, long from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(short value, short from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(short value, short from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(short value, short from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(short value, short from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(byte value, byte from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(byte value, byte from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(byte value, byte from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(byte value, byte from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(char value, char from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(char value, char from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(char value, char from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(char value, char from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(double value, double from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(double value, double from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(double value, double from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(double value, double from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(float value, float from) {
if (value > from) return;
throw new IllegalArgumentException(value + GT + from);
}
public static void lt(float value, float from) {
if (value < from) return;
throw new IllegalArgumentException(value + LT + from);
}
public static void gte(float value, float from) {
if (value >= from) return;
throw new IllegalArgumentException(value + GTE + from);
}
public static void lte(float value, float from) {
if (value <= from) return;
throw new IllegalArgumentException(value + LTE + from);
}
public static void gt(Comparable c1, T c2) {
if (c1.compareTo(c2) > 0) return;
throw new IllegalArgumentException(c1 + GT + c2);
}
public static void lt(Comparable c1, T c2) {
if (c1.compareTo(c2) < 0) return;
throw new IllegalArgumentException(c1 + LT + c2);
}
public static void gte(Comparable c1, T c2) {
if (c1.compareTo(c2) >= 0) return;
throw new IllegalArgumentException(c1 + GTE + c2);
}
public static void lte(Comparable c1, T c2) {
if (c1.compareTo(c2) <= 0) return;
throw new IllegalArgumentException(c1 + LTE + c2);
}
public static void inRangeII(Comparable value, T from, T to) {
gte(value, from);
lte(value, to);
}
public static void inRangeEE(Comparable value, T from, T to) {
gt(value, from);
lt(value, to);
}
public static void inRangeIE(Comparable value, T from, T to) {
gt(value, from);
lt(value, to);
}
public static void inRangeEI(Comparable value, T from, T to) {
gt(value, from);
lte(value, to);
}
public static void inRangeII(int value, int from, int to) {
gte(value, from);
lte(value, to);
}
public static void inRangeEE(int value, int from, int to) {
gt(value, from);
lt(value, to);
}
public static void inRangeIE(int value, int from, int to) {
gte(value, from);
lt(value, to);
}
public static void inRangeEI(int value, int from, int to) {
gt(value, from);
lte(value, to);
}
public static void inRangeII(long value, long from, long to) {
gte(value, from);
lte(value, to);
}
public static void inRangeEE(long value, long from, long to) {
gt(value, from);
lt(value, to);
}
public static void inRangeIE(long value, long from, long to) {
gte(value, from);
lt(value, to);
}
public static void inRangeEI(long value, long from, long to) {
gt(value, from);
lte(value, to);
}
public static void inRangeII(short value, short from, short to) {
gte(value, from);
lte(value, to);
}
public static void inRangeEE(short value, short from, short to) {
gt(value, from);
lt(value, to);
}
public static void inRangeIE(short value, short from, short to) {
gte(value, from);
lt(value, to);
}
public static void inRangeEI(short value, short from, short to) {
gt(value, from);
lte(value, to);
}
public static void inRangeII(byte value, byte from, byte to) {
gte(value, from);
lte(value, to);
}
public static void inRangeEE(byte value, byte from, byte to) {
gt(value, from);
lt(value, to);
}
public static void inRangeIE(byte value, byte from, byte to) {
gte(value, from);
lt(value, to);
}
public static void inRangeEI(byte value, byte from, byte to) {
gt(value, from);
lte(value, to);
}
public static void check(boolean assertion, String messageIfFailed) {
if (!assertion) {
throw new RuntimeException(messageIfFailed);
}
}
}