从具有每个元素的特定概率的数组中随机选择元素

时间:2012-06-28 17:46:10

标签: java

我希望从数组中随机选择元素,其中每个元素都具有被选择的特定概率。有没有一种有效的方法可以做到这一点,或者可能是已经实现了这一点的Java内置的东西?

5 个答案:

答案 0 :(得分:2)

O(log(n))方法(直接从answer to a very similar question翻录):

通常的技术是将数组转换为累积和数组:

 [10 60 5 25]  --> [10 70 75 100]

选择从零到累计总数的范围内的随机数(在示例中为0 <= x < 100)。然后,在累积数组上使用bisection将索引定位到原始数组中:

Random variable x      Index in the Cumulative Array      Value in Original Array
-----------------      -----------------------------      ----------------------
 0 <= x < 10                      0                            10
10 <= x < 70                      1                            60
70 <= x < 75                      2                             5
75 <= x < 100                     3                            25 

例如,如果随机变量 x 为4,则将累积数组二等分给出位置索引0,其对应于原始数组中的10。

并且,如果随机变量 x 是72,则将累积数组二等分给出位置索引2,其对应于原始数组中的5。

答案 1 :(得分:1)

这样做的一种方法是构建一个数组,其中必要时重复这些项以表示它们的概率。因此,如果数组中的项目A的概率为.3而项目B的概率为.7,则可以将它们放入10个项目的数组中,其中A重复3次,B重复7次。

然后你可以使用随机数生成器从数组中选择一个索引。

另一种解决方案是将每个项目及其概率加载到数据结构中,将每个概率表示为一个范围(即项目A可以表示范围.5-.8),然后从中生成随机值0-1并获取随机数落入的任何范围的值。

答案 2 :(得分:1)

一种方法是使用这样的加权概率:

MyClass getRandomElement(MyClass[] elements)
{
    int totalWeight = 0;
    for (MyClass element : elements)
    {
       totalWeight += element.weight;
    }

    int position = new Random().nextInt(totalWeight);

    for (MyClass element : elements)
    {
       if (position < element.weight)
       {
           return element;
       }
       position -= element.weight;
    }
    throw new IllegalStateException("Should never get here");
}

答案 3 :(得分:1)

如果编码选择数组中元素的概率权重(可能是通过数组中Objects的成员变量),则可以执行以下操作:

  1. 假设您有权重为整数的元素。
  2. 总结所有元素的权重。
  3. 创建1和该权重之间的随机值。
  4. 遍历数组,向上计数,直到匹配随机值。
  5. 示例:

    [1,3,2,5,2] 总和= 13 随机翻滚= 5

    元素[0] ..(我们已计入1)

    元素[1] ..(我们已计入4)

    元素[2] ..(我们已计入6) 6> 5因此我们选择2。

    现在,这需要O(n)时间,其中n是数组中值的数量。为了效率起见,更好的方法是将值放在值所指示的位置。这有点像:

    [a,b,b,b,c,c,d,d,d,d,d,e,e]。

    查看counting sort以获取更详细的信息;它允许O(1)访问。

答案 4 :(得分:0)

我个人喜欢使用NavigableMap的方法。这样的方法看起来像这样

interface Weighted {
    public double getWeight();
}

class UnbalancedRandomizer <E extends Weighted> {
    private NavigableMap<Double, E> container = new TreeMap<>();

    UnbalancedRandomizer(E... elements) {
        for (E element : elements) {
            add(element);
        }
    }

    public void add(E element) {
        double offset = container.isEmpty() ? 0.0 : container.lastEntry().getKey();
        container.put(offset + element.getWeight(), element);
    }

    public E getRandom() {
        double rolled = container.lastEntry().getKey() * Math.random();
        return container.ceilingEntry(rolled).getValue();
    }
}

但是,当您要删除元素或更改元素的权重时,这可能会变得有些棘手。毕竟,我相信在大多数用例中,添加才是真正有意义的。这些主要是用于查找要发生的事件的表(例如老虎机的符号机会),或者是用于对属性(例如体重/体重/年龄等)进行分组的匹配器。

示例用法

class Foo implements Weighted {
    private double weight;
    private String name;

    public Foo(double weight, String name) {
        this.weight = weight;
        this.name = name;
    }

    @Override
    public double getWeight() {
        return weight;
    }

    @Override
    public String toString() {
        return "Symbol{" +
                "weight=" + weight +
                ", name='" + name + '\'' +
                '}';
    }

    public static void main(String... args) {
        UnbalancedRandomizer<Foo> randomizer = new UnbalancedRandomizer(
                new Foo(10, "A"),
                new Foo(30, "B"),
                new Foo(50, "C"),
                new Foo(50, "D")
        );

        Stream.generate(randomizer::getRandom).limit(50).forEach(System.out::println);
    }
}

示例输出

Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='D'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='C'}
Symbol{weight=10.0, name='A'}
Symbol{weight=30.0, name='B'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=30.0, name='B'}
Symbol{weight=10.0, name='A'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='D'}
Symbol{weight=30.0, name='B'}
Symbol{weight=50.0, name='D'}
Symbol{weight=50.0, name='C'}
Symbol{weight=50.0, name='D'}
Symbol{weight=10.0, name='A'}
Symbol{weight=50.0, name='D'}