I want to choose a random item from a set, but the chance of choosing any item should be proportional to the associated weight
Example inputs:
item weight ---- ------ sword of misery 10 shield of happy 5 potion of dying 6 triple-edged sword 1
So, if I have 4 possible items, the chance of getting any one item without weights would be 1 in 4.
In this case, a user should be 10 times more likely to get the sword of misery than the triple-edged sword.
How do I make a weighted random selection in Java?
In weighted random sampling (WRS) the items are weighted and the probability of each item to be selected is determined by its relative weight.
To generated a random number, weighted with a given probability, you can use a helper table together with a formula based on the RAND and MATCH functions. Notice, we are intentionally shifting the cumulative probability down one row, so that the value in D5 is zero.
Use the random. choices() Function to Generate Weighted Random Choices. Here, the random module of Python is used to make random numbers. In the choices() function, weighted random choices are made with a replacement.
I would use a NavigableMap
public class RandomCollection<E> { private final NavigableMap<Double, E> map = new TreeMap<Double, E>(); private final Random random; private double total = 0; public RandomCollection() { this(new Random()); } public RandomCollection(Random random) { this.random = random; } public RandomCollection<E> add(double weight, E result) { if (weight <= 0) return this; total += weight; map.put(total, result); return this; } public E next() { double value = random.nextDouble() * total; return map.higherEntry(value).getValue(); } }
Say I have a list of animals dog, cat, horse with probabilities as 40%, 35%, 25% respectively
RandomCollection<String> rc = new RandomCollection<>() .add(40, "dog").add(35, "cat").add(25, "horse"); for (int i = 0; i < 10; i++) { System.out.println(rc.next()); }
There is now a class for this in Apache Commons: EnumeratedDistribution
Item selectedItem = new EnumeratedDistribution<>(itemWeights).sample();
where itemWeights
is a List<Pair<Item, Double>>
, like (assuming Item
interface in Arne's answer):
final List<Pair<Item, Double>> itemWeights = Collections.newArrayList(); for (Item i: itemSet) { itemWeights.add(new Pair(i, i.getWeight())); }
or in Java 8:
itemSet.stream().map(i -> new Pair(i, i.getWeight())).collect(toList());
Note: Pair
here needs to be org.apache.commons.math3.util.Pair
, not org.apache.commons.lang3.tuple.Pair
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With