我想从集合中选择一个随机项目,但是选择任何项目的机会应与相关的权重成比例
输入示例:
item weight ---- ------ sword of misery 10 shield of happy 5 potion of dying 6 triple-edged sword 1
因此,如果我有4种可能的物品,那么没有重量的任何一件物品的机会将是四分之一。
在这种情况下,用户遭受痛苦之剑的可能性应该是三刃剑的十倍。
如何在Java中进行加权随机选择?
Apache Commons中现在有一个用于此的类:EnumeratedDistribution
EnumeratedDistribution
Item selectedItem = new EnumeratedDistribution<>(itemWeights).sample();
这里itemWeights是List<Pair<Item, Double>>,像(假设Item接口阿恩的答案):
itemWeights
List<Pair<Item, Double>>
final List<Pair<Item, Double>> itemWeights = Collections.newArrayList(); for (Item i: itemSet) { itemWeights.add(new Pair(i, i.getWeight())); }
或在Java 8中:
itemSet.stream().map(i -> new Pair(i, i.getWeight())).collect(toList());
注意: Pair这里需要是org.apache.commons.math3.util.Pair,不是org.apache.commons.lang3.tuple.Pair。
Pair
org.apache.commons.math3.util.Pair
org.apache.commons.lang3.tuple.Pair
我会使用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(); } }
假设我列出了动物狗,猫和马的概率分别为40%,35%和25%
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()); }