




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、c语言中的随机函数分析与生成m个不重复随机数算法比较(转)C+&STL2008-12-1918:04:45阅读134评论0字号:大中小订阅一说起随机函数,恐怕又有人说这是老生长谈了一般很多人都形成了自己的固定格式,因为随机数用处比较大,用的时候比较多,拿过来就用了。但是新手不这么干,他们总是抱有疑惑,我就是一个新手,而且较菜为了让跟我一样的菜鸟看明白,我会尽量的说得让高手们不屑一顾(:由于可能内容太多可能会分篇,大家见谅人计算机的好处是精确,所以它不擅长模拟信号,但它的缺点也是如此。于是在一些模拟问题上计算机遇到麻烦了比如所随机数,因为函数嘛,总会是确定的,确定的算法就会生成确定的结果。各种编
2、程语言返回的随机数(确切地说是伪随机数)实际上都是根据递推公式计算的一组数值,当序列足够长,这组数值近似满足均匀分布。c的标准函数库提供一随机数生成器rand(定义在stdlib.h),能返回0RAND_MAX之间均匀分布的伪随机整数(RAND_MAX至少为32767,一般都默认为32767)。例如:#include#includevoidmain()for(inti=0;i10;i+)printf(%dn,rand();如果我们是第一次运行,而且对其不太清楚,那么它生成的基本上算是0RAND_MAX之间的等概率随机数列了。但是如果你第二次运行的时候会发现输出结果仍和第一次一样。:(原来ran
3、d()生成伪随机数时需要一个种子(计算伪随机序列的初始数值)才行,如果种子相同就会得到相同的序列结果(这就是函数的好处T-T)。这个“优点”被有的软件利用于加密和解密。加密时,可以用某个种子数生成一个伪随机序列并对数据进行处理;解密时,再利用种子数生成一个伪随机序列并对加密数据进行还原。这样,对于不知道种子数的人要想解密就需要多费些事了。当然,这种完全相同的序列对于你来说是非常糟糕的。要解决这个问题,需要在每次产生随机序列前,先指定不同的种子,这样计算出来的随机序列就不会完全相同了。srand()用来设置rand()产生随机数时的随机数种子。在调用rand()函数产生随机数前,必须先利用sra
4、nd()设好随机数种子(seed),如果未设随机数种子,rand()在调用时会自动设随机数种子为1(有人说默认是0,困惑中)。上面的两个例子就是因为没有设置随机数种子,每次随机数种子都自动设成相同值1,进而导致rand()所产生的随机数值都一样。(可能有人知道C语言中的随机函数random,可是random函数并不是ANSIC标准,所以说,random函数不能在gcc,vc等编译器下编译通过。我们可以自己编一个A0A)我们需要使程序每一次使用的种子都不一样,现在主要问题是种子srand的选择是不是接近随机(不存在完全随机),你也可以人为指定种子数。Windows9x/NT的游戏FreeCell
5、就允许用户指定种子数,这样用户如果一次游戏没有成功,下次还可以以同样的发牌结果再玩一次。但我们还是喜欢系统自动生成最简单的方法就是利用系统时间了(几乎所有的人都这么做),因为时间的数值随时间变化而变化,运行两次,一般不会出现前一次和后一次相同的局面,time(NULL)会返回一个表示当前系统时间的整数(它在time.h中,据说time()函数求出来的是自1970年1月1日到现在的秒数,有的说是unix元年,不知道这两个是不是一天:(,另外有的嫌麻烦会写作time(0)。我们这么来写:#include#include#includevoidmain()srand(int)time(0);for(
6、intx=0;x10;x+)printf(%dn,(rand();据说如果软件一直开两天,种子会有1/(60*60*24)个可能会重复,虽说这对于一般人已经绝对足够了,但纵然人不舒服,于是我在网上开到有这么写的:#include#include#include#includevoidmain(void)inti;unsignedintseedVal;structtimebtimeBuf;ftime(&timeBuf);seedVal=(unsignedint)timeBuf.time&0 xFFFF)+(unsignedint)timeBlitm)A(unsignedint)tim
7、eBlitm);srand(unsignedint)seedVal);for(i=0;i10;+i)printf(%6dn,rand();(下面是关于它的解释,但我也不是太明白,引用过来大家分析一下)“上面的程序先是调用_ftime()来检查当前时间,并把它的值存入结构成员timeBuf.time中,当前时间的值从1970年1月1日开始以秒计算。在调用了_ftime()之后,在结构timeBuf的成员millitm中还存入了当前那一秒已经度过的毫秒数,但在DOS中这个数字实际上是以百分之一秒来计算的。然后,把毫秒数和秒数相加,再和毫秒数进行异或运算。当然也可以对这两个结构成员进行更
8、多的计算,以控制seedVal的取值范围,并进一步加强它的随机性,但上例用的逻辑运算就已经足够了。”看下面代码:voidmain()for(inti=0;i100000;i+)srand(unsigned)time(NULL);printf(%dn,rand();为什么总是生成一个数?因为此程序产生一个随机数之前,都调用一次srand,而由于计算机运行很快,所以每用time得到的时间都是一样的(time的时间精度较低,只有55ms)。这样相当于使用同一个种子产生随机序列,所以产生的随机数总是相同的。把srand放在循环外看看:srand(unsigned)time(NULL);for(inti
9、=0;i100000;i+)问题解决了:)既然生成的是0RAND_MAX之间均匀分布的随机整数(我们姑且把以上方法生成的是理想的随机数吧),那么要生成0-x之间(包括0不包括x)的随机数就把rand()改成rand()/(double)RAND_MAX*x,要生成x-y之间(包括x不包括y)的就是rand()/(double)RAND_MAX*(y-x)+x了。但是如果要生0-10之间的整数很多人会这么写:#include#include#includevoidmain()srand(int)time(0);for(intx=0;x10;x+)printf(%dn,(rand()%10);x-
10、y的就是rand()%(y-x)+x?懂一点概率的就知道这样写,会使得到的数列分布不均的,但是大家确实都喜欢这么写因为在x的值比较小,RAND_MAX相对较大,而生成的数列有不算太大,又是用来解决精确度要求不高的问题(如一些游戏目标,传说游戏中踩地雷式的遇敌就是用rand()实现的.当主角在地图上走的时候动不动冒出三两小兵挑衅兼找死.它的实现方式是设主角所立位置为0,主角每走一步,变量加1,当变量=随机取得的数时,小兵出现),这样写足够了下面再讨论生成m个小于n的不重复随机数的算法本人认为算法结构很重要,但语句结构也很重要,所以我喜欢一个算法给出多个语句结构最容易想到的傻瓜算法,逐个产生这些随
11、机数,每产生一个,都跟前面的随机数比较,如果重复,就重新产生:算法一(1)srand(unsigned)time(NULL);for(j=0;jm;j+)a:aj=rand()%n;for(i=0;ij;i+)if(ai=aj)gotoa;很早的时候学编程都喜欢用goto语句,因为过去算法是用流程图表示的,用goto可以直接转译,而且循环语句发展不完善,仅仅是作为条件分支的一个补充,如果条件分支箭头向上就是一个循环,而且goto可以实现过去循环所不能实现的结构,形成很巧妙的循环交叉。所以过去一般认为有两种结构,顺序和分支,循环是分支的特殊情况。但是break和continue语句使这些结构实现
12、成为可能,后来发现goto的使用会造成维护和调试的许多麻烦,所以循环逐渐代替了goto,使程序更有层次。现在编程不建议用goto了,如果用goto还会被认为编程修养不好言归正题,把它的goto去掉:算法一(2)srand(unsigned)time(NULL);j=0;while(jm)aj=rand()%n;for(i=0;ij;i+)if(ai=aj)break;if(ij)continue;j+但是后来都建议用for循环,这样可以使循环条件跟紧凑:算法一(3)srand(unsigned)time(NULL);for(j=0;jm;j+)aj=rand()%n;for(i=0;ij;i+
13、)if(ai=aj)i=-1;aj=rand()%n;但这是个很笨的方法,且比较次数呈线性增长,越往后次数越多另一个想法是生成一个就把它从中去掉,就可以实现不重复了:算法二(1)for(i=0;in;i+)ai=i+1;for(i=0;im;i+)j=rand()%(n-i);bi=aj;for(k=j;kn-i-2;k+)ak=ak+1;b是生成的随机数列这样做也太麻烦了,生成的直接做个标记不就行了?算法三(1-1)i=rand()%m;ai=1;b0=i;for(j=1;jm;j+)for(i=rand()%m;ai=1;i=rand()%m);bj=i;ai=1;写紧凑一些吧,直接:算法
14、三(1-2)for(j=0;jm;j+)for(i=rand()%m;ai=1;i=rand()%m);bj=i;ai=1;但是我看到了更简洁的:intn=某值;intan=0;for(i=1;i=n;i+)while(ax=rand()%n);ax=i;这个无循环体的while保证am是初始化的0状态。这生成了n个,我们只取m个,这太浪费了,结合一下:intn=某值,m=某值;intan=0,bmfor(i=1;i=n;i+)while(ax=rand()%n);bi=x;ax=1;但是总叫人不舒服,对这种算法谁有更好的写法呢?这种算法越到后面,遇到已使用过的元素的可能性越高,重复次数就越多,但是当m较小时还是很好的:)这都不太让人满意么?看看下面这个:算法四(1)for(i=0;i0;i-)w=rand()%i;t=ai;ai=aw;aw=t;这个算法很不错,有人会怀疑其随机性,但个人认为是没问题的,首先第二行按顺序用0到n填满整个数组;第三行,是随机产生从0到n-2个数组下标,把这个下标的元素值跟n-1下标的元素值交换,一直进行到下标为1的元素。因此它只需要遍历一次就能产生全部的随机数。但这样会生成n个小于n的随机数,我们只要m个,再加两句:for(i=0;im;i+)bi=ai;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中兽医基础知到课后答案智慧树章节测试答案2025年春黑龙江农业工程职业学院(松北校区)
- 广东江门幼儿师范高等专科学校《流行舞》2023-2024学年第一学期期末试卷
- 上海第二工业大学《广联达软件实训》2023-2024学年第一学期期末试卷
- 河北地质大学《执业医师考试实践技能培训》2023-2024学年第一学期期末试卷
- 关于墨汁容器造型设计问卷调查
- 外网排水施工方案
- 桥梁施工方案编制计划
- 2025年中考语文一轮复习:文学类文本阅读 讲义
- 天津市河西区2024-2025学年高一上学期期末质量调查数学试题(解析版)
- 河北省邢台市2024-2025学年高二上学期期末英语测试试题【含答案】
- 2025中国铁塔甘肃分公司社会招聘60人易考易错模拟试题(共500题)试卷后附参考答案
- 2025社区医保工作计划
- 2025年河南中烟工业限责任公司大学生招聘笔试高频重点提升(共500题)附带答案详解
- 社会责任内审评估报告表
- 农村土地流转合同范本
- 个人借款分期还款合同
- 道德与法治研修日志
- 船舶起重吊装作业安全方案
- 2023年佛山市三水区乐平镇镇属国有企业招聘笔试真题
- T-GXAS 395-2022 蒜头果栽培技术规程
- 品管圈PDCA改善案例-降低高危患者夜间如厕跌倒发生率
评论
0/150
提交评论