题目描述:470. 用 Rand7() 实现 Rand10()

因为是第一次接触到这样的题目,毫无思绪,对官方题解也是“不知道为什么要这么做”。看过一些题解之后才逐渐明白,现在让我自己来写题解,我打算先从简单的开始讲起。

Part 1

假设已知rand2()可以均匀的生成[1,2]的随机数,现在想均匀的生成[1,4]的随机数,该如何考虑?

我想如果你也像我一样第一次接触这个问题,那么很可能会这么考虑——令两个rand2()相加,再做一些必要的边角处理。如下:

rand2() + rand2() = ? ==> [2,4]1    +   1     = 21    +   2     = 32    +   1     = 32    +   2     = 4

// 为了把生成随机数的范围规约成[1,n],于是在上一步的结果后减1

(rand2()-1) + rand2() = ? ==> [1,3]0       +   1     = 10       +   2     = 21       +   1     = 21       +   2     = 3

可以看到,使用这种方法处理的结果,最致命的点在于——其生成的结果不是等概率的。在这个简单的例子中,产生2的概率是50%,而产生1和3的概率则分别是25%。原因当然也很好理解,由于某些值会有多种组合,因此仅靠简单的相加处理会导致结果不是等概率的。

因此,我们需要考虑其他的方法了。

仔细观察上面的例子,我们尝试对 (rand2()-1) 这部分乘以 2,改动后如下:

(rand2()-1) × 2 + rand2() = ? ==> [1,3]0            +   1     = 10            +   2     = 22            +   1     = 32            +   2     = 4

神奇的事情发生了,奇怪的知识增加了。通过这样的处理,得到的结果恰是[1,4]的范围,并且每个数都是等概率取到的。因此,使用这种方法,可以通过rand2()实现rand4()。

也许这么处理只是我运气好,而不具有普适性?那就多来尝试几个例子。比如:

(rand9()-1) × 7 + rand7() = resulta               b

为了表示方便,现将rand9()-1表示为a,将rand7()表示为b。计算过程表示成二维矩阵,如下:

可以看到,这个例子可以等概率的生成[1,63]范围的随机数。再提炼一下,可以得到这样一个规律:
已知 rand_N() 可以等概率的生成[1, N]范围的随机数
那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范围的随机数
即实现了 rand_XY()

part2

那么想到通过rand4()来实现rand2()呢?这个就很简单了,已知rand4()会均匀产生[1,4]的随机数,通过取余,再加1就可以了。如下所示,结果也是等概率的。

rand4() % 2 + 1 = ?1 % 2    + 1 = 22 % 2    + 1 = 13 % 2    + 1 = 24 % 2    + 1 = 1

事实上,只要rand_N()中N是2的倍数,就都可以用来实现rand2(),反之,若N不是2的倍数,则产生的结果不是等概率的。比如:

rand6() % 2 + 1 = ?1 % 2    + 1 = 22 % 2    + 1 = 13 % 2    + 1 = 24 % 2    + 1 = 15 % 2    + 1 = 26 % 2    + 1 = 1
rand5() % 2 + 1 = ?1 % 2    + 1 = 22 % 2    + 1 = 13 % 2    + 1 = 24 % 2    + 1 = 15 % 2    + 1 = 2

Part 3

ok,现在回到本题中。已知rand7(),要求通过rand7()来实现rand10()。

有了前面的分析,要实现rand10(),就需要先实现rand_N(),并且保证N大于10且是10的倍数。这样再通过rand_N() % 10 + 1 就可以得到[1,10]范围的随机数了。

而实现rand_N(),我们可以通过part 1中所讲的方法对rand7()进行改造,如下:

(rand7()-1) × 7 + rand7()  ==> rand49()
但是这样实现的N不是10的倍数啊!这该怎么处理?这里就涉及到了“拒绝采样”的知识了,也就是说,如果某个采样结果不在要求的范围内,则丢弃它。基于上面的这些分析,再回头看下面的代码,想必是不难理解了。

class Solution extends SolBase {public int rand10() {while(true) {int num = (rand7() - 1) * 7 + rand7(); // 等概率生成[1,49]范围的随机数if(num <= 40) return num % 10 + 1; // 拒绝采样,并返回[1,10]范围的随机数}}
}

Part 4: 优化

这部分具体的代码是参考官方题解的,不过是我自己在理解了part 1和part 2之后才看懂的,一开始看真不知道为什么(/(ㄒoㄒ)/~~...

根据part 1的分析,我们已经知道(rand7() - 1) * 7 + rand7() 等概率生成[1,49]范围的随机数。而由于我们需要的是10的倍数,因此,不得不舍弃掉[41, 49]这9个数。优化的点就始于——我们能否利用这些范围外的数字,以减少丢弃的值,提高命中率总而提高随机数生成效率。

class Solution extends SolBase {public int rand10() {while(true) {int a = rand7();int b = rand7();int num = (a-1)*7 + b; // rand 49if(num <= 40) return num % 10 + 1; // 拒绝采样a = num - 40; // rand 9b = rand7();num = (a-1)*7 + b; // rand 63if(num <= 60) return num % 10 + 1;a = num - 60; // rand 3b = rand7();num = (a-1)*7 + b; // rand 21if(num <= 20) return num % 10 + 1;}}
}

作者:kkbill
链接:https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/cong-zui-ji-chu-de-jiang-qi-ru-he-zuo-dao-jun-yun-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

从最基础的讲起如何做到均匀的生成随机数相关推荐

  1. 《算法零基础100讲》(第30讲) 概率与统计

    文章目录 零.写在前面 一.概念定义 二.题目描述 三.算法详解 四.源码剖析 五.推荐专栏 六.习题练习 零.写在前面   这是<算法零基础100讲> 专栏打卡学习的第 30 天了.如果 ...

  2. Java基础第一讲:Java的故事和Java编程环境搭建

    { Android学习指南 } 适于自学的ANDORID学习指南,基于ANDROID 2.2.2.3.3及3.0版本讲解 <ANDROID学习指南>目录 RSS Java基础第一讲:Jav ...

  3. matlab 祁彬彬,MATLAB 向量化编程基础精讲

    <MATLAB 向量化编程基础精讲>使用MATLAB新版本2016a,拣选Mathworks官方群组Cody中一些有趣的代码问题,分6章讲解这些优秀示例代码中使用数组.字符串操作.正则表达 ...

  4. 《算法零基础100讲》(第42讲) 位运算 (位与) 入门

    文章目录 零.写在前面 一.概念定义 1.位与的定义 2.位与运算符的简单应用 1)奇偶性判定 2)取末五位 3)消除末尾五位 4)2的幂判定 二.题目描述 三.算法详解 四.源码剖析 五.推荐专栏 ...

  5. 【习题】《算法零基础100讲》位与 2

    前言 原文链接:<算法零基础100讲>(第43讲) 位运算 (位与) 进阶 习题 难度 习题 中等 397. 整数替换 中等 1404. 将二进制表示减到 1 的步骤数 中等 201. 数 ...

  6. 计算机网络冲刺串讲,计算机应用基础串讲冲刺讲义(二)

    计算机应用基础串讲冲刺讲义(二) 分类:自考 | 更新时间:2016-07-07| 来源:中华网教育 9.下面列出的四项中,不属于计算机病毒特征的是 A.潜伏性 B.激发性 C.传播性 D.免疫性 1 ...

  7. 计算机基础算术加法,计算机基础第二讲.ppt

    计算机基础第二讲 计算机的运算 算术运算:加.减.乘.除 逻辑运算:与.或.非 数据比较:大于.小于.等于.不等于.大于等于.小于等于 数据传送:输入.输出.赋值 二进制的算术运算 加法 0+0=0 ...

  8. 《算法零基础100讲》(第20讲) 进制转换(二) - 进阶

    文章目录 零.写在前面 一.概念定义 二.题目描述 三.算法详解 四.源码剖析 五.推荐专栏 六.习题练习 零.写在前面   这是<算法零基础100讲> 专栏打卡学习的第 20 天了.如果 ...

  9. 【题解】《算法零基础100讲》(第44讲) 位运算 (位或) 入门

    文章目录 一. 概念定义 1.1 位或定义 1.2 位与定义 二. 推荐专栏 三. 相关练习 3.1 根据数字二进制下 1 的数目排序 3.2 二进制表示中质数个计算置位 3.3 2 的幂 一. 概念 ...

最新文章

  1. Linux基本命令——vi文本编辑器
  2. php微信公众号开发入门
  3. 参考文献中的字母含义
  4. 协同OA对业务和管理进行流程的梳理
  5. oracle 之 COMMENT
  6. python删除重复值所在的行数_python – 在last中删除具有重复值的行
  7. cf1556D. Take a Guess
  8. 【Python】shutil内置模块复制和重命名文件
  9. MySQL笔记-MDL锁(metadata lock)
  10. caffe新手常遇到的三个问题
  11. 【LuoguP5004】 专心OI - 跳房子
  12. linux 进程的 5 大段
  13. maven+springMvc+velocity
  14. MJRefresh自定义刷新动画
  15. win7+vs2015+pcl1.8.0配置
  16. 软件测试流程图及描述
  17. MyEclipse使用阿里p3c代码规范
  18. 移动政务中的小程序技术
  19. 为什么要用Citrix桌面虚拟化?
  20. python---爬虫

热门文章

  1. 设定自动获得DNS服务器地址
  2. SAP NetWeaver平台介绍
  3. mysql的优化总结
  4. POJ 2187 凸包+旋转卡壳
  5. 五大经典算法之动态规划
  6. eclipse导入远程库的git项目
  7. 由作用域安全的构造函数想到的
  8. 使用while循环输入 1 2 3 4 5 6 8 9 10
  9. object-c 随机数总结
  10. The Linux device model