我想知道在每个数字有一定概率发生的特定范围内生成随机数的最佳方法(例如在Java中)是什么?
例如
使用以下概率从[1; 3]内生成随机整数:
P(1)= 0.2
P(2)= 0.3
P(3)= 0.5
现在我正在考虑在[0; 100]内生成随机整数的方法并执行以下操作:
如果它在[0; 20] - >我得到我的随机数1.
如果它在[21; 50] - >我得到我的随机数2.
如果它在[51; 100] - >我得到了随机数3.
你会说什么?
你的是一个非常好的方式,适用于任何范围.
只是想一想:另一种可能性是通过乘以常数乘数来摆脱分数,然后构建一个具有该乘数大小的数组.你可以乘以10乘以
P(1) = 2 P(2) = 3 P(3) = 5
然后创建一个带有反向值的数组 - '1'进入元素1和2,'2'进入3到6,依此类推:
P =(1,1,2,2,2,3,3,3,3,3);
然后你可以从这个数组中选择一个随机元素.
(添加.)使用kiruwka评论中示例的概率:
int[] numsToGenerate = new int[] { 1, 2, 3, 4, 5 }; double[] discreteProbabilities = new double[] { 0.1, 0.25, 0.3, 0.25, 0.1 };
导致全整数的最小乘数是20,这给了你
2, 5, 6, 5, 2
所以长度为numsToGenerate
20,具有以下值:
1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 5 5
分布完全相同:例如,"1"的概率现在是20分之二 - 仍为0.1.
这是基于你的原始概率加起来1.如果他们不加,则将总和乘以相同的因子(这也将是你的数组长度).
前段时间我写了一个帮助类来解决这个问题.源代码应该足够清楚地表明这个概念:
public class DistributedRandomNumberGenerator { private Map<Integer, Double> distribution; private double distSum; public DistributedRandomNumberGenerator() { distribution = new HashMap<>(); } public void addNumber(int value, double distribution) { if (this.distribution.get(value) != null) { distSum -= this.distribution.get(value); } this.distribution.put(value, distribution); distSum += distribution; } public int getDistributedRandomNumber() { double rand = Math.random(); double ratio = 1.0f / distSum; double tempDist = 0; for (Integer i : distribution.keySet()) { tempDist += distribution.get(i); if (rand / ratio <= tempDist) { return i; } } return 0; } }
该课程的用法如下:
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator(); drng.addNumber(1, 0.3d); // Adds the numerical value 1 with a probability of 0.3 (30%) // [...] Add more values int random = drng.getDistributedRandomNumber(); // Generate a random number
测试驱动程序以验证功能:
public static void main(String[] args) { DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator(); drng.addNumber(1, 0.2d); drng.addNumber(2, 0.3d); drng.addNumber(3, 0.5d); int testCount = 1000000; HashMap<Integer, Double> test = new HashMap<>(); for (int i = 0; i < testCount; i++) { int random = drng.getDistributedRandomNumber(); test.put(random, (test.get(random) == null) ? (1d / testCount) : test.get(random) + 1d / testCount); } System.out.println(test.toString()); }
此测试驱动程序的示例输出:
{1=0.20019100000017953, 2=0.2999349999988933, 3=0.4998739999935438}
如果您遇到性能问题而不是搜索所有n个值O(n)
你可以执行二进制搜索,费用为O(log n)
Random r=new Random(); double[] weights=new double[]{0.1,0.1+0.2,0.1+0.2+0.5}; // end of init double random=r.nextDouble(); // next perform the binary search in weights array
如果你有很多权重元素,你只需要平均访问log2(weights.length).
您已经在问题中编写了实现.;)
final int ran = myRandom.nextInt(100); if (ran > 50) { return 3; } else if (ran > 20) { return 2; } else { return 1; }
您可以通过在交换机表上计算结果来加快速度,以实现更复杂的实现,如下所示:
t[0] = 1; t[1] = 1; // ... one for each possible result return t[ran];
但是,只有在这是一个性能瓶颈并且每秒调用几百次时才应该使用它.