用 Rand7() 实现 Rand10():拒绝采样 vs. 古典概型
2023-11-15 02:16:04
使用 Rand7() 实现 Rand10() 的两种方法:拒绝采样与古典概型
在计算机科学的领域,我们经常需要生成随机数来模拟真实世界的情况,进行游戏设计,甚至在密码学中确保安全。虽然我们可能会接触到各种各样的随机数生成器,但其中一个基本问题是如何仅使用 Rand7() 函数(它生成 1 到 7 之间的随机整数)来生成 Rand10() 函数(它生成 1 到 10 之间的随机整数)。
这个问题看似简单,但有两种主要的方法可以解决:拒绝采样和古典概型。让我们深入了解每种方法,探讨它们的优点和缺点。
拒绝采样:一种简单但低效的方法
想象一下你在进行一场数字抽奖游戏,里面有 1 到 10 的数字。你手头只有 Rand7(),但你仍然希望从中获得 1 到 10 的数字。拒绝采样就像一个挑剔的裁判,它不断生成 Rand7(),直到它得到想要的结果。
具体来说,拒绝采样的步骤如下:
- 重复调用 Rand7(),直到获得 1 到 5 之间的数字。
- 将该数字作为 Rand10() 的结果返回。
乍一看,这似乎很简单,但这种方法的问题在于效率低下。由于 Rand7() 可以生成 1 到 7 之间的数字,因此获得 1 到 5 之间的数字的概率仅为 2/7。这意味着你平均需要调用 Rand7() 3.5 次才能获得所需的数字,这可能会浪费大量时间和资源。
古典概型:一种高效的解决方案
拒绝采样就像一个固执的守门人,只允许特定的数字通过。而古典概型则更像一个灵活的智者,它找到了绕过限制的巧妙方法。
古典概型的工作原理如下:
- 重复调用 Rand7(),直到获得 1 到 42 之间的数字。
- 将该数字映射到 1 到 10 的范围内。
为什么这种方法更有效率呢?答案在于概率。Rand7() 的 49 个可能结果中有 42 个(42/49 ≈ 0.857)对应于 1 到 10 的范围。这意味着你只需要调用 Rand7() 1.15 次左右即可获得所需的结果。
实现细节:代码示例
为了让事情更清晰,让我们用代码实现古典概型的方法:
int rand10() {
while (true) {
int num = rand7();
if (num <= 5) {
return num;
}
if (num <= 42) {
return num % 10 + 6;
}
}
}
性能比较:效率至上
现在是见证两种方法效率的时候了。让我们假设我们想生成 100000 个随机数。
方法 | 平均调用次数 |
---|---|
拒绝采样 | 3.5 |
古典概型 | 1.15 |
正如预期的那样,古典概型以显着的优势胜出。它比拒绝采样快得多,平均只需调用 Rand7() 1.15 次,而拒绝采样需要 3.5 次。
结论:选择最佳方法
现在,我们已经了解了使用 Rand7() 实现 Rand10() 的两种方法。拒绝采样虽然简单,但效率低下。而古典概型则是一种更有效的方法,在需要快速生成大量随机数的应用程序中至关重要。
常见问题解答
-
为什么拒绝采样效率如此低下?
- 因为它只接受 Rand7() 中的特定子集(1 到 5),这导致了大量的拒绝和重新生成。
-
古典概型如何提高效率?
- 它利用了 Rand7() 输出的概率分布,专注于更可能产生所需结果的子集(1 到 42)。
-
哪种方法在实践中更好?
- 古典概型是实际应用程序的最佳选择,因为它提供了更高的效率和更低的开销。
-
拒绝采样是否还有用处?
- 是的,尽管效率低下,但它仍然可以用于生成特定分布的随机数,而古典概型可能不适用。
-
除了这两种方法之外,还有其他生成 Rand10() 的方法吗?
- 有多种方法可以生成 Rand10(),包括 Box-Muller 变换和 Ziggurat 算法。