View Javadoc
1   package org.opentrafficsim.core.math;
2   
3   import java.util.Collection;
4   import java.util.Iterator;
5   import java.util.Map;
6   
7   import nl.tudelft.simulation.jstats.streams.StreamInterface;
8   import nl.tudelft.simulation.language.Throw;
9   
10  /**
11   * Utility to draw from a collection.
12   * <p>
13   * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
15   * <p>
16   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 23 aug. 2018 <br>
17   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
18   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
19   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
20   */
21  public final class Draw
22  {
23  
24      /** Constructor. */
25      private Draw()
26      {
27          // no instance
28      }
29  
30      /**
31       * Returns a randomly drawn element using draw weights.
32       * @param map Map&lt;E, Double&gt;; map of elements and respective weights
33       * @param stream StreamInterface; random number stream
34       * @param <E> element type
35       * @return E; randomly drawn element
36       */
37      public static <E> E drawWeighted(final Map<E, ? extends Double> map, final StreamInterface stream)
38      {
39          Throw.whenNull(map, "Map may not be null.");
40          Throw.whenNull(stream, "Stream may not be null.");
41          Throw.when(map.isEmpty(), IllegalArgumentException.class, "Map may not be empty.");
42          double sumProb = 0.0;
43          for (E e : map.keySet())
44          {
45              double w = map.get(e);
46              Throw.when(w < 0.0, IllegalArgumentException.class, "Probabilities should be at least 0.0.");
47              sumProb += w;
48          }
49          Throw.when(sumProb == 0.0, IllegalArgumentException.class, "Probabilities should not add to 0.0.");
50  
51          if (map.size() == 1)
52          {
53              return map.keySet().iterator().next();
54          }
55          double r = stream.nextDouble() * sumProb;
56          sumProb = 0.0;
57          E last = null;
58          for (E e : map.keySet())
59          {
60              double f = map.get(e);
61              if (sumProb <= r && r <= sumProb + f)
62              {
63                  return e;
64              }
65              sumProb += f;
66              last = e;
67          }
68          return last; // rounding error
69      }
70  
71      /**
72       * Returns a randomly drawn element using uniform weights.
73       * @param collection Collection&lt;E&gt;; collection of elements
74       * @param stream StreamInterface; random number stream
75       * @param <E> element type
76       * @return E; randomly drawn element
77       */
78      public static <E> E draw(final Collection<E> collection, final StreamInterface stream)
79      {
80          Throw.whenNull(collection, "Collection may not be null.");
81          Throw.whenNull(stream, "Stream may not be null.");
82          Throw.when(collection.isEmpty(), IllegalArgumentException.class, "Collection may not be empty.");
83          int n = (int) (collection.size() * stream.nextDouble());
84          Iterator<E> it = collection.iterator();
85          int m = 0;
86          while (m < n)
87          {
88              m++;
89              it.next();
90          }
91          return it.next();
92      }
93  
94  }