在指定概率分布范围内随机 select 个数字
Randomly select numbers in a range from a specified probability distribution
我想 select 一个范围为 3-10 或 4-6 的随机数。数字应该 selected,数字越小,它被 selected 的机会就越大。我下面的代码只有 selects 每个数字的概率相等。
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%
等发行版中 select?
这是最简单的(从 "understanding the code" 的角度来看)的方法:
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;
}
显然,您可以针对其他数字和选择这些数字的其他机会进行调整,但这只是为了演示基础知识。
另请注意,谷歌搜索 "weighted random number generator java" returns 大量结果,包括来自 Whosebug 的大量答案。
如果您的分布可能会发生变化,所以您不想像 Kevin 的回答那样对您的分布进行硬编码,您可以使用 NavigableMap
with the ceilingEntry
方法。这允许您为 selection 选项分配权重。然后,您可以生成一个从 0 到权重总和的随机数。然后根据您的加权分布统计输出。
select 条目的代码在创建地图后非常短:
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 个 select 离子给出了以下权重:
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);
}
对初始随机数使用 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]
这对我来说像是一个比例因子。
我想 select 一个范围为 3-10 或 4-6 的随机数。数字应该 selected,数字越小,它被 selected 的机会就越大。我下面的代码只有 selects 每个数字的概率相等。
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%
等发行版中 select?
这是最简单的(从 "understanding the code" 的角度来看)的方法:
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;
}
显然,您可以针对其他数字和选择这些数字的其他机会进行调整,但这只是为了演示基础知识。
另请注意,谷歌搜索 "weighted random number generator java" returns 大量结果,包括来自 Whosebug 的大量答案。
如果您的分布可能会发生变化,所以您不想像 Kevin 的回答那样对您的分布进行硬编码,您可以使用 NavigableMap
with the ceilingEntry
方法。这允许您为 selection 选项分配权重。然后,您可以生成一个从 0 到权重总和的随机数。然后根据您的加权分布统计输出。
select 条目的代码在创建地图后非常短:
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 个 select 离子给出了以下权重:
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);
}
对初始随机数使用 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]
这对我来说像是一个比例因子。