题目
已有方法 rand7
可生成 1
到 7
范围内的均匀随机整数,试写一个方法 rand10
生成 1
到 10
范围内的均匀随机整数。
不要使用系统的 Math.random()
方法。
示例 1:
输入: 1 输出: [7]
示例 2:
输入: 2 输出: [8,4]
示例 3:
输入: 3 输出: [8,1,10]
提示:
rand7 已定义。
传入参数: n 表示 rand10 的调用次数。
解题思路
要从rand7()
到 rand10()
,只要我们把小的数映射到一个大的数就好办了,那首先想到的办法是乘个两倍试一试,每个 rand7()
它能生成数的范围是 1~7
,rand两次,那么数的范围就变为 2~14
,你可能发现没有 11 了,想要再减去个 11 来弥补,rand7() + rand7() − 1
,其实这样是错误的做法,因为对于数字 55 这种,你有两种组合方式 (2+3 or 3+2)
,而对于 14,你只有一种组合方式(7+7)
,它并不是等概率的,那么简单的加减法不能使用,因为它会使得概率不一致,我们的方法是利用乘法。
我们的函数会得到 1~49 之间的数,而我们只想得到 1~10 之间的数,且等概率,这样子需要舍弃掉41~49之间的数。
public int rand10() {
// 首先得到一个数
int num = (rand7() - 1) * 7 + rand7();
// 只要它还大于40,那你就给我不断生成吧
while (num > 40)
num = (rand7() - 1) * 7 + rand7();
// 返回结果,+1是为了解决 40%10为0的情况
return 1 + num % 10;
}
这时候我们舍弃了 99 个数,舍弃的还是有点多,效率还是不高,怎么提高效率呢?那就是舍弃的数最好再少一点!因为这样能让 while 循环少转几次,那么对于大于 40 的随机数,别舍弃呀,利用这 9 个数,再利用那个公式操作一下:
(大于40的随机数 - 40 - 1) * 7 + rand7()
这样我们可以得到 1-63
之间的随机数,只要舍弃 3 个即可,那对于这 3 个舍弃的,还可以再来一轮:
(大于60的随机数 - 60 - 1) * 7 + rand7()
这样我们可以得到 1-21 之间的随机数,只要舍弃 1 个即可。
代码如下
代码
/**
* 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() {
while (true){
int num = (rand7() - 1) * 7 + rand7();
// 如果在40以内,那就直接返回
if(num <= 40) return 1 + num % 10;
// 说明刚才生成的在41-49之间,利用随机数再操作一遍
num = (num - 40 - 1) * 7 + rand7();
if(num <= 60) return 1 + num % 10;
// 说明刚才生成的在61-63之间,利用随机数再操作一遍
num = (num - 60 - 1) * 7 + rand7();
if(num <= 20) return 1 + num % 10;
}
}
}