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