下面是在牛客网看到的一道题;

//假设这n个数的序号依次为0,1,2,...,n-1,数组名为num

void knuth1(int* pNum, int m, int n){srand((unsigned int)time(0));for (int i=0; i<n; i++){if (rand()%(n-i) < m)//rand()%(n-i)的取值范围是[0, n-i){cout << pNum[i] << endl;m--;}}}

这是牛客网上的一道题,目的是从n个数中可放回地随机抽取m个数字。注意数字是可放回的,所以n个数字每一个数字被cout的概率都是m/n。当i取0,rand()%(n-i)的取值在是[0,n-1]范围随机分布,小于m的概率自然是m/n。当i取1,随机数的范围在[0,n-2],共n-1个取值。这时m的值要取决于i=0时有没有输出,所以可以用全概率公式计算。

这里想说的不是这道题本身,而是这个rand()函数。Rand()函数括号内是没有参数的,直接返回[0,RAND_MAX]的随机整数。但需要注意的是rand产生的是伪随机数,用线性同余法实现,依然是一个有限状态转换机,依然有周期(周期很长),所以当我们再一次调用这个函数,得到的结果相同,这在我们调试的时候很方便,但如果需要产生真正的随机数就需要srand来设置随机种子了。void srand (unsigned int seed);直接调用rand时,种子的值默认是1.要得到真正的随机数,每次设置的种子也应该不一样,我们通常使用time(0)作为种子,即把系统时间作为种子,保证了不同时刻得到的种子是不一样的。

在matlab中,rand函数就可以直接得到真正的随机数。为了在不同时刻运行函数时得到相同的随机数,便于调试,我们需要把随机数生成器初始化:RAND('state',0)。但是这一用法在新的matlab版本中不再支持,而推荐使用RNG。

在编译器中可看到RAND_MAX是一个宏定义,为0x7fff,也就是二进制的15位1. 百度百科中有:(C11)标准中未规定 RAND_MAX 的具体数值。但该标准规定了RAND_MAX 的值应至少为32767,最大为2147483647.这就引出了一个问题:int型明明在32位系统和64位系统中都占4字节,为什么这里产生的随机数的最大值只是15位全1的二进制和31位全1的二进制?其实,这就是带符号的short int型和int型的正数的最大值。于是,就有了第二个,也是很基本的一个问题,int型表示的范围是什么,正整数和负整数都是怎么表示的?(惭愧)

我们以一个字节长度为例。先不用管书上所强行灌输的数目符号位,原码,补码,我们从头开始,自己试着解决问题。8bit编码方式有2的8次方共256种,在图像中可以表示[0,255]的灰度级,在图像中像素取值只能是0或者正数,在计算机中,我们当然还需要表示负数,那么负数(先研究负整数)是怎么表示的呢?一个最自然而然的方式是把256种编码方式的一部分表示正数,一部分表示负数,一部分表示0.我们把0000 0001~01111 1111这一部分用来表示正整数,因为这一部分从0开始,是最符合我们数数的习惯的。那么现在的问题就是如何把剩下的表示负数。

首先,我们可以观察到剩下的部分除了0000 0000,最高位都是1,这就可以解释,为什么最高位的1来表示负数。那么1000 0000~1111 1111到底和负数是怎么对应的呢?一个理所当然的思路是1111 1111=-1*(0111 1111)=-127。我们来验证一下,1111 1111+0111 1111=0?明显不等于,但同时也给了我们一个思路,可以利用已有的正整数表达方法和绝对值相等的正负数之和为0的特点求负整数的表达方式。-1的二进制形式等于

0000 0000-0000 0001=1111 1111+0000 0001-0000 0001=1111 1111

于是我们知道,1111 1111对应的是-1.上面的式子还告诉了我们更多:0000 0000可以写做全1的数再加1,进位舍去就是全0.并且我们发现,将0拆分成全1和1的和,这样我们求-A的补码=全1-A+1,全1和二进制的加减都相对于异或,也就是取反,所以我们也终于得到了所谓的求负数补码的方法:按位取反再加1.

于是我们可以得到-2的补码:1111 1111+0000 0001-0000 0010=1111 1110

现在再考虑几个特殊的数,128=1000 0000,-128的补码=1000 0000,可见自然数128=-128的补码形式,由于我们已经规定了最高位是符号位,符号位1表示负数,所以0~255是代表补码时只有-128,没有128.于是我们也得到了所谓的一字节带符号整数取值范围[-128,127].

这样,我们得到规律,原来的0~255的数被分成两部分,[0,127]是递增的正数,和原来的表示方法一样,之后的数代表负数,也是递增。

到这里我们依然没有解释一句话,补码是为了让计算机把减法当做加法来做。其实,我们数轴首尾相接形成一个圆就好理解了。刚才我们也提到了,计算机中的加法超过长度会高位舍去,这其实意味着计算机中的数字是闭环的状态机。无论是加还是减,都是在这个闭环里面移位,只不过是逆时针还是顺时针罢了。我们把时钟的十二点位置看作是0/255,加法看作是顺时针移位(蓝色曲线),减法是逆时针(黄色曲线),这样六点钟附近是加数和减数绝对值最大的位置。为了避免减法(逆时针),我们可以顺时针移动相比于逆时针较大的角度达到相同的效果。逆时针转动30度就相当于顺时针转动330度,而330度就可以用时钟上的刻度来衡量,即0~255就是时钟的刻度。330度就是30度的补角,这也是补码的来历。

在查阅关于rand的使用的过程中,看到了一个例子,产生[0,10]之间的随机数:

#include<stdlib.h>int main(){int i,j;for(i=0; i<10; i++){j=1+(int)(10.0 * rand()/(RAND_MAX+1.0));printf("%d ",j);}}

产生介于 1 到 10 间的随机数值。这里的问题是为什么要加1.0?我的理解是如果分母取RAND_MAX,那么随机数范围就被归一化到[0,1],乘10后范围是[0,10],而我们的目标是先取得[0,9]的随机数再加1才能满足要求。注意到这里的加法和乘法都是float型,最后被强制转换成int型,其实这才是关键。分母取(最大值+1),使得随机数归一化后无法取到1,乘10之后范围是[0,10),最大值在9和10之间。而int强制转换是直接取浮点数的整数部分,这样我们就得到了范围在[0,9]的随机数。

P.s 浮点数到整型的转换,除了直接取整数部分,还有ceil函数和floor函数。Floor函数是取小于等于浮点数的整数,这一点与直接取整数部分在浮点数是负数时结果有区别。

最后,关于归一化方法和使用线性同余法得到想要的范围内的随机数的区别,有人说是前者是在多次随机出来的结果,前者理论上会更平均,而后者仅仅是和10求余得到的结果,没前面的结果来得平均。关于这个说法还不是很懂,有空可以再研究一下线性同余法。

Reference:

  1. rand函数https://blog.csdn.net/cmm0401/article/details/54599083
  2. 醍醐灌顶https://blog.csdn.net/wenxinwukui234/article/details/42119265
  3. 类型转换https://zhidao.baidu.com/question/1964506016596059780.html
  4. 牛客网:https://www.nowcoder.com/questionTerminal/12796031452e4ced8a16255bb02c4168
  5. 最后https://zhidao.baidu.com/question/561525713.html?qbl=relate_question_0&word=rand%20%BC%D31

Rand函数使用和对补码的理解相关推荐

  1. 关于srand()与rand()函数的理解-----必看系列

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.时间戳: 二.srand()和rand()函数的具体举例讲解 我们需要了解一下srand()函数 总结 前言 本文 ...

  2. rand()函数100000随机数_利用随机函数Rand、Randbetween制作抽奖器应用技巧解读

    在Excel系统中,随机数函数有两个,分别为Rand和Randbetween,其作用也是不相同的,Rank函数的作用为生成0-1之间的随机数,而Randbetween函数的作用为生成指定范围内的随机数 ...

  3. c语言rand再哪个头文件里,c语言下rand函数在哪个头文件中

    满意答案 kongxiang09 2012.11.20 采纳率:47%    等级:13 已帮助:7313人 1.只能产生伪随机数,最多也只是提高模拟的程度而已,rand函数内部管理着一个计数单位,程 ...

  4. 【C语言】rand()函数(如何生成指定范围随机数)

    一.rand()函数简介 我们先来看一下cplusplus.com - The C++ Resources Network网站上rand函数的基本信息: 系统生成随机数时需要使用rand函数(rand ...

  5. c语言随机字符rand,C语言中生产随机数 rand()函数

    一:如果你只要产生随机数而不需要设定范围的话,你只要用rand()就可以了:rand()会返回一随机数值, 范围在0至RAND_MAX 间.RAND_MAX定义在stdlib.h, 其值为214748 ...

  6. 不使用rand函数创建随机值

    不使用rand函数创建随机值 -今天无意中发现了一个不用rand函数创建随机值的方法,到我现在写这篇博客还是没弄明白它到底是怎么实现随机值的,如果有it大佬能否为我这个新人解惑,废话不多说看描述 问题 ...

  7. 23.代码简单实现模拟噪声(图像噪声/一、二阶矩/功率谱密度/at函数/rand函数)-- OpenCV从零开始到图像(人脸 + 物体)识别系列

    本文作者:小嗷 微信公众号:aoxiaoji 吹比QQ群:736854977 简书链接:https://www.jianshu.com/u/45da1fbce7d0 本文你会找到以下问题的答案: 图像 ...

  8. rand()函数与srand()函数以及随机数种子详解

    目录 引言 rand()函数 随机数 srand()函数 拓展思考 引言 初学者大部分对这两个函数的意义都不甚了解,以及不明白为什么需要srand()函数来播种,这里会对两函数的意义进行解释,让大家明 ...

  9. C语言详解生成随机数的过程,time函数、时间戳timer、rand函数和srand函数,附猜数字小游戏

    第十一篇:随机数详解 一.准备工作(预备知识) 1.1.生成伪随机数(函数rand) 1.2.伪随机数"变成"随机数(函数time) 1.3.生成确定范围随机数 二.练手随机数经典 ...

最新文章

  1. springboot2稳定版本_Spring Boot 2.4 正式发布,重大调整
  2. 获取机器安装.NET版本的几种方式
  3. 百度网盘直接解析高速下载文件源码
  4. 安全强化linux-SELinux
  5. 图论——P问题、NP问题、NPC问题、NP-hard问题
  6. oracle数据库查看建表语句,oracle 查看建表语句
  7. 墨魂服务器维修,墨魂琅轩路线怎么选最新游戏攻略
  8. LDUOJ spj 修改
  9. halcon一维码识别
  10. 联合证券|滴滴出行即日起恢复!A股嗨了!券商扛起领涨大旗
  11. HashMap的树化门槛为什么是8
  12. 局域网中的每台计算机主机扩展槽,计算机导论选择题
  13. 【餐厅点餐平台|一】项目描述+需求分析
  14. 关联规则:营销购物,自有乾坤
  15. 高手勿进!写给初中级程序员以及还在大学修炼的“准程序员”的成长秘籍
  16. 机器学习 K-Means(++)算法
  17. 【论文阅读】Long-term Temporal Convolutions for Action Recognition
  18. python中3or5-python3 中 and 和 or 运算规律
  19. 基于win10 和python3.6激活虚拟环境成功!
  20. MS51替换N76E003注意事项

热门文章

  1. 这可能是你与 AI 大神们近距离接触的唯一机会……
  2. 数据驱动精准化营销在大众点评的实践
  3. 微服务Dubbo和SpringCloud架构设计、优劣势比较
  4. 论文笔记(Neural Graph Collaborative Filtering)
  5. SpringBoot:如何处理SprintBoot提示Whitelabel Error Page以及了解原因?
  6. 知识图谱最新权威综述论文解读:知识表示学习部分
  7. Vue 组件间通信六种方式
  8. iOS:图片相关(19-05-09更)
  9. 操作文件 -------JavaScrip
  10. Python 学习随笔1