计算机中随机数的产生Word文档格式.docx
《计算机中随机数的产生Word文档格式.docx》由会员分享,可在线阅读,更多相关《计算机中随机数的产生Word文档格式.docx(10页珍藏版)》请在冰豆网上搜索。
}
voidrandom_start(void)
inttemp[2];
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
RAND_SEED=temp[0];
main()
unsignedinti,n;
random_start();
for(i=0;
i<
10;
i++)
printf("
%u\t"
random());
\n"
);
这个程序(rand01.c)完整地阐述了随机数产生的过程:
首先,主程序调用random_start()方法,random_start()方法中的这一句我很感兴趣:
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
这个函数用来移动内存数据,其中FP_SEG(farpointertosegment)是取temp数组段地址的函数,FP_OFF(farpointerto
offset)是取temp数组相对地址的函数,movedata函数的作用是把位于0040:
006CH存储单元中的双字放到数组temp的声明
的两个存储单元中。
这样可以通过temp数组把0040:
006CH处的一个16位的数送给RAND_SEED。
random用来根据随机种子RAND_SEED的值计算得出随机数,其中这一句:
是用来计算随机数的方法,随机数的计算方法在不同的计算机中是不同的,即使在相同的计算机中安装的不同的操作系统中
也是不同的。
我在linux和windows下分别试过,相同的随机种子在这两种操作系统中生成的随机数是不同的,这说明它们的
计算方法不同。
现在,我们明白随机种子是从哪儿获得的,而且知道随机数是怎样通过随机种子计算出来的了。
那么,随机种子为什么
要在内存的0040:
006CH处取?
0040:
006CH处存放的是什么?
学过《计算机组成原理与接口技术》这门课的人可能会记得在编制ROMBIOS时钟中断服务程序时会用到Intel8253定时/
计数器,它与Intel8259中断芯片的通信使得中断服务程序得以运转,主板每秒产生的18.2次中断正是处理器根据定时/记数
器值控制中断芯片产生的。
在我们计算机的主机板上都会有这样一个定时/记数器用来计算当前系统时间,每过一个时钟信号
周期都会使记数器加一,而这个记数器的值存放在哪儿呢?
没错,就在内存的0040:
006CH处,其实这一段内存空间是这样定
义的:
TIMER_LOWDW?
;
地址为0040:
006CH
TIMER_HIGHDW?
006EH
TIMER_OFTDB?
0070H
时钟中断服务程序中,每当TIMER_LOW转满时,此时,记数器也会转满,记数器的值归零,即TIMER_LOW处的16位二进制
归零,而TIMER_HIGH加一。
rand01.c中的
正是把TIMER_LOW和TIMER_HIGH两个16位二进制数放进temp数组,再送往RAND_SEED,从而获得了“随机种子”。
现在,可以确定的一点是,随机种子来自系统时钟,确切地说,是来自计算机主板上的定时/计数器在内存中的记数值。
这样,我们总结一下前面的分析,并讨论一下这些结论在程序中的应用:
1.随机数是由随机种子根据一定的计算方法计算出来的数值。
所以,只要计算方法一定,随机种子一定,那么产生的随机数
就不会变。
看下面这个C++程序:
//rand02.cpp
#include
usingnamespacestd;
intmain()
unsignedintseed=5;
srand(seed);
unsignedintr=rand();
cout<
//编辑者注:
可能代码有缺
在相同的平台环境下,编译生成exe后,每次运行它,显示的随机数都是一样的。
这是因为在相同的编译平台环境下,由随机
种子生成随机数的计算方法都是一样的,再加上随机种子一样,所以产生的随机数就是一样的。
2.只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟(即定时/计数器的值)
//rand03.cpp
srand((unsigned)time(NULL));
return0;
这里用户和其他程序没有设定随机种子,则使用系统定时/计数器的值做为随机种子,所以,在相同的平台环境下,编译生成
exe后,每次运行它,显示的随机数会是伪随机数,即每次运行显示的结果会有不同。
3.建议:
如果想在一个程序中生成随机数序列,需要至多在生成随机数之前设置一次随机种子。
看下面这个用来生成一个随机字符串的C++程序:
//rand04.cpp
intrNum,m=20;
char*ch=newchar[m];
for(inti=0;
i//大家看到了,随机种子会随着for循环在程序中设置多次
rNum=1+(int)((rand()/(double)RAND_MAX)*36);
//求随机值
switch(rNum){
case1:
ch[i]='
a'
;
break;
case2:
b'
case3:
c'
case4:
d'
case5:
e'
case6:
f'
case7:
g'
case8:
h'
case9:
i'
case10:
j'
case11:
k'
case12:
l'
case13:
m'
case14:
n'
case15:
o'
case16:
p'
case17:
q'
case18:
r'
case19:
s'
case20:
t'
case21:
u'
case22:
v'
case23:
w'
case24:
x'
case25:
y'
case26:
z'
case27:
ch[i]='
0'
break;
case28:
1'
case29:
2'
case30:
3'
case31:
4'
case32:
5'
case33:
6'
case34:
7'
case35:
8'
case36:
9'
}//endofswitch
}//endofforloop
而运行结果显示的随机字符串的每一个字符都是一样的,也就是说生成的字符序列不随机,所以我们需要把srand
((unsigned)time(NULL));
从for循环中移出放在for语句前面,这样可以生成随机的字符序列,而且每次运行生成的字符序列
会不同(呵呵,也有可能相同,不过出现这种情况的几率太小了)。
如果你把srand((unsigned)time(NULL));
改成srand
(2);
这样虽然在一次运行中产生的字符序列是随机的,但是每次运行时产生
的随机字符序列串是相同的。
把srand这一句从程序中去掉也是这样。
此外,你可能会遇到这种情况,在使用timer控件编制程序的时候会发现用相同的时间间隔生成的一组随机数会显得有规
律,而由用户按键command事件产生的一组随机数却显得比较随机,为什么?
根据我们上面的分析,你可以很快想出答案。
这是因为timer是由计算机时钟记数器精确控制时间间隔的控件,时间间隔相同,记数器前后的值之差相同,这样时钟取值就
是呈线性规律的,所以随机种子是呈线性规律的,生成的随机数也是有规律的。
而用户按键事件产生随机数确实更呈现随机
性,因为事件是由人按键引起的,而人不能保证严格的按键时间间隔,即使严格地去做,也不可能完全精确做到,只要时间
间隔相差一微秒,记数器前后的值之差就不相同了,随机种子的变化就失去了线性规律,那么生成的随机数就更没有规律了
,所以这样生成的一组随机数更随机。
这让我想到了各种晚会的抽奖程序,如果用人来按键产生幸运观众的话,那就会很好
的实现随机性原则,结果就会更公正。
最后,我总结两个要点:
1.计算机的伪随机数是由随机种子根据一定的计算方法计算出来的数值。
所以,只要计算方法一定,随机种子一定,那么产
生的随机数就是固定的。
2.只要用户或第三方不设置随机种子,那么在默认情况下随机种子来自系统时钟。
PS:
1.FromCSDN
//crt_rand_s.c
//Thisprogramillustrateshowtogeneraterandom
//integerorfloatingpointnumbersinaspecifiedrange.
//Rememberingtodefine_CRT_RAND_Sprior
//toinclusionstatement.
#define_CRT_RAND_S
#include<
stdlib.h>
stdio.h>
limits.h>
intmain(void)
inti;
unsignedintnumber;
doublemax=100.0;
errno_terr;
//Display10randomintegersintherange[1,10].
for(i=0;
i<
10;
i++)
{
err=rand_s(&
number);
if(err!
=0)
printf_s("
Therand_sfunctionfailed!
}
printf_s("
%u\n"
(unsignedint)((double)number/
(double)UINT_MAX*10.0)+1);
//Display10randomdoublesbetween0andmax.
for(i=0;
%g\n"
(double)number/
(double)UINT_MAX*max);
output:
10
4
5
2
8
6
1
32.6617
29.4471
11.5413
6.41924
20.711
60.2878
61.0094
20.1222
80.9192
65.0712
2.FromCSDN
//crt_rand.c
//Thisprogramseedstherandom-numbergenerator
//withthetime,thenexercisestherandfunction.
//
time.h>
voidSimpleRandDemo(intn)
//Printnrandomnumbers.
n;
i++)
printf("
%6d\n"
rand());
voidRangedRandDemo(intrange_min,intrange_max,intn)
//Generaterandomnumbersinthehalf-closedinterval
//[range_min,range_max).Inotherwords,
//range_min<
=randomnumber<
range_max
for(i=0;
intu=(double)rand()/(RAND_MAX+1)*(range_max-range_min)
+range_min;
u);
//Seedtherandom-numbergeneratorwiththecurrenttimesothat
//thenumberswillbedifferenteverytimewerun.
srand((unsigned)time(NULL));
SimpleRandDemo(10);
RangedRandDemo(-100,100,10);
22036
18330
11651
27464
18093
3284
11785
14686
11447
11285
74
48
27
65
96
64
-5
-42
-55
66