Dealt with all the TODO's in getWeighted() but in a new override

of pick().
- Iterates once.
- Checks each weight for validity.
- Only generates one random number.

On that last point: huh! I don't think there's anything wrong
with what I've done, and passing a Map with all the values as 1 is
identical to calling the other form of pick on the keySet iterator.
So: maybe I can change the Iterator-based picking and plucking
to only use one random double, like this. In fact, it would make
those methods more optimal in another way: we could stop iteration
as soon as we see the Nth element, where N is (1 / random).

Deprecated getWeighted().
RFC.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@2915 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
ray.j.greenwell
2010-10-12 22:59:01 +00:00
parent 880e785a8a
commit 5a8ee16ec5
@@ -142,13 +142,41 @@ public class Randoms
return (float)_r.nextGaussian() * dev + mean;
}
/**
* Pick a random key from the specified mapping of weight values, or return
* <code>ifEmpty</code> if no mapping has a weight greater than 0.
*
* @throws IllegalArgumentException if any weight is less than 0.
*/
public <T> T pick (Map<? extends T, ? extends Number> weightMap, T ifEmpty)
{
T pick = ifEmpty;
double r = _r.nextDouble();
double total = 0.0;
for (Map.Entry<? extends T, ? extends Number> entry : weightMap.entrySet()) {
double weight = entry.getValue().doubleValue();
if (weight > 0.0) {
total += weight;
if ((r * total) < weight) {
pick = entry.getKey();
}
} else if (weight < 0.0) {
throw new IllegalArgumentException("Weight less than 0: " + entry);
} // else: weight == 0.0 is OK
}
return pick;
}
/**
* Returns a key from the supplied map according to a probability computed as
* the key's value divided by the total of all the key's values.
*
* @throws NullPointerException if the map is null.
* @throws IllegalArgumentException if the sum of the weights is not positive.
*
* @deprecated Use {@link #pick(Map, Object)} instead.
*/
@Deprecated
public <T> T getWeighted (Map<T, ? extends Number> valuesToWeights)
{
// TODO: validate each weight to ensure it's not below 0