package tc.oc.commons.core.random;
import java.util.NoSuchElementException;
import java.util.Random;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Holds a set of weighted items which can then looked up
* with a number in the range [0..1)
* @param <T> Item type
* @param <N> Numeric weight type
*/
public abstract class WeightedRandomChooser<T, N extends Number> {
/**
* Keep this package-private, don't want it to be part of the interface
*/
class Option {
protected final T element;
protected final double weight;
protected Option(T element, double weight) {
this.element = element;
this.weight = weight;
}
}
public abstract double totalWeight();
public abstract boolean isEmpty();
protected abstract T chooseInternal(double n);
/**
* Choose an item in a consistent way using the given number.
* The probability of each item being chosen is proportional
* its weight. Any particular number will always choose the
* same item. Beyond that, the choice mechanism is undefined.
*
* @param n Number in the range [0..1)
* @return An item passed to the constructor, or null if there are no items
*
* @throws NoSuchElementException if this chooser is empty
*/
public T choose(double n) {
checkArgument(n >= 0);
checkArgument(n < 1);
if(isEmpty()) {
throw new NoSuchElementException("No choices");
}
return chooseInternal(n);
}
/**
* Choose an item at random using the given generator.
* The probability of each item being chosen is proportional
* its weight. The choice will be consistent with regard to
* the state of the generator. Beyond that, the choice mechanism
* is undefined.
*
* @param random A Random instance
* @return An item passed to the constructor
*
* @throws NoSuchElementException if this chooser is empty
*/
public T choose(Random random) {
return choose(random.nextDouble());
}
public T choose(Entropy entropy) {
return choose(entropy.randomDouble());
}
}