我有一组整数,每个整数都有一个概率,来自早期的实验,例如:
0 = 0.5
1 = 0.2
2 = 0.3
遵守概率分布的规范,这些权重总和为1.0。 我现在正在寻找一种有效的方法来采样其中一个值,同时考虑给定的概率,例如(pseude码):
Distribution distribution = new DiscreteDistribution(new double[]{0.5, 0.3, 0.2});
distribution.sample();
根据给定的数字,这应该导致0的一半时间。但是,不要假设其中的任何模式或规律。
我以前的实验一直使用Apache Commons Math,但它似乎没有为这种情况提供解决方案,Colt也没有。
我想知道这是否是因为我错过了一个简单的解决方案。一个天真的实施似乎或多或少是直截了当的,但有效地做这件事是相当复杂的。这就是我正在寻找既定实施的原因。
答案 0 :(得分:4)
鉴于分位数功能的简单性以及手动实现的微不足道,我没有明确表示有任何损害。
在[0,1]中绘制随机数r
后,请使用
if (r <= 0.5/*micro-optimisation: most likely case first*/){
return 0;
} else if (r <= 0.8/*then the next most likely case*/){
return 2;
} else {
return 1;
}
对于3个以上的数字,事情可能会更加花哨,考虑在这种情况下建立一个代表分位数函数的表,代价是性能会有所下降。
(在速度方面很难打败我的解决方案,在最糟糕的情况下,你有几个分支 - 而且你以最好的方式帮助分支预测可能,并且随机数绘图将是性能瓶颈所在。)
答案 1 :(得分:3)
一个非常简单的通用解决方案是:
class Distribution<T>{
List<Double> probs = new ArrayList<>();
List<T> events = new ArrayList<>();
double sumProb;
Random rand = new Random();
Distribution(Map<T,Double> probs){
for(T event : probs.keySet()){
sumProb += probs.get(event);
events.add(event);
this.probs.add(probs.get(event));
}
}
public T sample(){
T value;
double prob = rand.nextDouble()*sumProb;
int i;
for(i=0; prob>0; i++){
prob-= probs.get(i);
}
return events.get(i-1);
}
}
随意更改,如您所需,例如添加其他构造函数。当然,从效率开始,这里有很多要改进的东西,但是你可以在以后重复使用它。
答案 2 :(得分:3)
调用Random.nextDouble()
是一项相当昂贵的操作。在这种情况下,最好使用Random.nextInt(n)
int num = rand.nextInt(10);
return num <= 5 ? 0 : num <= 8 ? 1 : 2;