Rand-C++随机数

random

  C++随机数发生器。

Update

  c++11中提供了一种新的产生随机数的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <random>

int main(){
std::random_device device;
std::mt19937 generator(device());
std::uniform_int_distribution<int> distribution(1, 10); // random integer number bewteen [1, 10]
//std::uniform_real_distribution<> distribution(0, 1); // random real number bewteen [0, 1]

for (uint8_t i = 0; i < 10; i++){
std::cout << distribution(generator) << ' ';
}
std::cout << std::endl;
}

参考链接

Generating random numbers in C++


以下为原答案


rand()

  生成随机整数的核心函数是cstdlib中的rand(),它生成一个闭区间 [0, RAND_MAX] 内的均匀随机数(均匀的含义是:该区间内每个整数被随机获取的概率相同),其中RAND_MAX是定义在cstdlib中的一个宏变量,在不同环境下的值可能不同,但是保证至少为$32767(2^{15} - 1)$。
  严格地说,这里的随机数是“伪随机数”,因为它也是由数学公式计算出来的,不过在算法领域,多数情况下可以把它当作真正的随机数。

产生[0, n]之间的整数

  可以用rand() % n的方式产生区间 $[0, n - 1]$ 内的一个随机整数。但是一方面这会破坏产生的整数分布的均匀性,另一方面只要n大于 RAND_MAX这种方法就不能得到期望的结果。由于RAND_MAX可能只有 32767 这么小,所以在使用这种方法的时候要小心。
  另一个方法是执行rand()后先除以RAND_MAX,得到 $[0, 1]$ 之间的随机实数,扩大n倍后四舍五入,得到 $[0, n]$ 之间的均匀整数。

具体用法

  需要随机数的程序在最开始时一般会执行一次srand(time(NULL)),目的是初始化“随机数种子”。简单来说,种子是生成随机数的计算依据。种子相同,计算出来的“随机数”序列总是相同。譬如运行下面的程序:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <cstdlib>

using namespace std;

int main(){
srand(100);
for(int i = 0; i < 3; i++){
cout << rand() << endl;
}
return 0;
}

  可以得到这样的一组随机数:

1
2
3
677741240
611911301
516687479

  但是当我们第二次运行这段代码的时候,我们会得到相同的一组随机数,原因就在于,我们每次运行时srand函数中初始化的种子始终是100
  而当我们使用srand(time(NULL))来替代上面的srand(100)时,由于time()函数会返回系统当前时间,所以每次运行程序时这个值都会不一样,也就使得每次运行生成的随机数序列不同。
  如果不调用srand而直接使用rand(),相当于调用过一次srand(1)
  另外,不要在同一个程序每次生成随机数之前都重新调用一次srand。譬如运行下面的程序:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <cstdlib>

using namespace std;

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

  会发现生成的三个随机数是相同的(随机数的具体值因人而异):

1
2
3
805428961
805428961
805428961

  仔细思考一下就很可以分析出原因:生成随机数的序列是由“种子”决定的,在这段代码里的种子就是time(NULL)的返回值了,也就是系统当前时间(准确来说是自UTC时间1970年1月1日0点依赖经过的“秒数”),它每秒变化一次,但是程序在上面三次运行的时间间隔过短,应该毫秒甚至微秒级的,因此上面三个随机数是同一个种子生成的,而且因为每次都被重新初始化,所以都是同一个随机数序列的第一个值。

  “同一套随机数”可能是好事也可能是坏事。例如,若要反复测试程序对不同随机数据的响应,需要每次得到的随机数不同。另一方面,如果发现某一程序对于一组随机数据报错,就需要在调试时“重现”这组数据。另外,不同的编译器计算随机数的方法可能不同。如果是不同编译器编译出来的程序,即使是用相同参数调用srand(),也可能的都不同的随机序列。

参考

  主要参考了刘汝佳老师的《算法竞赛入门经典》(第2版)第五章中5.2.6-测试STL一节中的内容。