From 5a8ee16ec5ac7e84df210e390920c5642c56283f Mon Sep 17 00:00:00 2001 From: "ray.j.greenwell" Date: Tue, 12 Oct 2010 22:59:01 +0000 Subject: [PATCH] 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 --- .../java/com/samskivert/util/Randoms.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/com/samskivert/util/Randoms.java b/src/main/java/com/samskivert/util/Randoms.java index 5cdbbff8..f5b4ef26 100644 --- a/src/main/java/com/samskivert/util/Randoms.java +++ b/src/main/java/com/samskivert/util/Randoms.java @@ -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 + * ifEmpty if no mapping has a weight greater than 0. + * + * @throws IllegalArgumentException if any weight is less than 0. + */ + public T pick (Map weightMap, T ifEmpty) + { + T pick = ifEmpty; + double r = _r.nextDouble(); + double total = 0.0; + for (Map.Entry 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 getWeighted (Map valuesToWeights) { // TODO: validate each weight to ensure it's not below 0