从具有特定概率的离散元素集中进行选择的典型方法是选择一个随机浮点数并找出它所在的范围。我将用一个例子来解释。假设您在三个字母 A、B 和 C 中进行选择,概率分别为 0.255、0.407 和 0.338。您将计算一个介于 0 和 1 之间的随机数
double r = Math.random();
并首先将其与 0 到 0.255 的范围进行比较:
if (r < 0.255) {
return 'A';
}
然后到 0.255 到 (0.255 + 0.407) 的范围:
else if (r < 0.662) {
return 'B';
}
如果不是这两个,它必须是'C':
else {
return 'C';
}
如果您要对所有 26 个字母表进行此操作,那么写出 if-else 语句的所有 26 个大小写会很痛苦。您可以提前做的是准备一个字符数组及其各自的概率,
char[] chars = {'A', 'B', 'C', ...};
double[] probabilities = {0.01, 0.02, 0.05, ...};
然后您可以使用这样的循环自动执行所有if-ing:
double r = Math.random();
double cdf = 0.0;
for (int i = 0; i < chars.length; i++) {
cdf += probabilities[i]
if (r < cdf) {
return chars[i];
}
}
return chars[chars.length - 1];
在您的情况下,如果您的所有概率都是 1/96 的倍数,那么您可以选择小于 96 的随机整数而不是浮点数来做同样的事情。只需使用ints 代替doubles,并使用rnd.nextInt(96) 选择0 到95 之间的整数,包括0 和95,而不是Math.random()。此外,您的 probabilities 数组将包含实际概率乘以 96。
char[] chars = {'A', 'B', 'C', ...};
int[] probabilities = {5, 2, 4, ...}; // needs to sum to 96
// later...
int r = rnd.nextInt(96);
int cdf = 0;
for (int i = 0; i < chars.length; i++) {
cdf += probabilities[i]
if (r < cdf) {
return chars[i];
}
}
return chars[chars.length - 1];
现在,如果您正在执行诸如从袋子中抽出拼字游戏瓷砖之类的事情,那么它会变得更加棘手,因为这是一个无需替换的抽样过程,即每次抽奖后概率都会发生变化。我认为在这种情况下更好的方法是实际使用一个集合来模拟袋子,然后为每个上面有该字母的图块添加一个字母的副本。您仍然可以使用与之前相同的 chars 和 probabilities 数组在循环中执行此操作:
char[] chars = {'A', 'B', 'C', ...};
int[] probabilities = {5, 2, 4, ...}; // number of tiles with each letter
LinkedList<Character> bag = new LinkedList<Character>();
for (int i = 0; i < chars.length; i++) {
for (int n = 0; n < probabilities[i]; n++) {
bag.add(chars[i]);
}
}
然后你可以bag.shuffle() 来随机化图块,bag.pop() 让你随机选择一个。