题目
470. 用 Rand7() 实现 Rand10()
已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。
不要使用系统的 Math.random() 方法。
示例 1:
输入: 1
输出: [7]
示例 2:
输入: 2
输出: [8,4]
示例 3:
输入: 3
输出: [8,1,10]
解法
思路
这一题重点是找到规律
(randX() - 1) × Y + randY() ==> 可以等概率的生成[1, X * Y]范围的随机数
这个大家可以自己画图看一下,
如(rand2()-1) x 2 + rand2() 可以得到
| 1 | 2 | |
|---|---|---|
| 0 | 1 | 2 |
| 2 | 3 | 4 |
(rand3()-1) x 3 + rand3()可以得到
| 1 | 2 | 3 | |
|---|---|---|---|
| 0 | 1 | 2 | 3 |
| 3 | 4 | 5 | 6 |
| 6 | 7 | 8 | 9 |
那么这一题给出了rand7,根据上述总结 (rand7()-1) x 7 + rand7() ===> 可以等概率的生成[1, 49]范围的随机数
而我们要取[1,10]的随机数,而且尽可能的取样更多,那么我们只能使用[1,40]前40个数,我们就得拒绝采样最后9个数。
当然,取前30,前20,甚至前10个数都是ok的,但采样越多,我们取数能成功的概率越大,调用rand7的次数就可以越少。
这里区间末尾一定要是10的倍数,所以我们取[1,40]
那么我们就可以得到 let num = ((rand7()-1) x 7 + rand7())
当num<=40的时候,num % 10 + 1,就是[1,10]的等概率随机整数
代码如下
/**
* The rand7() API is already defined for you.
* var rand7 = function() {}
* @return {number} a random integer in the range 1 to 7
*/
var rand10 = function() {
while(true) {
let num = (rand7() - 1) * 7 + rand7(); // 等概率生成[1,49]范围的随机数
if(num <= 40) return num % 10 + 1; // 拒绝采样,并返回[1,10]范围的随机数
}
};
我们这里拒绝采样最后9个数,那么如果可以将最后9个数利用起来的话,就可以减少rand7调用的次数
优化代码如下:
/**
* The rand7() API is already defined for you.
* var rand7 = function() {}
* @return {number} a random integer in the range 1 to 7
*/
var rand10 = function() {
while(true) {
let a = rand7();
let b = rand7();
let 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;
}
};