C库<cstdlib>中的rand()可以很方便地生成随机数但也存在一些弊端,C++11标准引入了头文件<random>来提供更加完备地随机数功能。主要分为两个部分:随机数引擎(random-number engines)随机数分布类(random-number distribution)其中,一个引擎类可以生成 unsigned 随机数列,一个分布使用一个引擎类生成指定类型的,在给定范围内的,服从指定概率分布的随机数。

随机数引擎

random_device

标准库提供了一个真随机数生成设备random_device,在UNIX和LINUX中是真随机数,在Windows中使用的是操作系统来生成加密安全的伪随机数。由于各种原因,我们通常使用random_device来产生随机数种子,而不是直接用其产生随机数。

常用随机数算法模板

  • linear_congruential_engine线性同余法,这种速度最快、最常用
  • mersenne_twister_engine梅森旋转法,这种生成的随机数质量比较高
  • substract_with_carry_engine滞后Fibonacci

参考C++ Reference如果自己实例化模版类很麻烦,需要很强的数序知识,所以有几个常用的几个模版实例化生成器。

实例化的生成器

  • default_random_engine
    它是一个实例化的类。之所以不归入那三种算法,是因为它的实现是由编译器厂家决定的,有的可能用linear_congruential_engine实现,有的可能用mersenne_twister_engine实现。这种现象在C/C中见多了。不过,对于其他的类,C11是有明确规定用哪种算法和参数实现的。

线性同余法

  • minstd_rand()
  • minstd_rand0

利用适配器变种后

  • knuth_b: minstd_rand0 with shuffle_order_engine

梅森旋转法

  • mt19937
  • mt19937_64

滞后Fibonacci法

  • ranlux24_base
  • ranlux48_base

利用适配器变种后的滞后Fibonacci法:

  • ranlux24: ranlux24_base with discard_block_engine
  • ranlux48: ranlux48_base with discard_block_engine

三个适配器

discard_block_engine shuffle_order_engine independent_bits_engine

随机数分布模板

均匀分布:

  • uniform_int_distribution 整数均匀分布
  • uniform_real_distribution 浮点数均匀分布

注意,uniform_int_distribution的随机数的范围不是半开范围[ ),而是[ ],对于uniform_real_distribution却是半开范围[ )

伯努利类型分布:(仅有yes/no两种结果,概率一个p,一个1-p)

  • bernoulli_distribution伯努利分布
  • binomial_distribution 二项分布
  • geometry_distribution 几何分布
  • negative_biomial_distribution 负二项分布

Rate-based distributions:

  • poisson_distribution 泊松分布
  • exponential_distribution指数分布
  • gamma_distribution 伽马分布
  • weibull_distribution 威布尔分布
  • extreme_value_distribution 极值分布

正态分布相关:

  • normal_distribution 正态分布
  • chi_squared_distribution卡方分布
  • cauchy_distribution 柯西分布
  • fisher_f_distribution 费歇尔F分布
  • student_t_distribution t分布

分段分布相关:

  • discrete_distribution离散分布
  • piecewise_constant_distribution分段常数分布
  • piecewise_linear_distribution分段线性分布

这些模板类都是定义好了的、可以直接使用的。

使用方法实例

1
2
3
4
5
6
7
8
#include <iostream>
#include <random>
int main() {
std::default_random_engine e;
for (size_t i = 0; i < 10; i++) {
std::cout << e() << std::endl;
}
}

直接使用default_random_engine作为生成器产生随机数,通过多次运行这段程序我们发现生成的随机数都是一样的,随机数发生器会生成相同的随机数序列,这种特性在调试程序的时候可能会非常有用,但如果想要生成完全随机的序列,我们需要在初始化生成器的时候指定一个随机数种子。可以通过random_device来获得一个真随机数种子。

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

int main() {
- std::default_random_engine e;
+ std::random_device seed;
+ std::default_random_engine e(seed());
for (size_t i = 0; i < 10; i++) {
std::cout << e() << std::endl;
}
}

当需要特定随机数分布模板的时候,可以通过随机数分布模板(生成器引擎)的方式获得。

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

int main() {
std::random_device seed;
std::default_random_engine e(seed());
+ std::uniform_int_distribution<unsigned> u(0,9);//[0,9] 闭区间
for (size_t i = 0; i < 10; i++) {
- std::cout << e() << std::endl;
+ std::cout << u(e) << std::endl;
}
}

当我们需要在函数调用中获得不同的随机数,我们可以通过把生成器和分布模板设置为static来保存状态,这样每次调用的时候都能获得下一个随机数。