随机选择指定概率分布范围内的数字

时间:2015-01-28 15:07:02

标签: java random range

我想从3-10或4-6等范围中选择一个随机数。应该选择数字,使得数字越低,选择的机会就越多。我下面的代码只选择具有相同概率的每个数字。

private int bonusPoints;
private double randomBonusPoints = Math.Random() * 100;

bonusPoints = (int)randomBonusPoints;

如何从P(3,4,5)=85%, P(6,7,8)=10%, P(9,10)=5%

等分发中进行选择

3 个答案:

答案 0 :(得分:6)

如果您的发行版可能会发生变化,那么您不希望以Kevin的回答方式对您的发行版进行硬编码,您可以使用NavigableMap ceilingEntry方法。这允许您为选择选项指定权重。然后,您可以生成从0到权重总和的随机数。然后根据您的加权分布对输出进行统计。

创建地图后,选择条目的代码非常短:

Random rand = new Random();
...
double rnd = rand.nextDouble() * totalWeight;
int elem = map.ceilingEntry(rnd).getValue();

要创建任意分布,请执行以下操作:

int[] options = new int[]{3,4,5,6,7,8,9,10};
double[] weights = new double[]{ 0.85/3d, 0.85/3d, 0.85/3d,
                                 0.10/3d, 0.10/3d, 0.10/3d,
                                 0.05/2d, 0.05/2d };

NavigableMap<Double, Integer> map = new TreeMap<Double, Integer>();
double totalWeight = 0d;

for (int i = 0; i < weights.length; i++) {
    totalWeight += weights[i];
    map.put(totalWeight, options[i]);
}

测试这10000多个选项给出了以下权重:

03: 28.99% 
04: 28.10% 
05: 28.06% 
06: 3.27% 
07: 3.62% 
08: 3.08% 
09: 2.40% 
10: 2.48%  

测试代码:

//  select from the weighted elements
Random rand = new Random();
HashMap<Integer, Double> freqs = new HashMap<Integer, Double>();
int iterations = 10000;
for(int i = 0; i < iterations; i++) {
    double rnd = rand.nextDouble() * totalWeight;
    int elem = map.ceilingEntry(rnd).getValue();
    freqs.put(elem, (freqs.containsKey(elem) ? freqs.get(elem) : 0) + (1d/iterations));
}
Map<Integer, Double> sortedFreqs = new TreeMap<Integer, Double>(freqs);

for(Map.Entry<Integer,Double> entry : sortedFreqs.entrySet()) {
    System.out.printf("%02d: %.2f%% %n", entry.getKey(), entry.getValue() * 100d);
}

答案 1 :(得分:1)

这是最简单的(从“理解代码”的角度来看)这样做的方式:

int choice;
double r = Math.random();

if(r < .5){  //50% chance to choose 4
   choice = 4;
}
else if(r < .9){  //40% chance to choose 5
   choice = 5;
}
else{  //10% chance to choose 6
   choice = 6;
}

显然,您可以调整其他数字和其他机会来选择这些数字,但这证明了基础知识。

另请注意,谷歌搜索“加权随机数生成器java”会返回大量结果,包括来自StackOverflow的一堆答案。

答案 2 :(得分:0)

在初始随机数上使用Math.pow可提供平滑的比例而不会损坏重新连续性。选择你的体重是有争议的,但结果看起来还不错。你也可以清楚地扩大或缩小它。

public long weightedRandom(long lowest, long highest, double weight) {
    // Even distribution r >= 0 and < 1.
    double r = Math.random();
    // Add the weight while we are still between 0 and 1.
    r = Math.pow(r, weight);
    // Scale it - r >= 0 and <= highest - lowest.
    r = r * (highest - lowest + 1);
    // Translate to lowest.
    r += lowest;
    // Floor to long.
    return (long) r;
}

private void test(double weight) {
    List<Integer> results = new ArrayList<>(10);
    for (int i = 0; i < 10; i++) {
        results.add(0);
    }
    for (int i = 0; i < 1000; i++) {
        int r = (int) weightedRandom(0, results.size() - 1, weight);
        results.set(r, results.get(r) + 1);
    }
    System.out.println("Weight: " + weight + " Results: " + results);
}

public void test() {
    test(1);
    test(10);
    test(.1);
    test(2);
}

结果如下:

Weight: 1.0 Results: [119, 91, 112, 84, 96, 95, 86, 112, 93, 112]
Weight: 10.0 Results: [773, 57, 44, 37, 23, 15, 16, 9, 18, 8]
Weight: 0.1 Results: [0, 0, 0, 0, 0, 8, 14, 76, 243, 659]
Weight: 2.0 Results: [331, 119, 100, 85, 87, 71, 53, 59, 48, 47]

对我来说,这看起来像是缩放因素。