版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C语言如何产生随机数1. 基本函数在C语言中取随机数所需要的函数是:nt ran d(void);void srand (un sig ned int n);rand()函数和srand()函数被声明在头文件stdlib.h中,所以要使用这两个函数必 须包含该头文件:#i nclude <stdlib.h>2. 使用方法rand()函数返回0至U RAND_MAX 之间的伪 随机数(pseudorandom)。RAND_MAX 常量被定义在stdlib.h头文件中。其值等于 32767,或者更大。srand()函数使用自变量n作为种子,用来初始化随机数产生器。只要把相同的 种子传入
2、srand(),然后调用rand()时,就会产生相同的随机数序列。因此,我们 可以把时间作为srand()函数的种子,就可以避免重复的发生。如果,调用rand()之前没有先调用srand(),就和事先调用srand(1)所产生的结果一样。for (i nt i=0; i<10; i+)prin tf("%d ", ran d()%10);每次运行都将输出:1 7 4 0 9 4 8 8 2 4 sran d(1);for (i nt i=0; i<10; i+)prin tf("%d ", ran d()%10); 每次运行都将输出:1 7
3、4 0 9 4 8 8 2 4 例2的输出结果与例1是完全一样的。sran d(8);for (int i=0; i<10; i+)printf("%d ", rand()%10);每次运行都将输出:4 0 1 3 5 3 7 7 1 5该程序取得的随机值也是在0,10)之间,与srand(1所取得的值不同,但是每 次运行程序的结果都相同。sran d( un sig ned)time(NULL); for (i nt i=0; i<10; i+)prin tf("%d ", ran d()%10);该程序每次运行结果都不一样,因为每次启动程
4、序的时间都不同。另外需要 注意的是,使用time()函数前必须包含头文件time.h。3. 注意事项求一定范围内的随机数。如要取0,10)之间的随机整数,需将rand()的返回值与10求模。randnu mber = ran d() % 10;那么,如果取的值不是从0开始呢?你只需要记住一个通用的公式。要取a,b)之间的随机整数(包括a,但不包括b),使用:(ran d() % (b - a) + a伪随机浮点数。要取得01之间的浮点数,可以用:ran d() / (double)(RAND_MAX)如果想取更大范围的随机浮点数,比如0100,可以采用如下方法:ran d() /(double
5、)(RAND_MAX)/100)其他情况,以此类推,这里不作详细说明。当然,本文取伪随机浮点数的方法只是用来说明函数的使用办法,你可以采 用更好的方法来实现。举个例子,假设我们要取得010之间的随机整数(不含10本身): 大家可能很多次讨论过随机数在计算机中怎样产生的问题,在这篇文章中,我会对这个问题进行更深入的探讨,阐述我对这个问题的理解。首先需要声明的是,计算机不会产生绝对随机的随机数, 计算机只能产生 伪 随机数”。其实绝对随机的随机数只是一种理想的随机数, 即使计算机怎样发展, 它也不会产生一串绝对随机的随机数。 计算机只能生成相对的随机数,即伪随机 数。伪随机数并不是假随机数,这里的
6、 伪”是有规律的意思,就是计算机产生的 伪随机数既是随机的又是有规律的。怎样理解呢?产生的伪随机数有时遵守一定 的规律,有时不遵守任何规律;伪随机数有一部分遵守一定的规律; 另一部分不 遵守任何规律。比如 世上没有两片形状完全相同的树叶”,这正是点到了事物的 特性,即随机性,但是每种树的叶子都有近似的形状,这正是事物的共性,即规 律性。从这个角度讲,你大概就会接受这样的事实了:计算机只能产生伪随机数 而不能产生绝对随机的随机数。那么计算机中随机数是怎样产生的呢?有人可能会说,随机数是由随机种子”产生的。没错,随机种子是用来产生随机数的一个数,在计算机中,这样的 一个 随机种子”是一个无符号整形
7、数。那么随机种子是从哪里获得的呢?下面看这样一个C程序:/ran dOl.c#in clude<dos.h>static un sig ned int RAND_SEED;un sig ned int ran dom(void)rRAND_SEED=(RAND_SEED*123+59)%65536;return( RAND_SEED);void ran dom_start(void)rint temp2;movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);RAND_SEED=temp0;mai n()run sig ned int
8、 i,n;ran dom_start();for(i=0;i<10;i+)prin tf("%ut",ra ndom();prin tf(" n");这个程序(randO1Q完整地阐述了随机数产生的过程:首先,主程序调用random_start()方法,random_start()方法中的这一句我很感 兴趣:movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);这个函数用来移动内存数据, 其中FP_SEG(far pointer to segment是取temp 数组段地址的函数,FP_OFF(fa
9、r pointer to offset)是取temp数组相对地址的函 数,movedata函数的作用是把位于0040:006CH存储单元中的双字放到数组temp 的声明的两个存储单元中。这样可以通过 temp数组把0040:006CH处的一个16 位的数送给RAND_SEED。random用来根据随机种子RAND_SEED的值计算得出随机数,其中这一句:RAND_SEED = (RAND_SEED*123+59)e536;是用来计算随机数的方法,随机数的计算方法在不同的计算机中是不同的, 即使在相同的计算机中安装的不同的操作系统中也是不同的。我在linux和windows下分别试过,相同的随机
10、种子在这两种操作系统中生成的随机数是不同 的,这说明它们的计算方法不同。现在,我们明白随机种子是从哪儿获得的,而且知道随机数是怎样通过随机 种子计算出来的了。那么,随机种子为什么要在内存的0040:006CH处取?0040:006CH处存放的是什么?学过计算机组成原理与接口技术这门课的人可能会记得在编制 ROM BIOS 时钟中断服务程序时会用到In tel 8253定时/计数器,它与In tel 8259中断芯片的 通信使得中断服务程序得以运转,主板每秒产生的18.2次中断正是处理器根据定时/记数器值控制中断芯片产生的。在我们计算机的主机板上都会有这样一个 定时/记数器用来计算当前系统时间,
11、每过一个时钟信号周期都会使记数器加一, 而这个记数器的值存放在哪儿呢?没错,就在内存的0040:006CH处,其实这一段内存空间是这样定义的:TIMER_LOW DW ?;地址为 0040:006CHTIMER_HIGH DW ?;地址为 0040:006EHTIMER_OFT DB ?;地址为 0040:0070H时钟中断服务程序中,每当TIMER_LOW转满时,此时,记数器也会转满, 记数器的值归零,即TIMER_LOW 处白的 16位二进制归零,而 TIMER_HIGH加 一。rand01.c中的movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(tem
12、p),4);正是把TIMER_LOW 和TIMER_HIGH两个16位二进制数放进temp数组, 再送往rand_seEd,从而获得了 随机种子”现在,可以确定的一点是,随机种子来自系统时钟,确切地说,是来自计算 机主板上的定时/计数器在内存中的记数值。这样,我们总结一下前面的分析, 并讨论一下这些结论在程序中的应用:1. 随机数是由随机种子根据一定的计算方法计算出来的数值。所以,只要计算方法一定,随机种子一定,那么产生的随机数就不会变。看下面这个C+程序:/ran d02.cppEin elude <iostream>in elude <ctime>sing n am
13、espace std;nt mai n()un sig ned int seed = 5; sran d(seed);un sig ned int r = ran d(); cout << r << en dl;在相同的平台环境下,编译生成 exe后,每次运行它,显示的随机数都是一 样的。这是因为在相同的编译平台环境下,由随机种子生成随机数的计算方法都 是一样的,再加上随机种子一样,所以产生的随机数就是一样的。2. 只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统 时钟(即定时/计数器的值)看下面这个C+程序:/ran d03.cppEin clude
14、<iostream>in clude <ctime>sing n amespace std;nt mai n()sran d( un sig ned)time(NULL); un sig ned int r = ran d();cout << r << en dl;return 0;这里用户和其他程序没有设定随机种子,则使用系统定时/计数器的值做为随 机种子,所以,在相同的平台环境下,编译生成 exe后,每次运行它,显示的随 机数会是伪随机数,即每次运行显示的结果会有不同。3. 建议:如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置
15、 一次随机种子。看下面这个用来生成一个随机字符串的C+程序:/ran d04.cpp#include<iostream>Kin clude<time.h>sing n amespace std;nt mai n()iin t rNum,m = 20;char *ch = new charm;for ( int i = 0; i < m; i+ )大家看到了,随机种子会随着for循环在程序中设置多次sran d( un sig ned)time(NULL);rNum = 1+(int)(rand()/(double)RAND_MAX)*36); / 求随机值swit
16、ch (rNum)case 1: chi='a'break ;case 2: chi='b'break ;case 3: chi='c'break ;case 4: chi='d'break ;case 5: chi='e'break ;case 6: chi='f;break ;case 7: chi='g'break ;case 8: chi='h'break ;case 9: chi='i'break ;case 10: chi='j'b
17、reak ;case 11: chi='k'break ;case 12: chi=T;break ;case 13: chi='m'break ;case 14: chi=' n'break ;case 15: chi='o'break ;case 16: chi='p' break ;case 17: chi='q' break ;case 18: chi='r' break ;case 19: chi='s' break ;case 20: chi='t
18、' break ;case 21: chi='u' break ;case 22: chi='v' break ;case 23: chi='w' break ;case 24: chi='x' break ;case 25: chi='y' break ;case 26: chi='z' break ;case 27:chi='0' break;case 28:chi='1' break;case 29:chi='2' break;case
19、30:chi=3; break;case 31:chi='4' break;case 32:chi='5' break;case 33:chi='6' break;case 34:chi='7' break;case 35:chi=8; break;case 36:chi=9; break;/end of switchcout << chi; /end of for loopcout << en dl;delete ch;return 0;而运行结果显示的随机字符串的每一个字符都是一样的,也就是说生成的字
20、符序列不随机,所以我们需要把srand(unsigned)time(NULL);从for循环中移出 放在for语句前面,这样可以生成随机的字符序列,而且每次运行生成的字符序 列会不同(呵呵,也有可能相同,不过出现这种情况的几率太小了)。如果你把srand(unsigned)time(NULL);改成srand(2)这样虽然在一次运行中 产生的字符序列是随机的,但是每次运行时产生的随机字符序列串是相同的。把 srand这一句从程序中去掉也是这样。此外,你可能会遇到这种情况,在使用timer控件编制程序的时候会发现用相同的时间间隔生成的一组随机数会显得有规律,而由用户按键comma nd事件产生的
21、一组随机数却显得比较随机,为什么?根据我们上面的分析,你可以很快 想出答案。这是因为timer是由计算机时钟记数器精确控制时间间隔的控件,时 间间隔相同,记数器前后的值之差相同,这样时钟取值就是呈线性规律的,所以随机种子是呈线性规律的,生成的随机数也是有规律的。而用户按键事件产生随 机数确实更呈现随机性,因为事件是由人按键引起的,而人不能保证严格的按键 时间间隔,即使严格地去做,也不可能完全精确做到,只要时间间隔相差一微秒, 记数器前后的值之差就不相同了,随机种子的变化就失去了线性规律,那么生成 的随机数就更没有规律了,所以这样生成的一组随机数更随机。 这让我想到了各 种晚会的抽奖程序,如果用
22、人来按键产生幸运观众的话, 那就会很好的实现随机 性原则,结果就会更公正。最后,我总结两个要点:1计算机的伪随机数是由随机种子根据一定的计算方法计算出来的数值。所 以,只要计算方法一定,随机种子一定,那么产生的随机数就是固定的。2只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时 钟用rand()和srand()产生伪随机数的方法总结 标准库cstdlib (被包含于iostream中)提供两个帮助生成伪随机数的函数:函数一: int rand(void);从 srand (seed中指定的 seed开始,返回一个seed, RAND_MAX (0x7fff)间的 随机整数。函
23、数二: void srand(unsigned seed;)参数seed是rand()的种子,用来初始化rand()的起始值。可以认为rand()在每次被调用的时候,它会查看:1) 如果用户在此之前调用过 srand(seed,) 给 seed 指定了一个值,那么它会自 动调用sran d(seed一次来初始化它的起始值。2) 如果用户在此之前没有调用过 srand(seed)它会自动调用srand(1一次。根据上面的第一点我们可以得出:1) 如果希望rand ()在每次程序运行时产生的值都不一样,必须给srand(seed) 中的seed 一个变值,这个变值必须在每次程序运行时都不一样(比如
24、到目前为 止流逝的时间)。2) 否则,如果给seed指定的是一个定值,那么每次程序运行时 rand ()产生 的值都会一样, 虽然这个值会是 seed, RAND_MAX(0x7fff) )之间的一个随机取 得的值。3) 如果在调用rand()之前没有调用过srand(seed)效果将和调用了 srand(1)再调 用rand()一样(1也是一个定值)。举几个例子,假设我们要取得 06之间的随机整数(不含6本身):例一,不指定 seed:for(int i=0;i<10;i+) ran_num=rand() % 6; cout<<ran_num<<" &
25、quot;每次运行都将输出: 5 5 4 4 5 4 0 0 4 2例二,指定seed为定值1: srand(1);for(int i=0;i<10;i+) ran_num=rand() % 6; cout<<ran_num<<" "每次运行都将输出: 5 5 4 4 5 4 0 0 4 2 跟例子一的结果完全一样。例三,指定seed为定值6:srand(6);for(int i=0;i<10;i+) ran_num=rand() % 6; cout<<ran_num<<" "每次运行都将输出:
26、 4 1 5 1 4 3 4 4 2 2随机值也是在0,6)之间,随得的值跟 srand(1)不同,但是每次运行的结果都相 同。例四,指定seed为当前系统流逝了的时间(单位为秒):time_t time(0):#include <ctime>srand(unsigned)time(0);for(int i=0;i<10;i+) ran_num=rand() % 6;cout<<ran_num<<" "第一次运行时输出: 0 1 5 4 5 0 2 3 4 2第二次: 3 2 3 0 3 5 5 2 2 3 总之,每次运行结果将不一
27、样,因为每次启动程序的时刻都不同(间隔须大于 1 秒?,见下)。关于 time_t time(0):time_t 被定义为长整型,它返回从 1970 年 1 月 1 日零时零分零秒到目前为止所 经过的时间,单位为秒。比如假设输出:cout<<time(0);值约为 1169174701,约等于 37(年)乘 365(天)乘 24(小时)乘 3600(秒) (月日没算)。另外,关于 ran_num = rand() % 6,将rand()的返回值与6求模是必须的,这样才能确保目的随机数落在 0,6)之间, 否则rand()的返回值本身可能是很巨大的。一个通用的公式是:要取得a,b)之
28、间的随机整数,使用(rand() % (b-a) + a (结果值将含a不含b)。 在a为0的情况下,简写为rand() % b。最后,关于伪随机浮点数:用rand() / double(RAND_MAX)可以取得01之间的浮点数(注意,不同于整型 时候的公式,是除以,不是求模) ,举例:double ran_numf=0.0; srand(unsigned)time(0);for(int i=0;i<10;i+)ran_numf = rand() / (double)(RAND_MAX); cout<<ran_numf<<" "运行结果为:0
29、.716636, 0.457725,等10个01之间的浮点数,每次结果都 不同。如果想取更大范围的随机浮点数,比如110,可以将rand() /(double)(RAND_MAX) 改为 rand() /(double)(RAND_MAX/10)运行结果为:7.19362, 6.45775,等10个110之间的浮点数,每次结果都不 同。至于 100, 1000 的情况,如此类推。以上不是伪随机浮点数最好的实现方法,不过可以将就着用用 问题1:怎样获得一个真正的随机数?要知道,ran d()是不能产生真正的随机数的 即使不能产生真正的随机数,也要大概接近呀!而rand()好象每次的随机都一样。专
30、家解答 :之所以rand()每次的随机数都一样是因为rand()函数使用不正确。各种编程语 言返回的随机数(确切地说是伪随机数) 实际上都是根据递推公式计算的一组数 值,当序列足够长, 这组数值近似满足均匀分布。 如果计算伪随机序列的初始数 值(称为种子)相同,则计算出来的伪随机序列就是完全相同的。这个特性被有 的软件利用于加密和解密。 加密时,可以用某个种子数生成一个伪随机序列并对 数据进行处理; 解密时,再利用种子数生成一个伪随机序列并对加密数据进行还 原。这样,对于不知道种子数的人要想解密就需要多费些事了。当然,这种完全 相同的序列对于你来说是非常糟糕的。 要解决这个问题, 需要在每次产生随机序 列前,先指定不同的种子, 这样计算出来的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 月嫂入户首日服务执行标准
- 环境风险物质储存管理办法
- 农资产品购销合同管理规范
- 身体机能检测标准化流程
- 门店运营成本控制细则案
- 蛋鸡光照管理技术方案
- 膝关节功能评估操作标准
- 健康知识推送计划指引
- 体质辨识评估标准化操作流程
- 安全生产红线意识教育方案
- 四川自贡高新国有资本投资运营集团有限公司招聘笔试题库2026
- 中国临床戒烟指南(2026年版)解读
- 【2026】年新高考英语(全国II卷)全真模拟试卷(含答案解析)
- 2026年亳州市辅警招聘考试备考试题及答案详解
- 2025北京中国机械总院集团物业中心怀柔分中心招聘1人笔试历年参考题库附带答案详解
- nccn临床实践指南:软组织肉瘤(2026.v2)解读课件
- 2026云南曲靖市商业银行股份有限公司招聘若干人考试备考题库及答案解析
- 2026年香精香料专业考试试题及答案
- 2026春小学信息科技四年级下册浙教版(新教材)教案(全册)
- 安宁疗护专科试题及答案
- 医疗器械生产奖罚制度
评论
0/150
提交评论