前言
LeetCode死磕系列十二: 数学基础
说实话,其实都是刷题都是数学的实现,不过感觉有些题目更加偏向数学应用,譬如蓄水池,等概率抽样…。
题目
470 用rand7()实现Rand10()
从上述的文章可以得出:
已知: $randN()$可以==等概率==生成$[1, N]$范围的数字
那么: $(randN()-1) \times Y + randY()$可以==等概率==生成$[1, Y \times N]$ 范围的数字即:实现了$randNY()$
以$[(rand9() - 1) \times 7 + rand7()]$为例子
rand9()-1\rand7 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
3 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
4 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
5 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
6 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
7 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
8 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
是不是很神奇,数学证明不会
那么想到通过rand4()来实现rand2()呢?这个就很简单了,已知rand4()会均匀产生[1,4]的随机数,通过取余,再加1就可以了。如下所示,结果也是等概率的。
rand4() % 2 + 1 = ?
1 % 2 + 1 = 2
2 % 2 + 1 = 1
3 % 2 + 1 = 2
4 % 2 + 1 = 1
事实上,只要rand_N()中N是2的倍数,就都可以用来实现rand2(),反之,若N不是2的倍数,则产生的结果不是等概率的。比如:
rand6() % 2 + 1 = ?
1 % 2 + 1 = 2
2 % 2 + 1 = 1
3 % 2 + 1 = 2
4 % 2 + 1 = 1
5 % 2 + 1 = 2
6 % 2 + 1 = 1
rand5() % 2 + 1 = ?
1 % 2 + 1 = 2
2 % 2 + 1 = 1
3 % 2 + 1 = 2
4 % 2 + 1 = 1
5 % 2 + 1 = 2
通用一些的
如果需要实现$randX()$
- 先实现$randN()$
- $N$为$X$的倍数($N>X$)
- 再通过 $randN() \% X + 1$即可
public int rand10() {
// rand 49
while (true) {
int num = (rand7() - 1) * 7 + rand7(); // rand49
if (num <= 40) { // 使用拒绝采样,得到rand40
return num % 10 + 1; // rand10
}
}
}
优化
class Solution extends SolBase {
// 上述拒绝了9个数字,
// 优化之后,减少丢弃值,提高命中率
public int rand10() {
while(true) {
int a = rand7();
int b = rand7();
int num = (a-1)*7 + b; // rand 49
if(num <= 40) return num % 10 + 1; // 拒绝采样
a = num - 40; // rand 9
b = rand7();
num = (a-1)*7 + b; // rand 63
if(num <= 60) return num % 10 + 1;
a = num - 60; // rand 3
b = rand7();
num = (a-1)*7 + b; // rand 21
if(num <= 20) return num % 10 + 1;
}
}
}