文章目录
LC470 用Rand7() 实现 Rand10()
0.高频考题!
面试高频好题! 考察数学中概率论内容的灵活应用~
另外优化部分考察 “考虑问题的多面性“
刷题不努力 面试徒伤悲!
1.读题
2.解题思路
其实这题是个数学题emmm…
参考题解 详细思路及优化思路分析,逐行解释
从最基础的讲起如何做到均匀的生成随机数
这题想做出来
第一步需要想到 这是一个跟概率挂上钩的题!
跟着大佬的思维 来看看这题怎么解吧~
我们要求从rand7()—rand10()是等概率的!
【1】先让小的范围可以映射大的范围——先试试把rand7()*2 -1 变为 1-14?
【2】但是这些数等概率么???比如说5 有 2+3 3+2 两种组合方式
但是14 只有 7+7这一种组合方式【3】
(rand7() - 1) * 7 + rand7()
生成等概率的结果 得到数的范围 [1,49]
具体为啥是用这个公式?
来个小结论
先注意 rand7() - 1 的范围是 [0,6]
【1】
(rand2()-1) × 2 + rand2()
----- [1,4]
【2】(rand7() - 1) * 7 + rand7()
------[1,49]
有点感觉不?再来个
【3】(rand9()-1) × 7 + rand7() = result
------[1,63]
得到结论已知
rand_n()
可以生成 [1,n] 范围的随机数
那么(rand_X() - 1) × Y + rand_Y()
=> 可以等概率的生成[1, X * Y]范围的随机数
下面再来补充一个反向的小结论
用rand4()来实现rand2()
rand4() % 2 + 1 = rand2()
但是如果rand_n()的n不是偶数 就不可以用这个与2取余 + 1
的方法 (产生的结果不是等概率的~)
3.代码逻辑
大佬的题解给出了逐步优化的一个思路
咱们来一步步记录下!
3.1 v1.0
【1】生成[1,49]的随机数
具体为啥是这个公式
可以死记硬背
但是建议看一看上面的推导 跟着推一遍!
几分钟的事儿!就永远记住了!!
【2】开始暴力while循环 只要这个数大于10 我就给它重新赋个值~
【3】返回结果
3.2 v2.0
【1】生成[1,49]的随机数
【2】遇到 [1,40] 的随机数 返回 num % 10 + 1
【3】舍弃了9个数 但是不用疯狂循环了
时间复杂度大大降低 爽~
3.3 v3.0
v2.0版本舍弃了9个数 这个版本一个数都不舍弃 保证一遍过~
时间复杂度再次降低
【1】生成[1,49]的随机数
【2】第一种情况 随机数在[1,40]的范围内 直接返回 num % 10 + 1
【3】第二种情况 让[41,49]的num值重新扩充起来!
num = (num - 40 - 1) * 7 + rand7();
这一步就相当于 (rand9() - 1) * 7 + rand7()
根据我们之前得到的公式 随机数范围变为了[1,63]
之后 在 [1, 60] 内的随机数 直接返回 num % 10 + 1
【4】第三种情况 (也正好是最后一种情况了) 让 [61,63] 的num值重新扩充起来!
num = (num - 60 - 1) * 7 + rand7();
变为了 [1,21]
之后 丢弃21这个数据 在[1, 20]这个范围中 返回 num % 10 + 1
4.Java代码
别看有三个版本 其实每个版本只需要稍微改动一丢丢的思维就行~
球球了看下去吧!!
4.1 v1.0
暴力循环 时间复杂度拉胯
足足有21ms
class Solution extends SolBase {
public int rand10() {
int num = (rand7() - 1) * 7 + rand7();
while(num > 10) {
num = (rand7() -1) * 7 +rand7();
}
return num;//直接暴力循环 时间复杂度较高
}
}
4.2 v2.0
丢弃9个数但是不会进行暴力循环的第二版本
时间复杂度大大减少
class Solution extends SolBase {
public int rand10() {
int num = (rand7() - 1) * 7 + rand7();
while(num > 40){
//如果得到的随机数大于40 就重新搞出来一个随机数
//为了保证等概率嘛~
num = (rand7() - 1) * 7 + rand7();
}
return num % 10 + 1;
}
}
4.3 v3.0的最终版本
只丢弃一个数!
时间复杂度进一步减小
class Solution extends SolBase {
//最优化解法
public int rand10() {
while (true) {
int num = (rand7() - 1) * 7 + rand7();
//01 [1,40]的范围 皆大欢喜 直接返回结果
while(num <= 40) {
return num % 10 + 1;
}
//02 如果超过这个范围呢 即为[41,49]
//因为我们需要[(n)1,(n+1)0]这样的范围~
//所以继续调整我们的num
num = (num - 40 - 1) * 7 + rand7();
if(num <= 60) {
return num % 10 + 1;
}
//03 因为上面的num经过调整 范围变为 [1,63]
//且上面的判断语句 剔除了60以下的
//之后[61,63]的也不能丢弃!为了提升时间复杂度~
num = (num - 60 - 1) * 7 + rand7();
if(num <= 20) {
return num % 10 + 1;
}
}
}
}