分类: C/C++
2009-04-09 20:58:10
在计算机中并没有一个真正的随机数发生器,但是可以做到使产生的数字重复率很低,这样看起来好象是真正的随机数,实现这一功能的程序叫伪随机数发生器。
有关如何产生随机数的理论有许多,如果要详细地讨论,需要厚厚的一本书的篇幅。不管用什么方法实现随机数发生器,都必须给它提供一个名为“种子”的初始值。而且这个值最好是随机的,或者至少这个值是伪随机的。“种子”的值通常是用快速计数寄存器或移位寄存器来生成的。
下面讲一讲在c语言里所提供的随机数发生器的用法。现在的c编译器都提供了一个基于ansi标准的伪随机数发生器函数,用来生成随机数。它们就是rand()和srand()函数。这二个函数的工作过程如下:
1) 首先给srand()提供一个种子,它是一个unsigned int类型,其取值范围从0~65535;
2) 然后调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到32767之间)
3) 根据需要多次调用rand(),从而不间断地得到新的随机数;
4) 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。
这个过程看起来很简单,问题是如果你每次调用srand()时都提供相同的种子值,那么,你将会得到相同的随机数序列,这时看到的现象是没有随机
数,而每一次的数都是一样的了。例如,在以17为种子值调用srand()之后,在首次调用rand()时,得到随机数94。在第二次和第三次调用
rand()时将分别得到26602和30017,这些数看上去是很随机的(尽管这只是一个很小的数据点集合),但是,在你再次以17为种子值调用
srand()后,在对于rand()的前三次调用中,所得的返回值仍然是在对94,26602,30017,并且此后得到的返回值仍然是在对
rand()的第一批调用中所得到的其余的返回值。因此只有再次给srand()提供一个随机的种子值,才能再次得到一个随机数。
下面的例子用一种简单而有效的方法来产生一个相当随机的“种子”值----当天的时间值:
#include
#include
#include
#include
void main(void)
{
int i;
unsigned int seedval;
struct timeb timebuf;
ftime(!amp;timebuf);
seedval=((((unsigned int)timebuf.time!amp;0xffff)+
(unsigned int)timebuf.millitm)^
(unsigned int)timebuf.millitm);
srand((unsigned int)seedval);
for(i=0;i<10;++i)
printf("%6d\n",rand());
}
上
面的程序先是调用_ftime()来检查当前时间,并把它的值存入结构成员timebuf.time中,当前时间的值从1970年1月1日开始以秒计算。
在调用了_ftime()之后,在结构timebuf的成员millitm中还存入了当前那一秒已经度过的毫秒数,但在dos中这个数字实际上是以百分之
一秒来计算的。然后,把毫秒数和秒数相加,再和毫秒数进行异或运算。当然也可以对这两个结构成员进行更多的计算,以控制seedval的取值范围,并进一
步加强它的随机性,但上例用的逻辑运算就已经足够了。
注意上例中rand()的输出并没有被限制在一个指定的范围,假定要建立一个彩票选号器,其
取值范围是从1到44。可以简单地忽略掉rand()所输出的在该范围外的值,但这将花费许多时间去得到所需的全部(例如6个)彩票号码。假如你已经建立
了一个随机数发生器,它所产生的随机数范围是从0到32767,而你想把输出限制在1到44之间。下面的例子就说明了如何来完成这项工作:
int i,k range;
int min,max;
double j;
min=1;
max=44;
range=max-min;
i=rand();
j=((oduble)i/(double)rand_max);
i=(int)(j*(double)range);
i+=min;
这个例子把输出的随机数限制在1到44之间,其工作原理如下:
1)得到一个在0到rand_max(32767)之间的随机数,把它除以rand_max,从而产生一个在0到1之间的校正值;
2)把校正值乘以所需要的范围值(在本例中为43,即44-1)从而产生一个在0到43之间的值
3) 把该值和所要求的最小值相加,从而使该值最终落在正确的取值范围----1到44之间。你可以用不同的min和max值来验证这个例子,你会发现它总是会正确地产生在新的min和max值之间的随机数。
**************************************************************************
在C语言中怎么产生随机数呢?
rand()函数可以用来产生随机数,但是这不是真真意义上的随机数,是一个伪随机数,是根据一个数,我们可
以称它为种了,为基准以某个递推公式推算出来的一系数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机
正常开机后,这个种子的值是定了的,除非你破坏了系统,为了改变这个种子的值,C提供了 srand()函数,它的原形是void srand(
int a).
初始化随机产生器既rand()函数的初始值,即使把种子的值改成a;
从这你可以看到通过sand()函数,我们是可以产生可以预见的随机序列,那我们如何才能产生不可预见的随机序列呢?我们可能常常需要这样的随机序列,是
吧。利用srand((unsign)(time(NULL))是一种方法,因为每一次运行程序的时间是不同的.
下面讲一讲在C语言里所提供的随机数发生器的用法。现在的C编译器都提供了一个基于ANSI标准的伪随机数发生器函数,用来生成随机数。它们就是rand()和srand()函数。这二个函数的工作过程如下:
1) 首先给srand()提供一个种子,它是一个unsigned int类型,其取值范围从0~65535;
2) 然后调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到32767之间)
3) 根据需要多次调用rand(),从而不间断地得到新的随机数;
4) 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。
以下是一个产生随机数的例子:
需要首先使用随机数“种子”初始化,srand函数:
#i nclude
#i nclude
#i nclude
void main( void )
{
int i;
srand( (unsigned)time( NULL ) );//初始化随机数
/* 打印10个随机数. */
for( i = 0; i < 10;i++ )
printf( " %d\n", rand() );
}
在C语言中,是如何取得这个随机数的?
一个语句:rand();
它的作用就是随机取0到RAND_MAX之间的任何数。ANSI标准指出,RAND_MAX值的范围到少是32767,也就是双字节整数的最大值。
下面我们用一个例子来取得从1到6的随机整数:
#include
#include
main()
{
int i;
for(i=1;i<=20;i++)
printf("%10d",1+(rand()%6));
}
第2行的stdlib.h是rand()函数的头文件,第7行就是 实现功能的语句了。前面我们以经说过函数取的值是介于0到RANDMAX之前 的,而我们所要的是1到6之间的整数。用脚想一想就知道:任一整数除以6所得的余数是0~5之间的整数,再加一之后就是1到6了,也就是我们的目标。利用 这个原理我们使用%号对随机数进行了缩放。
OK,运行两次程序:
一:6 6 5 5 6 5 1 1 5 3 6 6 2 4 2 6 2 3 4 1
二:6 6 5 5 6 5 1 1 5 3 6 6 2 4 2 6 2 3 4 1
发现:两次运行的结果完全一样。
结论:这算什么随机数。
具有讽刺意味的是,这种重复性是rand的一个重要特点,在调试程序时,这种重复性是必不可少的,因为它可以证明对程序的修改能够正常运行。
实际上,rand函数产生的是伪随机数。但我们需要的是随机数。于是,我们需要对程序进行随机化,这需要使用标准库函数srand来实现。函数srand需要一个无符号的整型参数,在每次程序执行时用函数rand去生在一组不同的随机数。如下例:
#include
#include
main()
{
int i;
unsignde seed;
printf("Enter a seed:");
scanf("%u",&seed);
srand(seed);
for( i = 1;i<=20;i++)
printf(""%10d",1+(rand()%6));
}
程序的运行结果是当我们输入不同的SEED时,产生不同组的随机数。这句话的意思有两个:一:当输入相同的SEED时,产生的同组随机数;二:每次运行我们都要输入一个SEED。
如果我们希望不用每次输入SEED值,而且每次运行时SEED值都是不同的,我们可以用下面语句代替上面的第三块的三个语句:
srand(time(NULL));
这会使计算机自动读取自己的时钟以获得SEED值。于是我们就获值了每次运行都会改变的SEED,同样也就获得了真正的随机数