C++中的随机数


rand()函数

  rand()函数是从C语言继承过来的随机数函数,存在于标准库中。它会向我们返回一个0~RAND_MAX(32767)的整数,不需要参数。

  简单测试一下:

int main( ){
    for(int i=0;i<5;i++){
        cout<endl;
    }
}
/**
输出结果为:
41
18467
6334
26500
19169
**/

  好像确实输出了一串随机数,但事实上,无论何时何地去运行这段代码,得到的结果总是一样的。

随机数种子

  rand()函数的作用其实并不是生成随机数,而是根据种子生成一个序列,它的逻辑是这样的:

  规定一个函数f,令a1=f(seed),an+1=f(an)。如此下去,就得到了一个数字序列,在程序里每调用一次rand()函数,就返回这个序列中的下一个数。只要函数f的计算方式足够复杂(事实上rand()函数的计算原理确实比较复杂),那么表现出来就是随机的。由于这个数列的首项是对种子seed的运算结果,所以只要种子相同,得到的数列也是一样的,这也能解释为什么每次调用rand()得到的第一个随机数都是41,因为种子默认为1。

   事实上,我们知道计算机系统都是按照指令来行动的,而处理这些指令的逻辑都是既定的。因此,在计算机系统内部,完全的真随机数是不存在的,想要实现真随机数,就必须引入外部变量。例如著名的随机数网站random.org就是通过引入大气噪声这种无法预测的外部变量来实现真随机数的。

srand()函数

  既然随机数是由种子经过既定的算法模拟的,因此我们只需要在每次运行程序前更新种子就能使每次运行程序得到不同的随机数序列了。srand()函数就是用来设置rand()函数的种子的,根据不同的参数产生不同的种子。因此只需要每次运行程序时给srand()函数传入不同的参数就行了,这个参数我们一般使用时间戳(timestamp),只要不在一秒内运行两次程序,得到的随机数序列就是不同的。srand()函数只需要在程序里设置一次就可以了。

time(NULL);函数会返回1970年1月1日至今所经历的时间(以秒为单位),需要引入头文件

#include 
#include 

using namespace std;

int main( ){
    srand(time(NULL));
    for(int i=0;i<5;i++){
        cout<endl;
    }
}

虽然连续运行程序,得到的序列很接近,但经过我们的四则和取余运算后,确实是能满足绝大部分的需求了。

一些常见用法

  其实这个属于数学运算了:

1、如果你要产生0~99这100个整数中的一个随机整数,可以表达为:int num = rand() % 100; 

     这样,num的值就是一个0~99中的一个随机数了。

2、如果要产生1~100,则是这样:int num = rand() % 100 + 1;  

3、总结来说,可以表示为:int num = rand() % n +a;

     其中的a是起始值,n-1+a是终止值,n是整数的范围。

4、一般性:rand() % (b-a+1)+ a ;    就表示  a~b 之间的一个随机整数。

5、若要产生0~1之间的小数,则可以先取得0~10的整数,然后均除以10即可得到“随机到十分位”的10个随机小数。

     若要得到“随机到百分位”的随机小数,则需要先得到0~100的10个整数,然后均除以100,其它情况依 此类推。

 

随机数库(Random Number Library)

  从上面所述,rand()函数使用笨拙且原始。C++既然引入了面向对象的特性,自然也要对随机数做封装,于是在C++11中,随机数库诞生了。

  主要用到的是两个类:随机数引擎类(random-number engines)、随机数分布类(random-number distribution)。

#include 
#include 
#include 

using namespace std;

int main( ){
    //设置随机数引擎
    default_random_engine e;
    /**
     * 为引擎设置随机数种子
     * 也可以写default_random_engine e(time(0))
     */
    e.seed(time(0));
    //生成0到9之间均匀分布的随机无符号整数
    uniform_int_distribution u(0,9);
    for(int i=0;i<9;i++) {
        /**
         * u作为随机数源
         * e生成的随机数是在u定义的范围内并服从均匀分布的
         */
        cout << u(e) << endl;
    }
}

从代码上看,似乎和rand()也没多少区别,甚至变得更复杂了。其实随机数库厉害的地方不在引擎,而在分布。除了生成上面的均匀分布,C++ 11 还规定了可以生成 20 种不同的分布类型,比如 均匀分布uniform,正态分布normal,二项分布binomial,泊松分布poisson,学生分布 student 等等,相关函数可以查看相应的文档。