理解「为何直接映射 random7() 到 random10() 会导致概率不均」

问题背景

我们需要用 random7()(生成 1-7 均匀分布的随机整数)实现 random10()(生成 1-10 均匀分布的随机整数)。直接通过缩放或取整映射会导致概率不均,本文将详细解释其原因。


直接映射的错误示例

假设尝试直接将 random7() 的结果缩放并取整到 1-10,常见错误代码如下:

def wrong_random10():
    x = random7()  # x ∈ {1, 2, ..., 7}
    return (x - 1) * 10 // 7 + 1  # 错误映射!

步骤分解

  1. 将 x 减 1,得到 {0, 1, ..., 6}(共 7 个值)。
  2. 乘以 10,得到 {0, 10, ..., 60}(间隔为 10)。
  3. 除以 7 并向下取整(// 7),得到 {0, 1, ..., 8}(因为 60 // 7 = 8)。
  4. 加 1 后映射到 {1, 2, ..., 9},但无法覆盖 10,且概率不均。

概率不均的具体表现

通过具体计算每个 x 对应的映射结果,可以直观看到分布问题:

映射关系表

x(random7() 结果)(x-1)*10 // 7最终映射 y((x-1)*10//7 + 1)
101
212
323
445
556
678
789

问题分析

  • ​缺失的数字​:4、7、10 从未出现(概率为 0)。
  • ​概率不均​:1、2、3、5、6、8、9 各对应 1 个 x(概率为 71​≈14.29%),而 4、7、10 概率为 0。
  • ​根本矛盾​:7 个输入无法均匀分配到 10 个输出(7÷10=0.7,非整数)。

数学解释:均匀分布的必要条件

要从输入空间 X(大小 m)生成输出空间 Y(大小 n)的均匀分布,需满足:

m≥n且m 是 n 的整数倍(或通过拒绝采样丢弃多余部分)

本例中的矛盾

  • 输入空间大小 m=7(random7() 的可能值数量)。
  • 输出空间大小 n=10(random10() 的可能值数量)。
  • 由于 7<10 且 7 与 10 互质(最大公约数为 1),无法将 7 个输入均匀分配到 10 个输出,必然导致部分输出概率偏高或缺失。

正确方法的简要思路:拒绝采样

为保证均匀性,需扩大输入空间,使其包含足够多的离散值(如 72=49),并拒绝多余部分:

  1. 调用两次 random7() 生成 num = (a-1)*7 + (b-1)(范围 0-48,共 49 个值)。

  2. 仅保留前 40 个值(40=4×10,是 10 的整数倍),映射到 1-10:

    if num < 40:
        return num % 10 + 1  # 每个数字对应 4 个输入,概率 \( \frac{4}{49} \)
  3. 若 num ≥ 40(剩余 9 个值),可复用或重试(进一步优化效率)。


总结

直接映射 random7() 到 random10() 会因输入与输出数量不满足整除关系,导致部分数字概率偏高或缺失。​拒绝采样通过扩大输入空间并丢弃多余部分,是保证均匀性的关键方法。