亚当的回答是正确的。但是,只是为了澄清两者之间的区别,第二个将可能的范围提高了 1 以使范围包含在内。需要记住的重要一点是,模是余数除法,因此虽然有toNumber
可能的结果,但其中一个是零(如果 的结果arc4random()
是 的倍数toNumber
)并且toNumber
本身不能是余数。
// 1.
arc4random() % (10 - 5) + 5;
这导致范围0 + 5
为4 + 5
5 到 9。
//2.
arc4random() % ((10 - 5) + 1) + 5;
这导致范围0 + 5
为(4 + 1) + 5
5 到 10。
如果您想使用模数,则既不正确也不不正确。一个不包括上限,另一个包括上限。但是,如果您考虑余数除法的工作原理并考虑任何 PRNG 返回的数字池以周期为单位的总范围长度,那么您会意识到,如果范围没有均匀地划分为最大范围你会得到有偏见的结果。例如,如果arc4random()
返回一个从 1 到 5 的结果(显然不是)并且您想要一个从 0 到 2 的数字,并且您使用arc4random() % 3
了 ,那么这些是可能的结果。
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0
4 % 3 = 1
5 % 3 = 2
请注意,有两个一和两个二,但只有一个零。这是因为我们的 3 范围并没有均匀地划分为 PRNG 的 5 范围。结果是(足够幽默)PRNG range % desired range
循环结束时的数字需要被剔除,因为它们是“有偏见的”——数字本身不是确实有偏见,但从最后剔除更容易。不这样做会导致范围的较小数字变得更有可能出现。
我们可以通过计算我们可以生成的数字的上限来剔除这些数字,将其与所需的范围取模,然后将这些数字从末尾拉出。通过“从末尾拉出这些数字”,我的真正意思是“无限循环,直到我们得到一个不是末尾数字之一的数字”。
有人会说这是不好的做法;理论上你可以永远循环。然而,在实践中,预期的重试次数总是小于 1,因为模偏差永远不会超过 PRNG 数量池的一半(通常远小于该数量)。我曾经为rand
使用这种技术编写了一个包装器。
您可以在OpenBSD的源代码中看到一个这样的示例,其中循环arc4random_uniform
调用arc4random
,直到确定一个数字是干净的。