Java中的离散概率分布

时间:2016-02-29 13:52:04

标签: java math probability

我有一组整数,每个整数都有一个概率,来自早期的实验,例如:

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也没有。

我想知道这是否是因为我错过了一个简单的解决方案。一个天真的实施似乎或多或少是直截了当的,但有效地做这件事是相当复杂的。这就是我正在寻找既定实施的原因。

3 个答案:

答案 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;