/** * */ package vroom.common.utilities; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import umontreal.iro.lecuyer.rng.RandomStream; /** * <code>RouletteWheel</code> is a simple implementation of a roulette wheel. * <p> * Creation date: May 12, 2011 - 2:03:33 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class RouletteWheel<O> { private final List<WheelElement<O>> mWheel; private boolean mChanged; /** * Creates a new empty <code>RouletteWheel</code> */ public RouletteWheel() { mWheel = new LinkedList<WheelElement<O>>(); } /** * Creates a new <code>RouletteWheel</code> * * @param evaluations * a mapping between objects and their evaluation */ public RouletteWheel(Map<O, Double> evaluations) { this(); addAll(evaluations); } /** * Add an object to this wheel. * <p> * If this wheel already contains the object, then it will be added a second time. * </p> * * @param object * the object to be added * @param evaluation * the evaluation of the added <code>object</code> */ public void add(O object, double evaluation) { if (evaluation < 0) throw new IllegalArgumentException("Evaluation must be positive"); mWheel.add(new WheelElement<O>(object, evaluation)); mChanged = true; } /** * Add all the object from a map * * @param evaluations * a mapping between objects and their evaluation * @see #add(Object, double) */ public void addAll(Map<O, Double> evaluations) { for (Entry<O, Double> e : evaluations.entrySet()) { add(e.getKey(), e.getValue()); } } /** * Returns <code>true</code> if this wheel is empty * * @return <code>true</code> if this wheel is empty */ public boolean isEmpty() { return mWheel.isEmpty(); } /** * Returns the number of objects in this wheel * * @return the number of objects in this wheel */ public int size() { return mWheel.size(); } /** * Draw an object from this wheel * * @param stream * the {@link RandomStream} that will be used * @param removeAfterDraw * <code>true</code> if the object should be removed after being drawn * @return the drawn object */ public O drawObject(RandomStream stream, boolean removeAfterDraw) { if (size() == 0) return null; else if (size() == 1) return mWheel.get(0).object; updateWheel(); double rnd = stream.nextDouble(); Iterator<WheelElement<O>> it = mWheel.iterator(); double acc = 0; WheelElement<O> e = null; while (it.hasNext() && acc < rnd) { e = it.next(); acc += e.evaluation; } O object = e != null ? e.object : null; if (removeAfterDraw && object != null) { it.remove(); mChanged = true; } return object; } /** * Calculate the probability of each wheel element and sort the wheel */ private void updateWheel() { if (!mChanged) return; double cumulated = 0; for (WheelElement<O> e : mWheel) { cumulated += e.evaluation; } for (WheelElement<O> e : mWheel) { e.evaluation = e.evaluation / cumulated; } Collections.sort(mWheel); mChanged = false; } /** * Reset this selection wheel */ public void clear() { mWheel.clear(); mChanged = false; } @Override public String toString() { updateWheel(); StringBuilder sb = new StringBuilder(); sb.append("["); for (WheelElement<O> e : mWheel) { if (sb.length() > 1) sb.append(","); sb.append(e.toString()); } sb.append("]"); return sb.toString(); } /** * <code>WheelElement</code> representation of a wheel element for the random selection of an object * <p> * Creation date: 2 juil. 2010 - 21:00:56 * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ class WheelElement<OO> implements Comparable<WheelElement<O>> { final O object; double evaluation; /** * Creates a new <code>WheelElement</code> * * @param object * @param evaluation */ public WheelElement(O object, double probability) { this.object = object; this.evaluation = probability; } @Override public int compareTo(WheelElement<O> o) { return Double.compare(evaluation, o.evaluation); } @Override public String toString() { return String.format("[%s,%.4f]", object, evaluation); } } }