在代码中,程序员常通过生成一个大随机数并对选项数量取模来选择列表中的随机元素:`choice = items[rng() % n]`。博文展示了这种做法为何不能使每个项被等概率选择。如果随机数生成器在 0 到 9(共十种可能性)之间均匀分布,而我们需要从三个对象中选择一个,则映射为:值 {0,3,6,9} → 第一个对象,{1,4,7} → 第二个,{2,5,8} → 第三个。因此第一个对象被选中的概率是 4/10(40%),而其他两个对象各被选中的概率只有 3/10(30%)。这种均匀性的损失被称为取模偏差。

保持均匀性的正确方法是使用在所需范围内返回均匀整数的函数,例如 `random_between(l, h)`。对于三个选项,应调用 `random_between(0,2)` 并直接使用结果。更具表达力的 API 是 `random_choice(options)`,它接受相对整数权重;例如 `[(A,4),(B,3),(C,3)]` 在真正意图为如此时会产生相同的 40/30/30 分布,但允许程序员明确指定所需的概率。博文指出,大多数标准库(如 Python 的 `random.randrange`) 已经实现了此方法,且拥有 `random_between` 使得构建 `random_choice` 变得简单。

显示十个值映射到三个结果的随机选择中偏差的图示
说明取模运算如何导致随机选择时分布不均的示图ersc.io

此图可视化了文章中描述的偏差示例,其中十个随机值不均匀地映射到三个选择,导致选择概率为 40% 和 30%。

在 Antithesis,作者所在的团队审查了他们的模糊测试代码,并用一个 `weighted_choice` 辅助函数替换了所有临时的 `random_u64() % n` 调用,以驱动 Blob 大小分布(例如 88% Small,4% Medium,4% Large,4% XLarge)。此改动消除了其测试覆盖中隐藏的确定性偏差来源,并导致在出现更好的抽象之前暂停新的 `random_u64()` 用法。此事例说明了一种看似无害的便利如何扭曲随机实验,以及为什么更高层次、意图清晰的 API 是更可取的。