您的位置:首页 > 其它

随机数生成问题小结

2012-03-07 12:10 393 查看
随机数在很多程序都会使用,最常用的生成随机数的方法就是c的标准函数库提供的随机数生成器rand(定义在stdlib.h中),能返回0-RAND_MAX之间均匀分布的伪随机整数(RAND_MAX至少为32767,一般都默认为32767)。若直接调用rand(),每次运行生成的随机数都是相同的,这是因为rand()在生成伪随机数时需要一个种子(种子默认值是1),作为计算伪随机数的初始值,如果种子相同,生成的随机数也就相同。这个特点会被有的软件用来进行加密和解密。加密时,使用某个种子数生成一个伪随机序列对数据进行处理;解密时,再利用相同的种子生成相同的为随机序列对加密数据进行还原。这样,对于一个不知道种子的人要想解密就需要多费些事了。

当然,这样完全相同的伪随机序列对我们的程序来说是非常糟糕的。要解决这个问题,需要在每次产生随机序列前,先指定不同的种子值。而srand()函数就是用来设置rand()产生伪随机数时的种子。这两个函数的工作过程如下:

1) 首先给srand()提供一个种子,它是一个unsigned int类型,其取值范围从0~65535;

2) 然后调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到32767之间)

3) 根据需要多次调用rand(),从而不间断地得到新的随机数;

4) 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。

可以人为指定种子数值,但最常用的方法是使用系统时间最为种子,具体方法是
srand((unsigned int)time(NULL));
rand();
time()(在time.h中定义)用于获取当前的系统时间,返回一个time_t类型的大整数,其值表示1970年1月1日00:00:00到当前时刻的秒数。

注意,当需要生成多个随机数时,要将srand()放到循环外面,否则每次产生一个随机数时都调用srand(),会因为计算机运行速度太快,每次time值都是相同的。

******************************************************************************************************

这样生成的是0~RAND_MAX之间的随机整数,但很多情况下我们需要的是某个范围(比如x~y)之间的随机数。常见的方法有两种:
方法一:(int)((double)rand()/(double)RAND_MAX*(y-x))+x
方法二:rand()%(y-x)+x


其中方法一能使获得x~y之间数值的概率更均匀,但由于是浮点运算,复杂性更高;而方法二运算更快捷,但会使前RAND_MAX%(y-x)个数的概率略大。并且考虑到直接调用rand()的场合,对精度要求不是太高,所以通常使用第二种方法。(若需自己做随机数生成器,通用的方式是线性同余方法,参考《计算机程序设计艺术》(第二卷)。自己还没看过,找机会看一下。)

*****************************************************************************************************
关于生成随机数的问题,很常见的还有“产生不重复的随机数”(参考《编程珠玑》第一章问题3)。对这个问题,查找相关资料后总结了三种思路:第一种,每产生一个随机数,都与前面的数比较,如果重复,就去掉这个数,重新产生;第二种,将一个数组初始化为随机数范围内不重复的所有数,每产生一个随机数就将表内的数据删掉,下一次从剩余的数组中产生新的随机数;第三种,将一个数组初始化为随机数范围内不重复的所有的数,使用某种方法,将该数组中元素的顺序打乱,然后从前面取需要个数的随机数。
思路一实现:
srand((unsigned)time(NULL));
for (j=0;j<m;j++)
{
a[j]=rand()%n;
for(i=0;i<j;i++)
{
if(a[i]==a[j])
{
j-=1;
break;
}
}
}
这是一种最容易想到的方法,但比较的次数呈线性增长,越到后面比较次数越多,并且重复的可能性越高。
思路二实现:
int a
={0},b[m];            //a
全部初始化为0
srand((unsigned)time(NULL));
for (i=0;i<m;i++)
{
while(a[x=rand()%n]);     //新生成的随机数已出现过,重新生成
b[i]=x;
a[x]=1;                   //新生成的随机数对应的标志位变成1
}


在小规模输入下,这种方法是不错,但某些情况下也有问题,当一个范围内大部分的数已经生成过(标志位为1),再生成一个新的随机数,可能在while循环处浪费非常多的时间,这跟方法一一样,越到后面,生成随机数重复的可能性越高,生成一个新的随机数的代价也就越大。

思路三实现:
for (i=0;i<n;i++)
a[i]=i+1;                    //初始化为小于n的所有数
srand((unsigned)time(NULL));
for (i=0;i<m;i++)                //将数组中元素的顺序打乱,前m个数即为需要的随机数
{
w=rand()%(n-i)+i;
t=a[i];
a[i]=a[w];
a[w]=t;
}
当数值范围比较小,并且范围数组与目标数组的元素个数相近时,上述算法非常有效,若目标数组元素个数远小于数值范围,这种方法浪费太多额外的空间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: