热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C/C++编程笔记:C语言rand()随机函数,深入解析程序随机数!

各种编程语言返回的随机数(确切地说是伪随机数)实际上都是根据递推公式计算的一组数值,当序列足够长,这组数值近似满足均匀分布。

各种编程语言返回的随机数(确切地说是伪随机数)实际上都是根据递推公式计算的一组数值,当序列足够长,这组数值近似满足均匀分布。

C的标准函数库提供一随机数生成器rand(定义在stdlib.h),能返回0~RAND_MAX之间均匀分布的伪随机整数(RAND_MAX至少为32767,一般都默认为32767)。

用rand()随机生成在[x,y]内的整数

int k;

k=x+rand()%(y-x+1),k即为所求范围内随机生成的数,rand()%a的结果最大为a-1.

rand( )%20的意思的生成20以内的随机数。

例如:

#include#includevoid main(){for(int i&#61;0;i<10;i&#43;&#43;)printf("%d\n",rand());}

如果我们是第一次运行&#xff0c;而且对其不太清楚&#xff0c;那么它生成的基本上算是0&#xff0d;RAND_MAX之间的等概率随机数列了。但是如果你第二次运行的时候会发现输出结果仍和第一次一样。

原来rand()生成伪随机数时需要一个种子&#xff08;计算伪随机序列的初始数值&#xff09;才行&#xff0c;如果种子相同就会得到相同的序列结果&#xff08;这就是函数的好处T-T&#xff09;。这个“优点”被有的软件利用于加密和解密。加密时&#xff0c;可以用某个种子数生成一个伪随机序列并对数据进行处理&#xff1b;解密时&#xff0c;再利用种子数生成一个伪随机序列并对加密数据进行还原。这样&#xff0c;对于不知道种子数的人要想解密就需要多费些事了。当然&#xff0c;这种完全相同的序列对于你来说是非常糟糕的。要解决这个问题&#xff0c;需要在每次产生随机序列前&#xff0c;先指定不同的种子&#xff0c;这样计算出来的随机序列就不会完全相同了。

srand()用来设置rand()产生随机数时的随机数种子。在调用rand()函数产生随机数前&#xff0c;必须先利用srand()设好随机数种子seed,如果未设随机数种子&#xff0c;rand()在调用时会自动设随机数种子为1&#xff08;有人说默认是0&#xff0c;困惑中&#xff09;。上面的两个例子就是因为没有设置随机数种子&#xff0c;每次随机数种子都自动设成相同值1&#xff0c;进而导致rand()所产生的随机数值都一样。(可能有人知道C语言中的随机函数random&#xff0c;可是random函数并不是ANSIC标准&#xff0c;所以说&#xff0c;random函数不能在gcc,vc等编译器下编译通过。我们可以自己编一个^0^&#xff09;我们需要使程序每一次使用的种子都不一样&#xff0c;现在主要问题是种子srand的选择是不是接近随机&#xff08;不存在完全随机&#xff09;&#xff0c;你也可以人为指定种子数。

Windows9x/NT的游戏FreeCell就允许用户指定种子数&#xff0c;这样用户如果一次游戏没有成功&#xff0c;下次还可以以同样的发牌结果再玩一次。但我们还是喜欢系统自动生成……最简单的方法就是利用系统时间了&#xff08;几乎所有的人都这么做&#xff09;&#xff0c;因为时间的数值随时间变化而变化&#xff0c;运行两次&#xff0c;一般不会出现前一次和后一次相同的局面&#xff0c;time(NULL)会返回一个表示当前系统时间的整数&#xff08;它在time.h中&#xff0c;据说time()函数求出来的是自1970年1月1日到现在的秒数&#xff0c;有的说是unix元年&#xff0c;不知道这两个是不是一天……:&#xff08;&#xff0c;另外有的嫌麻烦会写作time&#xff08;0&#xff09;&#xff09;。我们这么来写&#xff1a;

#include#include#includevoid main(){srand((int)time(0));for(int x&#61;0;x<10;x&#43;&#43;)printf("%d\n",(rand());}

 据说如果软件一直开两天&#xff0c;种子会有1/(60*60*24)个可能会重复&#xff0c;虽说这对于一般人已经绝对足够了&#xff0c;但纵然人不舒服&#xff0c;于是我在网上开到有这么写的&#xff1a;

#include#include#include#includevoid main(void){int i;unsigned int seedVal;struct timeb timeBuf;ftime(&timeBuf);seedVal&#61;((((unsigned int)timeBuf.time&0xFFFF)&#43;(unsigned int)timeBuf.millitm)^(unsigned int)timeBuf.millitm);srand((unsigned int)seedVal);for(i&#61;0;i<10;&#43;&#43;i)printf("%6d\n",rand());}

“上面的程序先是调用_ftime()来检查当前时间&#xff0c;并把它的值存入结构成员timeBuf.time中&#xff0c;当前时间的值从1970年1月1日开始以秒计算。在调用了_ftime()之后&#xff0c;在结构timeBuf的成员millitm中还存入了当前那一秒已经度过的毫秒数&#xff0c;但在DOS中这个数字实际上是以百分之一秒来计算的。然后&#xff0c;把毫秒数和秒数相加&#xff0c;再和毫秒数进行异或运算。当然也可以对这两个结构成员进行更多的计算&#xff0c;以控制seedVal的取值范围&#xff0c;并进一步加强它的随机性&#xff0c;但上例用的逻辑运算就已经足够了。”

看下面代码&#xff1a;

void main(){for(int i&#61;0;i<100000;i&#43;&#43;){srand( (unsigned)time( NULL ) );printf&#xff08;"%d\n",rand());}}

为什么总是生成一个数&#xff1f;&#xff1f;&#xff1f;因为此程序产生一个随机数之前&#xff0c;都调用一次srand&#xff0c;而由于计算机运行很快&#xff0c;所以每用time得到的时间都是一样的&#xff08;time的时间精度较低&#xff0c;只有55ms&#xff09;。这样相当于使用同一个种子产生随机序列&#xff0c;所以产生的随机数总是相同的。把srand放在循环外看看&#xff1a;

srand( (unsigned)time( NULL ) );

for(int i&#61;0;i<100000;i&#43;&#43;)

问题解决了&#xff1a;&#xff09;

既然生成的是

0&#xff0d;RAND_MAX之间均匀分布的随机整数&#xff08;我们姑且把以上方法生成的是理想的随机数吧&#xff09;&#xff0c;那么要生成0-x之间(包括0不包括x)的随机数就把rand&#xff08;&#xff09;改成 

rand()/(double)RAND_MAX*x &#xff0c;要生成x-y之间&#xff08;包括x不包括y&#xff09;的就是 

rand()/(double)RAND_MAX*(y-x)&#43;x   了。但是如果要生0-10之间的整数很多人会这么写&#xff1a;

#include#include#includevoid main(){srand((int)time(0));for(int x&#61;0;x<10;x&#43;&#43;)printf("%d\n",(rand()%10);}

x-y的就是 rand()%(y-x)&#43;x

???懂一点概率的就知道这样写&#xff0c;会使得到的数列分布不均的&#xff0c;但是大家确实都喜欢这么写……因为在x的值比较小&#xff0c;RAND_MAX相对较大&#xff0c;而生成的数列有不算太大&#xff0c;又是用来解决精确度要求不高的问题&#xff08;如一些游戏目标,传说游戏中踩地雷式的遇敌就是用rand()实现的.

当主角在地图上走的时候动不动冒出三两小兵挑衅兼找死.它的实现方式是设主角所立位置为0&#xff0c;主角每走一步&#xff0c;变量加1&#xff0c;当变量&#61;&#61;随机取得的数时&#xff0c;小兵出现&#xff09;&#xff0c;这样写足够了……

下面再讨论生成m个小于n的不重复随机数的算法

本人认为算法结构很重要&#xff0c;但语句结构也很重要&#xff0c;所以我喜欢一个算法给出多个语句结构……

最容易想到的傻瓜算法&#xff0c;逐个产生这些随机数&#xff0c;每产生一个&#xff0c;都跟前面的随机数比较&#xff0c;如果重复&#xff0c;就重新产生&#xff1a;

算法一&#xff08;1&#xff09;

srand((unsigned)time(NULL));for(j &#61; 0; j

很早的时候学编程都喜欢用goto语句&#xff0c;因为过去算法是用流程图表示的&#xff0c;用goto可以直接转译&#xff0c;而且循环语句发展不完善&#xff0c;仅仅是作为条件分支的一个补充&#xff0c;如果条件分支箭头向上就是一个循环&#xff0c;而且goto可以实现过去循环所不能实现的结构&#xff0c;形成很巧妙的循环交叉。所以过去一般认为有两种结构&#xff0c;顺序和分支&#xff0c;循环是分支的特殊情况。但是break和continue语句使这些结构实现成为可能&#xff0c;后来发现goto的使用会造成维护和调试的许多麻烦&#xff0c;所以循环逐渐代替了goto&#xff0c;使程序更有层次。现在编程不建议用goto了&#xff0c;如果用goto还会被认为编程修养不好……言归正题&#xff0c;把它的goto去掉&#xff1a;

算法一&#xff08;2&#xff09;

srand((unsigned)time(NULL));j&#61;0;while (j{a[j] &#61; rand() % n ;for(i&#61;0;iif(ij&#43;&#43;}

 但是后来都建议用for循环&#xff0c;这样可以使循环条件跟紧凑&#xff1a;

算法一&#xff08;3&#xff09;

srand((unsigned)time(NULL));for(j&#61;0;j

但这是个很笨的方法&#xff0c;且比较次数呈线性增长&#xff0c;越往后次数越多……另一个想法是生成一个就把它从中去掉&#xff0c;就可以实现不重复了&#xff1a;

算法二&#xff08;1&#xff09;

for (i&#61;0;i

b[]是生成的随机数列

 这样做也太麻烦了&#xff0c;生成的直接做个标记不就行了&#xff1f;

算法三&#xff08;1-1&#xff09;

i&#61;rand()%m;a[i]&#61;1;b[0]&#61;i;for(j&#61;1;j

写紧凑一些吧&#xff0c;直接&#xff1a;

算法三&#xff08;1-2&#xff09;

for(j&#61;0;j

但是我看到了更简洁的&#xff1a;

int n&#61;某值&#xff1b;int a[n]&#61;{0}&#xff1b;for (i&#61;1;i<&#61;n;i&#43;&#43;){while(a[x&#61;rand()%n]);a[x]&#61;i;}

这个无循环体的while保证a[m]是初始化的0状态。这生成了n个&#xff0c;我们只取m个&#xff0c;这太浪费了&#xff0c;结合一下&#xff1a;

int n&#61;某值&#xff0c;m&#61;某值&#xff1b;int a[n]&#61;{0},b[m]for (i&#61;1;i<&#61;n;i&#43;&#43;){while(a[x&#61;rand()%n]);b[i]&#61;x;a[x]&#61;1;}

但是总叫人不舒服&#xff0c;对这种算法谁有更好的写法呢&#xff1f;

这种算法越到后面&#xff0c;遇到已使用过的元素的可能性越高&#xff0c;重复次数就越多&#xff0c;但是当m较小时还是很好的&#xff1a;&#xff09;

这都不太让人满意么&#xff1f;看看下面这个&#xff1a;

算法四&#xff08;1&#xff09;

for (i&#61;0;i0;i--){w&#61;rand()%i;t&#61;a[i];a[i]&#61;a[w];a[w]&#61;t;}

这个算法很不错&#xff0c;有人会怀疑其随机性&#xff0c;但个人认为是没问题的&#xff0c;首先第二行按顺序用0到n填满整个数组&#xff1b;第三行&#xff0c;是随机产生从0到n-2个数组下标&#xff0c;把这个下标的元素值跟n-1下标的元素值交换&#xff0c;一直进行到下标为1的元素。因此它只需要遍历一次就能产生全部的随机数。

但这样会生成n个小于n的随机数&#xff0c;我们只要m个&#xff0c;再加两句&#xff1a;

for(i&#61;0;i

 b[i]&#61;a[i];

b[]是生成的随机数列

 如果m和n很接近的话或者相等是最好&#xff0c;但可能不是……那改一下&#xff1a;

算法四&#xff08;2&#xff09;

for (i&#61;0;i

但是条件反过来了&#xff0c;如果m远小于n还行&#xff0c;如果接近&#xff0c;其随机性就存在问题了&#xff08;为什么&#xff1f;&#xff09;&#xff0c;再改一下&#xff1a;

算法四&#xff08;3&#xff09;

for (i&#61;0;i

这样可以万无一失了……

希望对大家有帮助&#xff01;

自学C/C&#43;&#43;编程难度很大&#xff0c;不妨和一些志同道合的小伙伴一起学习成长&#xff01;

C语言C&#43;&#43;编程学习交流圈子&#xff0c;【点击进入】微信公众号&#xff1a;C语言编程学习基地

有一些源码和资料分享&#xff0c;欢迎转行也学习编程的伙伴&#xff0c;和大家一起交流成长会比自己琢磨更快哦&#xff01;


推荐阅读
  • 深入探讨前端代码优化策略
    本文深入讨论了前端开发中代码优化的关键技术,包括JavaScript、HTML和CSS的优化方法,旨在提升网页加载速度和用户体验。 ... [详细]
  • 汇编语言:编程世界的始祖,连C语言都敬畏三分!
    当C语言还在萌芽阶段时,它首次接触到了汇编语言,并对其简洁性感到震惊。尽管汇编语言的指令极其简单,但它却是所有现代编程语言的基础,其重要性不言而喻。 ... [详细]
  • c语言二元插值,二维线性插值c语言
    c语言二元插值,二维线性插值c语言 ... [详细]
  • 本文提供了一种有效的方法来解决当Android Studio因电脑意外重启而导致的所有import语句出现错误的问题。通过清除缓存和重建项目结构,可以快速恢复开发环境。 ... [详细]
  • 深入解析C语言中的关键字及其分类
    本文将全面介绍C语言中的关键字,并按照功能将其分为数据类型关键字、控制结构关键字、存储类别关键字和其他关键字四大类,旨在帮助读者更好地理解和运用这些基本元素。C语言中共有32个关键字。 ... [详细]
  • 本文通过分析一个具体的案例,探讨了64位Linux系统对32位应用程序的兼容性问题。案例涉及OpenVPN客户端在64位系统上的异常行为,通过逐步排查和代码测试,最终定位到了与TUN/TAP设备相关的系统调用兼容性问题。 ... [详细]
  • protobuf 使用心得:解析与编码陷阱
    本文记录了一次在广告系统中使用protobuf进行数据交换时遇到的问题及其解决过程。通过这次经历,我们将探讨protobuf的特性和编码机制,帮助开发者避免类似的陷阱。 ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • OpenCV中的霍夫圆检测技术解析
    本文详细介绍了如何使用OpenCV库中的HoughCircles函数实现霍夫圆检测,并提供了具体的代码示例及参数解释。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • CoreData 表关联详解
    在企业中,通常会有多个部门,每个员工隶属于某个部门。这种情况下,员工表和部门表之间就会形成关联关系。本文将详细介绍如何在CoreData中实现表关联,并通过示例代码展示如何添加和查询关联数据。 ... [详细]
  • Spring Boot + RabbitMQ 消息确认机制详解
    本文详细介绍如何在 Spring Boot 项目中使用 RabbitMQ 的消息确认机制,包括消息发送确认和消息接收确认,帮助开发者解决在实际操作中可能遇到的问题。 ... [详细]
  • 开发笔记:1035 Password (20) ... [详细]
  • 在机器学习中,我们经常需要对训练数据进行随机打乱以提高模型的泛化能力。本文介绍如何使用 numpy.random.permutation 函数在打乱数据的同时保持 x 和 y 的原始映射关系。 ... [详细]
author-avatar
快乐皇妃800
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有