先上题代码。
int rand10()
{
while (true)
{
int x = (rand7() - 1) * 7 + (rand7() - 1);
if (x >= 0 && x <= 39)
return x % 10 + 1;
}
}
不均匀硬币,产生等概率的问题
现有一枚不均匀的硬币 coin(),能够返回 0、1 两个值,其概率分别为 0.6、0.4。要求使用这枚硬币,产生均匀的概率分布。即编写一个函数 coin_new() 使得它返回 0、1 的概率均为 0.5。
不难发现,抛硬币出现0 1和1 0的概率是相等的。所以当出现0 1情况的时候就返回true,出现1 0的情况的时候就返回false。否则就继续循环
int coin_new()
{
while (true)
{
int a = coin();
if (coin() != a)
return a;
}
}
均匀硬币,产生不等概率问题
现有一枚均匀的硬币 coin(),能够返回 0、1 两个值,其概率均为 0.5。要求编写一个函数 coin_new(),使得它返回指定的 0、1 概率分布。
P(0) = 1/4,P(1) = 3/4
对于均匀硬币而言,连续抛两次,得到 0 0、0 1、1 0、1 1 的概率均1/4。显然,只需要连续抛两次硬币,如果得到 0 0,返回0,其他情况返回 1。
int coin_new()
{
int a=coin();
int b=coin();
if(a==0&&b==0)
return 0;
return 1;
}
P(0) = 1/3,P(1) = 2/3
连续抛两次硬币。如果得到 1 1,返回 0;如果得到 1 0 或 0 1,返回 1;如果得到 0 0,继续抛硬币。
int coin_new()
{
while(true)
{
int a=coin()
int b=coin()
if(a&&b)
return 0;
if(a||b)
return 1;
}
}
## P(0) = 0.3,P(1) = 0.7
1.每抛一次硬币,会得到二进制数的一位
2.连续抛 4 次硬币,可以等概率生成 [0, 15] 的每个数,记为 x
3.去掉 [10, 15],剩下 [0, 9] 的每个数依然是等概率的
4.如果 x∈[0,2],返回 0;x∈[4,9],返回 1;x ≥ 10重复上述过程
int coin_new()
{
while (true)
{
int x = 0;
for (int i = 0; i < 4; i++) {
x = (x << 1) + coin();
}
if (x <= 2)
return 0;
if (x <= 9)
return 1;
}
}
Rand7 生成 Rand10
将 Rand7() 视作一个 7 进制位的生成器。由于 Rand7() 的结果范围是 [1, 7],故我们取 Rand7() - 1 作为对应的 7 进制位。每执行 k次 Rand7(),将得到一个 k位的 7 进制整数,在 [0, 7^k-1][范围内均匀分布。
int rand10()
{
while (true)
{
int x = (rand7() - 1) * 7 + (rand7() - 1);
if (x >= 0 && x <= 39)
return x % 10 + 1;
}
}
优化
这样我们依然有[40,48]的数字没有用上。我们继续使用rand7()生成7进制。则到x>=40时。(x%40)*7+rand7()可以生成[1,63]的等概率的随机数y。我们可以用生成的随机数y,当y位于[1,60]时%10,可以生成等概率的[1,10].
此时,没有使用的还有61,62,63。计算 (x%61) * 7 + Rand7(),相当Rand2() * 7 + Rand7(),可以得到 [1, 21] 范围的均匀随机整数,这时再作取余运算,只有 1 个无用数字(21):
int rand10()
{
while (true)
{
int x = (rand7() - 1) * 7 + (rand7() - 1);
if (x >= 1 && x <= 40)
return x % 10 + 1;
x = (x % 40) * 7 + rand7();
if (x <= 60)
return x % 10 + 1;
x = (x%61) * 7 + 7;
if (x <= 20)
return x % 10 + 1;
}
}