3

我想编写一个算法来生成 1-7 之间的随机数,给定一个生成 1-5 之间的随机数的方法。

我认为一种解决方案是 rand(5)/5*7 ??

我认为这应该有效。

谁能告诉我一个最佳解决方案?

我在某处读过这个解决方案,但我不知道他们是如何考虑保留“int num = 5 * (rand5() - 1) + (rand5() - 1);”的 . 我知道它会生成一个 1-7 之间的随机数,但他们是如何思考这个逻辑的,或者他们试图传达的内容并没有进入我的脑海。任何人都可以对此有所了解。

public static int rand7() {
while (true) {
    int num = 5 * (rand5() - 1) + (rand5() - 1);
    if (num < 21) 
        return (num % 7 + 1);
}
}
4

2 回答 2

12

您发布的代码是一个接受拒绝算法的示例。表达方式

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()返回一个整数,而不是浮点数。)

于 2013-08-23T04:35:33.103 回答
1

将 5 更改为您想要的数字。你必须导入import java.util.Random;

int range = 7; // Now you can change your limit here.
Random r = new Random();
int number = r.nextInt(range);

它的另一种解决方案不使用rand5()

于 2013-08-23T04:33:16.540 回答