Cracking Coding Interview - 16.23 Rand7 From Rand5

Rand7 from Rand5: Implement a method rand7() given rand5(). That is, given a method that generates a random number between 0 and 4 (inclusive), write a method that generates a random number between 0 and 6 (inclusive).

Hints: #505, #574, #637, #668, #697, #720

解法

rand5()的范围是[0, 4],先看看那么两个rand5()相加是否能够等价rand10()[0, 9]

结果 组合 概率
0 0 + 0 0.04( 0.2 * 0.2)
1 1 + 0, 0 + 1 2 * 0.04 = 0.08
2 1 + 1, 0 + 2, 2 + 0 3 * 0.04 = 0.12
3 1 + 2, 2 + 1, 0 + 3, 3 + 0 4 * 0.04 = 0.16
4 1 + 3, 3 + 1, 0 + 4, 4 + 0, 2 + 2 5 * 0.04 = 0.20
5 1 + 4, 4 + 1, 2 + 3, 3 + 2 4 * 0.04 = 0.16
6 3 + 3, 2 + 4, 4 + 2 3 * 0.04 = 0.12
7 3 + 4, 4 + 3 2 * 0.04 = 0.08
8 4 + 4 0.2 * 0.2 = 0.04

可以每个结果的概率并不相同,所以不能成立。其实同理,相乘也不能成立。

如果对结果取模,那么分布就变成了:

结果 组合 概率
0,7 (7 % 7 = 0) 0 + 0, 3 + 4, 4 + 3 3 * 0.04 = 0.12
1,8 (8 % 7 = 1) 1 + 0, 0 + 1, 4 + 4 3 * 0.04 = 0.12
2 1 + 1, 0 + 2, 2 + 0 3 * 0.04 = 0.12
3 1 + 2, 2 + 1, 0 + 3, 3 + 0 4 * 0.04 = 0.16
4 1 + 3, 3 + 1, 0 + 4, 4 + 0, 2 + 2 5 * 0.04 = 0.20
5 1 + 4, 4 + 1, 2 + 3, 3 + 2 4 * 0.04 = 0.16
6 3 + 3, 2 + 4, 4 + 2 3 * 0.04 = 0.12

回发现概率分布还是不均匀。

得想一个办法将各种结果出现的概率弄成一致,如果这样 5 * rand5() + rand5()

因子1 因子2 结果
0 0 0
0 5 5
0 10 10
0 15 15
0 20 20
1 0 1
1 5 6
1 10 11
1 15 16
1 20 21
2 0 2
2 5 7
2 10 12
2 15 17
2 20 22
3 0 3
3 5 8
3 10 13
3 15 18
3 20 23
4 0 4
4 5 9
4 10 14
4 15 19
4 20 24

可以发现,结果出现的次数都是一次,也就是概率相同。那么,我们可以抛弃掉[21, 24]这4个结果,在[0, 20]之间 % 7,代码:

1
2
3
4
5
6
7
8
public int rand7() {
  while (true) {
    int num = rand5() * 5 + rand5();
    if (num < 21) {
      return num % 7;
    }
  }
}

为什么这样做概率是均匀的?我们可以把代码做一个转换,因为已知rand5(5) + rand(5)能够得到一个均匀分布的[0, 24]的区间,那么就等价于rand25(),然后我们把 % 7 这个操作去掉,把代码变成这样:

1
2
3
4
5
6
7
8
public int rand21() {
  while (true) {
    int num = rand(25);
    if (num < 21) {
      return num;
    }
  }
}

假设每次调用rand25()的返回结果是0, 1, 2, 3, ..., 24, 0, 1, 2, ..., 24,那么调用rand21()的记录就是:

rand21()结果 rand25()调用结果
0 0
1 1
2 2
20 20
0 21, 22, 23, 24, 0,结果取0
1 1
2 2

可以看到rand21()的结果的分布都是均匀的,所以解法提到的方法是可行的。

版权

评论