Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
leetcode-470 用 Rand7() 实现 Rand10()
题目介绍
给定方法 rand7 可生成 [1,7] 范围内的均匀随机整数,试写一个方法 rand10 生成 [1,10] 范围内的均匀随机整数。
你只能调用 rand7() 且不能调用其他方法。请不要使用系统的 Math.random() 方法。
每个测试用例将有一个内部参数 n,即你实现的函数 rand10() 在测试时将被调用的次数。请注意,这不是传递给 rand10() 的参数。
示例1
输入: 1
输出: [2]
示例2
输入: 2
输出: [2,8]
示例3
输入: 3
输出: [3,8,10]
提示:
-
1 <= n <= 10^5进阶: -
rand7()调用次数的 期望值 是多少 ? -
你能否尽量少调用
rand7()?
解题思路一
首先思考一个问题,rand10 指的是等概率的生成 [1, 10] 中的整数,也就是说,生成每个整数的概率都是 1/10。如果用 rand10 实现 rand7 的话,应该怎么实现?
可以直接用 rand10 生成一个随机数,如果生成的数属于 [1, 7] 就可以了,上面已经说过了,生成每个数的概率都是 1/10,因此符合题目所给的 rand7 的性质。如果生成的数属于 [8, 10],可以重新生成随机数
那么为什么 rand7 不能直接生成 rand10?因为数不够,因此我们的思考方向就是想办法利用 rand7 等概率的生成超过 10 个数
rand7() + rand7() ?
rand7() + rand7() 确实可以生成 13 个数 [2, 14],但是并不是等概率的,看一下下面的表格
| + | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
rand7() * 7 ?
rand7() * 7 会得到 {1, 14, 21, 28, 35, 42, 49},只能等概率生成这几个数中的一个,中间的空白怎么补上?可以用加法,但是 1 加上任意不为 0 的数都将大于 1,因此考虑 (rand7() - 1) * 7
(rand7() - 1) * 7 ?
rand7() - 1 会等概率生成 {0, 1, 2, 3, 4, 5, 6}
(rand7() - 1) * 7 会等概率生成集合A {0, 7, 14, 21, 28, 35, 42},怎么填补空白,加上 rand7
(rand7() - 1) * 7 + rand7() ?
rand7 等概率生成集合B {1, 2, 3, 4, 5, 6, 7}
因为 (rand7() - 1) * 7 和 rand7() 是两个独立事件,根据概率论 P(AB) = P(A) * P(B) = 1/7 * 1/7 = 1/49,因此 (rand7() - 1) * 7 + rand7() 能够等概率生成 [1, 49]
因此可以直接使用 [1, 40] 这 40 个数对 10 取余再加 1,就可以等概率的得到 [1, 10] 中的随机数,如果是 [41, 49] 则重新获取
解题代码一
var rand10 = function() {
let num = (rand7() - 1) * 7 + rand7()
while (num > 40){
num = (rand7() - 1) * 7 + rand7()
}
return 1 + num % 10
};
优化
在上面我们将 [41, 49] 直接舍去了,其实 [41, 49] 也是等概率出现的,完全可以看成是 rand9,那么问题就变成了 用 Rand9() 实现 Rand10(),思路和 rand7 一样
var rand10 = function() {
while(true) {
let num = (rand7() - 1) * 7 + rand7()
if (num <= 40) return 1 + num % 10
// 如果是 [41, 49],再次生成 [1, 63]
num = (num - 40 - 1) * 7 + rand7()
if (num <= 60) return 1 + num % 10
}
};
上述优化之后舍弃了 [61, 63],同理 用 Rand3() 实现 Rand10()
var rand10 = function() {
while(true) {
let num = (rand7()-1) * 7 + rand7()
if (num <= 40) return 1 + num % 10
// 如果是 [41, 49],再次生成 [1, 63]
num = (num - 40 - 1) * 7 + rand7()
if (num <= 60) return 1 + num % 10
// 如果是 [61, 63],再次生成 [1, 21]
num = (num - 60 - 1) * 7 + rand7()
if (num <= 20) return 1 + num % 10
}
};
解题思路二
思路一已经说明了用 Rand10() 实现 Rand7() 很简单,那么用 Rand7() 实现 Rand5() 和 Rand7() 实现 Rand2() 也很简单
rand5 会等概率生成 {1, 2, 3, 4, 5},rand2 会等概率生成 {1, 2},如果 rand2 生成 2 时,rand5() + 5,rand2 生成 1 时,rand5 不变,那么就等概率得到 [1, 10],实现了 rand10
解题代码二
var rand10 = function() {
let p = rand7(), q = rand7()
// p 实现 rand5
while (p > 5) p = rand7()
while (q === 7) q = rand7()
// q 大于 3 的概率是 1/2,可以看成 rand2
return q > 3 ? p : p + 5
};