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 <= 105
进阶:
rand7()
调用次数的 期望值 是多少 ?- 你能否尽量少调用
rand7()
?
题解分析
解法一
现在要从 rand7() 到 rand10(),也要求是等概率的,那只要我们把小的数映射到一个大的数就好办了,那首先想到的办法是乘个两倍试一试,每个 rand7() 它能生成数的范围是 1~7,rand 两次,那么数的范围就变为 2~14,哦,你可能发现没有 1 了,想要再减去个 1 来弥补,rand7() + rand7()− 1,其实这样是错误的做法,因为对于数字 5 这种,你有两种组合方式 (2+3 or 3+2),而对于 14,你只有一种组合方式(7+7),它并不是等概率的,那么简单的加减法不能使用,因为它会使得概率不一致,我们的方法是利用乘法,一般思路如下面这样构建:
-
首先 rand7()-1 得到的数的集合为
-
再乘 7 后得到的集合 A 为
-
后面 rand7() 得到的集合B为
有人可能会疑惑,你咋不乘 66,乘 55 呢?因为它不是等概率生成,只有乘 77 才能使得结果是等概率生成的,啥意思?我们得到的集合 AA 和集合 BB,利用这两个集合,得到的数的范围是 1~491~49,每个数它显然是等概率出现的,因为这两个事件是独立事件,如果你不懂什么是独立事件,你试着加加看也能体会一点。
/**
* The rand7() API is already defined in the parent class SolBase.
* public int rand7();
* @return a random integer in the range 1 to 7
*/
class Solution extends SolBase {
public int rand10() {
int num = (rand7() - 1) * 7 + rand7();// 随机生成1-49的数字
while(num > 10){
num = (rand7() - 1) * 7 + rand7();
}
return num;
}
}
解法二:算法优化
这样的一个问题是,我们的函数会得到 1~49 之间的数,而我们只想得到 1~10 之间的数,这一部分占的比例太少了,简而言之,这样效率太低,太慢,可能要 while 循环很多次,那么解决思路就是舍弃一部分数,舍弃 41~49,因为是独立事件,我们生成的 1~40 之间的数它是等概率的,我们最后完全可以利用 1~40 之间的数来得到 1~10 之间的数。所以,我们的代码可以改成下面这样
/**
* The rand7() API is already defined in the parent class SolBase.
* public int rand7();
* @return a random integer in the range 1 to 7
*/
class Solution extends SolBase {
public int rand10() {
int num = (rand7() - 1) * 7 + rand7();// 随机生成1-49的数字
while(num > 40){
num = (rand7() - 1) * 7 + rand7();
}
return 1 + num % 10;
}
}