要将一个01不等概率随机 (即生成0和1的概率不相等,例如 ( P(0) = p ), ( P(1) = 1-p ),其中 ( p \neq 0.5 ))转换为01等概率随机(即 ( P(0) = P(1) = 0.5 )),可以通过以下方法实现。核心思想是利用不等概率随机数生成器的输出,构造一个等概率的结果。
方法: Von Neumann 技巧
假设原始随机数生成器 rand_unequal() 生成 0 的概率为 ( p ),生成 1 的概率为 ( 1-p )。我们可以通过以下步骤构造等概率随机数:
- 生成两个独立的随机数 :
- 调用
rand_unequal()两次,得到 ( a ) 和 ( b )(每个都是 0 或 1)。
- 调用
- 分类讨论 :
- 如果 ( a = 0 ) 且 ( b = 1 ),输出 0(概率为 ( p(1-p) ))。
- 如果 ( a = 1 ) 且 ( b = 0 ),输出 1(概率为 ( (1-p)p ))。
- 如果 ( a = b )(即 00 或 11),丢弃这两个数,重新生成。
关键点:
- 输出 0 和 1 的概率均为 ( p(1-p) ),因此是等概率的。
- 丢弃 ( a = b ) 的情况是为了保证独立性。
代码实现(Python)
假设 rand_unequal() 是一个函数,生成 0 的概率为 ( p ),生成 1 的概率为 ( 1-p ):
python
import random
# 原始不等概率随机数生成器(示例:P(0)=0.6, P(1)=0.4)
def rand_unequal():
return 0 if random.random() < 0.6 else 1
# 构造等概率随机数生成器
def rand_equal():
while True:
a = rand_unequal()
b = rand_unequal()
if a == 0 and b == 1:
return 0
elif a == 1 and b == 0:
return 1
# 其他情况(a == b)丢弃,继续循环
# 测试
results = [rand_equal() for _ in range(10000)]
print("P(0):", results.count(0) / 10000) # 应接近 0.5
print("P(1):", results.count(1) / 10000) # 应接近 0.5
输出示例:
css
P(0): 0.4987
P(1): 0.5013
数学证明
- 输出 0 的概率:( P(a=0, b=1) = p \cdot (1-p) )。
- 输出 1 的概率:( P(a=1, b=0) = (1-p) \cdot p )。
- 两者相等,且 ( P(a=b) = p^2 + (1-p)^2 ) 的部分被丢弃,不影响最终结果的等概率性。
优化与扩展
-
效率问题:
- 丢弃 ( a = b ) 的情况会导致多次调用
rand_unequal(),效率取决于 ( p ) 的值。 - 当 ( p ) 接近 0 或 1 时,( P(a=b) ) 较大,效率较低。
- 丢弃 ( a = b ) 的情况会导致多次调用
-
扩展到多比特:
- 类似方法可以扩展到生成更长的等概率随机比特串(如 000 和 111 丢弃,001→0,010→1,等等)。
-
通用方法:
- 对于任意离散分布,可以通过拒绝采样(Rejection Sampling)构造目标分布。
其他方法:使用对数
如果知道 ( p ) 的具体值,也可以通过数学变换构造等概率随机数(但通常需要浮点运算,不如 Von Neumann 技巧通用):
python
import math
def rand_equal_alt():
# 假设 p 已知,例如 p = 0.6
p = 0.6
while True:
x = rand_unequal() # 生成 0 或 1
# 通过指数分布变换(仅当 p 已知时适用)
if random.random() < (0.5 if x == 0 else 0.5 / (1 - p)):
return x
但这种方法需要已知 ( p ) 且实现复杂,通常推荐 Von Neumann 技巧。
总结
- Von Neumann 技巧是通用方法,无需知道 ( p ) 的具体值,适用于任意 ( p \neq 0.5 )。
- 通过丢弃 ( a = b ) 的情况,保证输出 0 和 1 的等概率性。
- 实际应用中需权衡效率(多次调用原始随机数生成器)与实现简单性。