一尘不染

Java中的随机加权选择

java

我想从集合中选择一个随机项目,但是选择任何项目的机会应与相关的权重成比例

输入示例:

item                weight
----                ------
sword of misery         10
shield of happy          5
potion of dying          6
triple-edged sword       1

因此,如果我有4种可能的物品,那么没有重量的任何一件物品的机会将是四分之一。

在这种情况下,用户遭受痛苦之剑的可能性应该是三刃剑的十倍。

如何在Java中进行加权随机选择?


阅读 535

收藏
2020-03-16

共2个答案

一尘不染

Apache Commons中现在有一个用于此的类:EnumeratedDistribution

Item selectedItem = new EnumeratedDistribution<>(itemWeights).sample();

这里itemWeightsList<Pair<Item, Double>>,像(假设Item接口阿恩的答案):

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

2020-03-16
一尘不染

我会使用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());
} 
2020-03-16