Draw.java

  1. package org.opentrafficsim.core.math;

  2. import java.util.Collection;
  3. import java.util.Iterator;
  4. import java.util.Map;

  5. import org.djutils.exceptions.Throw;

  6. import nl.tudelft.simulation.jstats.streams.StreamInterface;

  7. /**
  8.  * Utility to draw from a collection.
  9.  * <p>
  10.  * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  11.  * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
  12.  * <p>
  13.  * @version $Revision$, $LastChangedDate$, by $Author$, initial version 23 aug. 2018 <br>
  14.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  15.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  16.  * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
  17.  */
  18. public final class Draw
  19. {

  20.     /** Constructor. */
  21.     private Draw()
  22.     {
  23.         // no instance
  24.     }

  25.     /**
  26.      * Returns a randomly drawn element using draw weights.
  27.      * @param map Map&lt;E, ? extends Double&gt;; map of elements and respective weights
  28.      * @param stream StreamInterface; random number stream
  29.      * @param <E> element type
  30.      * @return E; randomly drawn element
  31.      */
  32.     public static <E> E drawWeighted(final Map<E, ? extends Double> map, final StreamInterface stream)
  33.     {
  34.         Throw.whenNull(map, "Map may not be null.");
  35.         Throw.whenNull(stream, "Stream may not be null.");
  36.         Throw.when(map.isEmpty(), IllegalArgumentException.class, "Map may not be empty.");
  37.         double sumProb = 0.0;
  38.         for (E e : map.keySet())
  39.         {
  40.             double w = map.get(e);
  41.             Throw.when(w < 0.0, IllegalArgumentException.class, "Probabilities should be at least 0.0.");
  42.             sumProb += w;
  43.         }
  44.         Throw.when(sumProb == 0.0, IllegalArgumentException.class, "Probabilities should not add to 0.0.");

  45.         if (map.size() == 1)
  46.         {
  47.             return map.keySet().iterator().next();
  48.         }
  49.         double r = stream.nextDouble() * sumProb;
  50.         sumProb = 0.0;
  51.         E last = null;
  52.         for (E e : map.keySet())
  53.         {
  54.             double f = map.get(e);
  55.             if (sumProb <= r && r <= sumProb + f)
  56.             {
  57.                 return e;
  58.             }
  59.             sumProb += f;
  60.             last = e;
  61.         }
  62.         return last; // rounding error
  63.     }

  64.     /**
  65.      * Returns a randomly drawn element using uniform weights.
  66.      * @param collection Collection&lt;E&gt;; collection of elements
  67.      * @param stream StreamInterface; random number stream
  68.      * @param <E> element type
  69.      * @return E; randomly drawn element
  70.      */
  71.     public static <E> E draw(final Collection<E> collection, final StreamInterface stream)
  72.     {
  73.         Throw.whenNull(collection, "Collection may not be null.");
  74.         Throw.whenNull(stream, "Stream may not be null.");
  75.         Throw.when(collection.isEmpty(), IllegalArgumentException.class, "Collection may not be empty.");
  76.         int n = (int) (collection.size() * stream.nextDouble());
  77.         Iterator<E> it = collection.iterator();
  78.         int m = 0;
  79.         while (m < n)
  80.         {
  81.             m++;
  82.             it.next();
  83.         }
  84.         return it.next();
  85.     }

  86. }