C++随机数解析

来源:互联网 发布:网络弱电工程师 编辑:程序博客网 时间:2024/06/05 10:15

深度解读如何生成大范围随机数

前几天在做毕业设计的时候需要用到随机数,发现随机数并不“随机”。Rand生成的都是伪随机数。

随机数介绍

先来看看rand的函数原型及用法

#include <stdlib.h> 

int rand( void );

功能: 函数返回一个在零到RAND_MAX 之间的伪随机整数。

例如:

srand( time(NULL) );   

for( i = 0; i < 10; i++ )     

printf( "Random number#%d: %d\n", i, rand() );

RAND_MAX在不同的平台下定义的大小不一样 windows 定义的为0X7FFF。

与rand()配套使用的还有另外一个函数 srand()。

#include <stdlib.h>

void srand( unsigned seed );

功能: 设置rand()随机序列种子。对于给定的种子seed,rand()会反复产生特定的随机序列。   

srand( time(NULL) );   

for( i = 0; i < 10; i++ )    

printf( "Random number#%d: %d\n", i, rand() );

解释:

srand就是为rand产生一个初始值,若不手动调用srand系统也会自动调用srand(1),这样每次产生的随机数就都一样了。程序第一次执行“随机数”产生顺序为5,8,2,5,0,1…,第二次执行仍然是5,8,2,5,0,1…,第三次执行仍然是5,8,2,5,0,1…若我们把时间做为种子的话每次生成的“随机数”就不一样了。

在整个程序中srand()只需要运行一次就够了,没有必要在每次运行的时候都要调用,若调用的时间间隔太近的话会造成设定的种子seed全部一样导致产生的随机数也都一样。

基于以上几个特性我们称rand产生的随机数为“伪随机数”。

随机数数内部是如何实现的

rand的内部实现是用线性同余法做的,他不是真的随机数,只不过是因为其周期特别长,所以有一定的范围里可看成是随机的,rand()会返回一随机数值,范围在0至RAND_MAX间。

这些是数学上的内容我们不做讨论。

如何产生大范围的随机数

rand()函数产生的随机数范围比较小只能是0x0000~0x7FFF之间的整数随机数即2^15以内的数,我们在实际的应用中经常需要用到大范围的随机数例如会用到2^32 以内的随机数。显然rand已经无法满足我们的要求了。

这时候就要想办法对随机数做扩展,C++标准库提供了标准的扩展方法RAND_MAX*rand() + rand();这样就在[0, RAND_MAX*RAND_MAX)做到了随机。这种方法有缺点就是容易产生数的“两端聚集”现象。但是rand*rand和rand+rand的方法做不到随机数的扩展只会使得出的随机数产生“二次聚集”现象。

有没有一种更好的扩展方法呢?

有利用计算机的位偏移和位运算可以实现随机数的更好扩展。

unsigned long ulrand(void)

{

    return (

     (((unsignedlong)rand()<<24)&0xFF000000ul)

    |(((unsignedlong)rand()<<12)&0x00FFF000ul)

    |(((unsigned long)rand()    )&0x00000FFFul));

}

关于位运算我就不做过多的解释。需要注意的一点是rand返回的随机数最高位始终为0,并不随机,因此不能参与随机数扩展的二进制运算。

负随机数

rand()-RAND_MAX/2这样就产生了[-RAND_MAX,RAND_MAX]之间的随机数。负随机数的扩展同上。

随机小数

rand()/RAND_MAX 产生[0,1)之间的小数。

rand()/(RAND_MAX-1) 产生[0,1]之间的小数。

       倍率* 和 +起始值,可以合成我们想要的随机数。

随机数终极利器Boost随机库

rand函数产生的随机数随机性并不够强,范围不够大。我们要找另外一种更加强大的随机数数生成函数。

Boost随机库中利用了MersenneTwister算法,可以快速产生高质量的伪随机数Mersenne Twister有以下优点:随机性好,在计算机上容易实现,占用内存较少(mt19937的C程式码执行仅需624个字的工作区域),与其它已使用的伪随机数发生器相比,产生随机数的速度快、周期长,可达到2^19937-1,且具有623维均匀分布的性质,对于一般的应用来说,足够大了,序列关联比较小,能通过很多随机性测试。

voidtest_mt19937()

{

    // 以时间为种子创建一个随机数发生器

    boost::mt19937 rng(time(0));

    for (int i = 0; i < 100; ++i)

    {

       std::cout << rng() <<std::endl;

    }

}

 

不做过多解释,boost的强大有点反人类了!

0 0
原创粉丝点击