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]
思路:
我们先考虑,使用rand2()可以生成[1,2]的随机数,那么如何得到[1,4]呢?
是不是一下想到用rand2()+rand2()呢?
rand2() + rand2() = ? ==> [2,4]
1 + 1 = 2
1 + 2 = 3
2 + 1 = 3
2 + 2 = 4
为了把生成随机数的范围缩小成[1,n],于是在上一步的结果后减1
(rand2()-1) + rand2() = ? ==> [1,3]
0 + 1 = 1
0 + 2 = 2
1 + 1 = 2
1 + 2 = 3
可以看到,这样生成的结果虽然是[1,3]但是概率却不相等,所以是不能用的
那如果我们把(rand()2-1)×2呢?(这里怎么想到的我TM也不知道)我们发现
(rand2()-1) × 2 + rand2() = ? ==> [1,4]
0 + 1 = 1
0 + 2 = 2
2 + 1 = 3
2 + 2 = 4
概率竟然完全相等,我们已经通过rand2()实现了rand4()
于是我们得出一个数学规律:
已知 rand_N() 可以等概率的生成[1, N]范围的随机数
那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范围的随机数
即实现了 rand_XY()
我们再想一个问题:如何反过来通过rand4()实现rand2()呢?我们可以通过取余实现
rand4() % 2 + 1 = ?
1 % 2 + 1 = 2
2 % 2 + 1 = 1
3 % 2 + 1 = 2
4 % 2 + 1 = 1
事实上,只要N是2的倍数,我们都可以通过randN()来实现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
有了上面的规律方法,我们可以分析本题了,需要我们通过rand7()实现rand10()
那么我们可以先实现randN()–N为10的倍数,然后取余
我们通过上面规律进行处理:
(rand7()-1) × 7 + rand7() ==> rand49()
但是49不是10的倍数啊!我们可以利用"拒绝采样"(不想要哪个采样结果就拒绝它)
把rand49()产生的41~49全部拒绝掉再取余就好了…
代码:
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution
{
public:
int rand10()
{
while (true)
{
int num = (rand7() - 1) * 7 + rand7(); // 得到[1,49]
if (num <= 40) // 拒绝采样,得到[1,40]
return num % 10 + 1; // 取余得到[1,10]
}
}
};
优化
我们在上面的方法中,"拒绝采样"了41-49,那么我们能不能合理利用这九个数,从而提高生成随机数的效率,我们可以利用41-49减去40得到rand9()进一步得到rand10(),这时候我们发现再次处理后又"拒绝"61-63三个随机数,我们再利用其减去60得到rand3()进一步得到rand10(),这时最后只"拒绝"了21一个数,我们无需再处理,至此结束,提高了随机数的命中率
// The rand7() API is already defined for you.
// int rand7();
// @return a random integer in the range 1 to 7
class Solution
{
public:
int rand10()
{
while (true)
{
int a = rand7();
int b = rand7();
int num = (a - 1) * 7 + b; // rand49()
if (num <= 40) return num % 10 + 1; //拒绝采样得到rand10()
//下面的代码处理上面得到的无用的随机数[41,49]
a = num - 40; // rand49()-rand40()=rand9()
b = rand7();
num = (a - 1) * 7 + b; // rand63
if (num <= 60) return num % 10 + 1; //拒绝采样得到rand10()
//下面的代码处理上面得到的无用的随机数[61,63]
a = num - 60; // rand3
b = rand7();
num = (a - 1) * 7 + b; // rand21
if (num <= 20) return num % 10 + 1; //拒绝采样得到rand10()
//再往下就无法再物尽其用了,因为只剩了一个21了
}
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix