老黄、老银和老阳共事宇宙中心某互联网企业H,并且同在一个由校招新生组成的Team里。刚走出校园的他们,对自己去年在校招这场演技大比拼中的表现意犹未尽,因此常会拿一些没什么实际意义的技术问题大谈特谈。

这次是午饭后的扯皮时间,这几个人就吸着免费的明胶酸奶,从一些概率问题讨论到了随机数发生器上……

黄:像去年(校招),面试官就很喜欢问这种题目:给定一个[0, M)的均匀分布随机整数发生器(以下简称RNG),设计一个[0, N)的整数的均匀分布RNG。

阳:我被问的时候不知道怎么做,问我的人就得意的告诉我用——M进制。

黄:现实中RNG一般输出的是什么呢?

阳:我觉得最基本的应该是以bit或者byte为单位输出的。毕竟RNG就是一连串的位操作,比如像那个什么Twister算法(梅森旋转算法)这种,所以可见M必定是2的幂。

黄:如果是这样,我会先用RNG产生足够的随机位数存下N,然后截取小于N的值返回就行了。

阳:……我想如果从这个RNG出发,它只有类似于nextByte的方法返回一个随机byte,以此实现一个生成指定范围的int、long、float、double的RNG……

黄:int和long很简单啊,就我刚才说的方法

老黄在白板上写下pseudo code

int nextInt(int min, int max) {int range = max - min;while (true) {int random = 0;int ceiling = 1;for (int i = 0; i < 4; ++i) {random = (random << 8) | rng.nextByte();ceiling = ceiling << 8;if (ceiling >= range)break;}if (random < 0 || random >= range)continue;return min + random;}
}

(看上去不错,首先把[a, b)的问题转化为[0, b-a);生成随机数时,只产生了必要的byte数;最后考虑了负数情况)

阳:……貌似有两个问题:1)range有可能溢出int的范围;2)在极端情况下,这个RNG生成一个随机数平均要执行200多次nextByte。比方说给定随机数范围[0, 1)吧,其实只有0是可取的,你这边虽然只调用一次nextByte,但是值域是[0, 256),取到0的概率是1/256啊,这是不是太heavy了?……哦,还有一个问题,3)ceiling只要算一次就行了吧?而你这边生成失败就会重算。

黄:第一个问题range可以用long替代。第二个问题么……

阳:那nextLong怎么办?

黄:……有没有128位的整型?

阳:…………我想没有……你可以造一个…………算了,我们限定所有的范围都必须是非负整数吧,那第二个问题呢?

黄:(思考一分钟)……我改变一下设计。

int nextInt(int min, int max) {int range = max - min;int times = 0;int ceiling = 1;for (; times < 4; ++times) {if (ceiling >= range)break;ceiling = (ceiling << 8);}if (ceiling < 0)ceiling = MAX_INT;ceiling = ceiling - ceiling % range;while (true) {int random = 0;for (int i = 0; i < times; ++i)random = (random << 8) | rng.nextByte();if (random < 0 || random >= ceiling)continue;return min + random % range;}
}

黄:现在我只需执行一次就能获得最小byte数times,然后ceiling现在是小于等于RNG产生值域的最大值的range的倍数中最大的那个……

阳:我明白,你把[0, ceiling)上的均匀分布折叠到[0, range)上,只要ceiling是range的倍数,均匀性就能保持,ceiling扩大了RNG输出的有效值域范围,也就降低了重复试验的次数。

黄:对,换而言之,重复试验的概率现在最差情况是1/2,就是当range刚好超过RNG输出值域一半的时候,这时候range本身就是ceiling……但这里面还有一个问题:当range非常大,需要四次nextByte的时候,我有1/2概率会得到负数,加上刚才说的情况,于是我会有3/4的概率要抛弃生成的byte,而这正好又发生在nextByte调用次数最多的一类里面。

阳:可以用abs?

黄:对,但是要排除掉MIN_INT,否则会溢出嘛…………(沉思片刻)………不对,不行。

阳:为什么?

黄:你看,排除掉MIN_INT我得到的是[-MAX_INT, MAX_INT]上的均匀分布?

阳:对。

黄:然后我用abs把负数对折到正数上?

阳:…………我明白了,是0,对折后,0取到的概率是其他正整数的1/2,这就破坏均匀性了。

黄:是的…………其实我们只要这样,把负数的最高位置零就行了,这建立了负数到非负数的一一映射。

阳:MIN_INT被映射到了0,不错。但均匀性能保证么,比如虽然是一一映射,但是把松散的区域映射到密集的区域就不一定有效吧?

黄:应该不会,首先nextByte是无符号的,所以通过它生成的4字节随机数是无符号整型上的均匀分布,uint上大于MAX_INT的部分也就是[MIN_INT, -1]并且顺序也是一样的,所以我们把负数高位1抹去,也就是一种折叠,这种操作是证明保持均匀性的。相比较abs反而不合理,因为它将[MIN_INT+1, -1]上分布先逆序,再叠加到正整数上,我不确定这种操作是否合理。

阳:有道理。

黄:所以代码这么写。

int nextInt(int min, int max) {int range = max - min;int times = 1;int ceiling = 1;for (; times < 4; ++times) {if (ceiling >= range)break;ceiling = (ceiling << 8);}if (ceiling < 0)ceiling = MAX_INT;ceiling = ceiling - ceiling % range;while (true) {int random = 0;for (int i = 0; i < times; ++i)random = (random << 8) | rng.nextByte();random = random & MAX_INT;if (random < ceiling)return min + random % range;}
}

阳:现在最坏情况下期望2次可以得到随机数。

黄:其实现在我觉得没必要获得times,每次都生成一个4字节随机数也可以,毕竟省掉了计算times的一次循坏。

int nextInt() {int random = 0;for (int i = 0; i < 4; ++i)random = (random << 8) | rng.nextByte();return random & MAX_INT;
}int nextInt(int max) {int ceiling = MAX_INT - MAX_INT % max;while (true) {int random = nextInt();if (random < ceiling)return min + random % range;}
}int nextInt(int min, int max) {return min + nextInt(max - min);
}

黄:写成这样可读性更好不是么,也能丰富API。

阳:现在这样实现,long的写法也一模一样了,不错。下面看看浮点数怎么实现。

银:你们可以看看apache.commons.lang模块啊,人家的……好像叫RandomUtils吧,什么都有。可以学学别人的实现。

(未完)

转载于:https://www.cnblogs.com/nagao/p/3944008.html

一个随机数发生器(一)相关推荐

  1. OpenCV之core 模块. 核心功能(2)基本绘图 随机数发生器绘制文字 离散傅立叶变换 输入输出XML和YAML文件 与 OpenCV 1 同时使用

    基本绘图 目的 本节你将学到: 如何用 Point 在图像中定义 2D 点 如何以及为何使用 Scalar 用OpenCV的函数 line 绘 直线 用OpenCV的函数 ellipse 绘 椭圆 用 ...

  2. 基础学习笔记之opencv(14):随机数发生器绘制文字

    本文主要介绍下opencv中自带的一个随机数发生器的类RNG,这里我用这个类来画一些图形,和基础学习笔记之opencv(13):基本绘图 一文中类似,只是 这里画出来图像的坐标,大小,颜色,角度等所有 ...

  3. boost 随机数发生器

    Random     随机数 在很多应用中都需要使用随机数.本库力求提供一个高效的,通用的随机数库.boost库有多种随机数生成方式.先熟悉一下各种随机数生成器的概念. 数字生成器(Number Ge ...

  4. 量子计算机真随机数,量子真随机数发生器研究取得进展

    量子真随机数发生器研究取得进展 超高速真随机数发生器简化设计方案(上)与后处理方法(下) 信息科学技术学院郭弘教授课题组与物理学院张建玮副教授课题组开展合作,在用光学随机源设计研制量子真随机数发生器的 ...

  5. OpenCV 【十六】RNG随机数发生器putText绘制文字

    1 目的 使用 随机数发生器类 (RNG) 并得到均匀分布的随机数. 通过使用函数 putText 显示文字. 第一步是实例化一个 Random Number Generator(随机数发生器对象) ...

  6. c51汇编语言随机数函数,汇编语言随机数发生器

    我最近开始学习汇编语言.对于我正在研究的项目,我必须使用线性一致性来生成随机数生成器.我想拿三个数字.上限,下限和我想要的随机数的数量.如式为获得一个随机数,我想出了....汇编语言随机数发生器 ra ...

  7. c++语言生成一个随机数,C/C++怎么生成“真正的”随机数

    C/C++怎么生成"真正的"随机数 C语言中,直接用rand()函数生成随机数的话,会出现一个尴尬的局面,那就是每次运行都会生成相同的一个数,这显然不能满足我们的期望.怎么回事呢? ...

  8. 【STM32】随机数发生器相关函数和类型

    00. 目录 文章目录 00. 目录 01. RNG概述 02. 相关类型 03. 相关函数 04. 结构体封装 05. 预留 06. 附录 07. 声明 01. RNG概述 RNG 处理器是一个以连 ...

  9. 【STM32】随机数发生器详解

    00. 目录 文章目录 00. 目录 01. 随机数发生器简介 02. 随机数发生器主要特性 03. 随机数发生器功能说明 04. 随机数发生器操作 05. 随机数发生器寄存器 5.1 RNG 控制寄 ...

最新文章

  1. 企业分布式微服务云SpringCloud SpringBoot mybatis - 服务消费者(Feign)
  2. css3遮罩层_CSS3鼠标hover图片超酷遮罩层动画特效
  3. 左滑右滑,在VS Code里滑个妹纸给你写喜欢的代码?
  4. springboot集成redis配置多数据源
  5. C语言内存管理之结构体
  6. mysql基础知识整理_mysql基础知识整理(一)
  7. Java操作MongoDB之mongodb-driver
  8. C语言开定时器做呼吸灯程序,简单的基于51单片机定时器上呼吸灯程序
  9. 凸优化第八章几何问题 8.4极值体积椭圆
  10. 如何设置pycharm代码字体
  11. linux下面如何看抓包文件,linux抓包
  12. i18n国际语言代码对照表
  13. [Go]基于Go语言的Web路由转发,多个网站共享一个端口(新版本,支持WebSocket)
  14. nginx配置静态页面html,nginx 服务器配置静态网页
  15. 鸿蒙和小米哪个值得入手,鸿蒙系统能够吸引小米的优势是什么
  16. win10无敌隐身术
  17. HTML三只松鼠怎么编写,卖萌的设计体验 ——访三只松鼠首席设计师 李子明(原创文章)...
  18. 好书推荐 | 2020 年读过的这些好书,建议收藏!
  19. Java HashMap的遍历(常用的四种方式),及key,value规律
  20. 判断两个平面向量之间夹角是顺时针还是逆时针

热门文章

  1. python包裹和运费_这个Python库真的太好用了,10行代码就能轻松搞定目标检测
  2. can总线短距离不用双绞线_汽车中的总线系统——以太网(2)博通出世
  3. 阿里云 centos mysql 5.6_关于centOS安装配置mysql5.6那点事
  4. observable java_java源码阅读Observable(观察者模式)
  5. 字符界面运行mysql_详解linux系列之字符界面下MySQL+apache+php的源代码安装
  6. deeplung代码实现测试是卡住情况说明
  7. 图像算法二:【图像几何变换】平移、镜像、转置、缩放、旋转、插值
  8. Evaluate the Malignancy of Pulmonary Nodules Using the 3D Deep Leaky Noisy-or Network 论文阅读
  9. Dcmtk在PACS开发中的应用(基础篇) 第一章 与影像设备互连 作者 冷家锋
  10. 5步详解PHP文件上传功能的实现