您发布的代码是一个接受拒绝算法的示例。表达方式
5 * (rand5() - 1) + (rand5() - 1);
生成一个均匀分布在 0 到 24 之间的随机数。第一项是 0 到 4 之间的随机数的 5 倍,以相等的概率产生集合 {0, 5, 10, 15, 20} 中的一个。第二个是 0 到 4 之间的随机数。由于这两个随机数可能是独立的,因此将它们相加得到一个均匀分布在 0 到 24 之间的随机数。第二个数字“填补”了第一项生成的数字之间的空白.
然后测试拒绝任何大于或等于 21 的数字并重复该过程。当一个数字通过测试时,它将是一个均匀分布在 0 到 20(含)之间的随机数。然后将此范围划分为 7 个数字(介于 0 和 6 之间),% 7
并在返回之前添加一个。由于区间 [0, 20] 中有 21 个数字,并且 21 可以被 7 整除,因此 0 和 6 之间的数字将以相等的概率出现。
在表中:
A B num
rand5()-1 rand5()-1 5 * A + B num % 7 + 1
--------- --------- --------- -----------
0 0 0 1
1 0 5 6
2 0 10 4
3 0 15 2
4 0 20 7
0 1 1 2
1 1 6 7
2 1 11 5
3 1 16 3
4 1 21 reject
0 2 2 3
1 2 7 1
2 2 12 6
3 2 17 4
4 2 22 reject
0 3 3 4
1 3 8 2
2 3 13 7
3 3 18 5
4 3 23 reject
0 4 4 5
1 4 9 3
2 4 14 1
3 4 19 6
4 4 24 reject
请注意,最后一列恰好包含 [1, 6] 范围内的每个数字中的三个。
理解逻辑的另一种方法是用 base-5 算术来思考。由于rand5()
生成一个介于 1 和 5(含)之间的随机整数,我们可以使用rand5() - 1
它为基数为 5 的数字生成随机数字。我们正在尝试生成数字 1 到 7(或等效地,从 0 到 6),因此我们需要至少两个以 5 为基数的数字才能拥有七个数字。因此,让我们逐位生成一个随机的、两位数、以 5 为底的数字。就是5*(rand5() - 1) + (rand5() - 1)
这样。然后我们可以一遍又一遍地这样做,直到我们得到一个介于 0 和 6 之间的数字(0 和 11 5)。但是,拒绝我们正在生成的两位数中的更少会更有效。我们可以通过使用最多(但不包括)最大的两位数、基数为 5 的数字(即 7 的倍数)来做到这一点。恰好是 415,即 21 10。(我们排除 41 5本身,因为我们从 0 开始,而不是 1。)因为我们想要一个介于 0 和 6 之间的数字,所以我们使用 . 缩小范围% 7
。(我们也可以除以 3,因为在 [0, 40 5 ] 中恰好有三个 7 整数范围。但是,使用余数运算符可以清楚地表明我们的目标是范围 [0, 6]。 )
PS您提出的解决方案不起作用。虽然它似乎具有正确的范围,但该表达式只能生成五个不同的数字,因此它不可能是正确的。它也可以产生 0(当rand5()
返回 1 时)。如果您正在处理浮点随机数生成,那么像这样的简单缩放将是一个好方法。(但是,您希望在缩放之前移至 0,然后移回 1 以获得正确的范围下限。我认为表达式是1 + 7 * (rand5() - 1) / 5
. 但rand5()
返回一个整数,而不是浮点数。)