菜鸟笔记
提升您的技术认知

C++随机数生成

一、伪随机数生成

1. rand()

 rand()会返回一个随机数值,范围是在0至RAND_MAX间。RAND_MAX定义在stdlib.h头文件中,其值为2147483647。

2. srand()

 srand()可以用来设置rand()产生随机数时的随机数种子。通过设置不同的种子,可以获取不同的随机数序列。例如,可以利用srand(time(nullptr))的方式,根据系统时钟,产生不同的随机数种子。

3. 测试代码

#include <iostream>
#include <vector>
#include <ctime>

int main(int argc, char** argv){
  
	// 生成rand()的随机数种子
	srand(time(nullptr));
	for (int i = 0; i < 100; ++i){
  
		// 用rand()产生随机数
		std::cout << rand() << std::endl;
		std::cout << rand()/(RAND_MAX * 1.f) << std::endl;
	}
	return 0;
}

备注: 随机数生成的范围:
  1) [0, n),即rand() % n;
  2) [a, b),即rand() % (b - a) + a;
  3) [a, b],即rand() % (b - a + 1) + a;
  4) (a, b],即rand() % (b - a) + a + 1。
  5) a + rand() % n,[a, a + n);
  6) [0 , 1)之间的浮点数,rand()/(RAND_MAX * 1.f)

二、C++11随机数生成

1. 随机数生成算法

 相对于rand()和srand()生成伪随机数的方式来说,C++11的随机数生成器更加复杂,主要是因为在C++11中提供了比较多的选择方式
 C++11随机数生成方式有:
  1) linear_congruential_engine 线性同余随机数引擎
  2) mersenne_twister_engine 梅森旋转随机数引擎
  3) subtract_with_carry_engine 带进位随机数引擎
 上面的三种随机数生成算法均是模板类,需要我们自己进行实例化;模板类实例化需要的参数,均是算法中使用的参数,若是不懂其原理,建议不要使用这些模板类,因为我们不清楚用什么样的参数才能得到较好的随机序列。不过不用担心,在C++11标准中,已经帮我们预先定义了一些随机数类,它们都是用过上面的三个类模板实例化出来的。

类名称 属性 依赖类
linear_congruential_engine templates \
mersenne_twister_engine templates \
subtract_with_carry_engine templates \
discard_block_engine Engine adaptors \
independent_bits_engine Engine adaptors \
shuffle_order_engine Engine adaptors \
default_random_engine instantiations 待定
minstd_rand instantiations linear_congruential_engine
minstd_rand0 instantiations linear_congruential_engine
mt19937 instantiations mersenne_twister_engine
mt19937_64 instantiations mersenne_twister_engine
ranlux24_base instantiations subtract_with_carry_engine
ranlux48_base instantiations subtract_with_carry_engine
ranlux24 instantiations ranlux24_base、discard_block_engine
ranlux48 instantiations ranlux48_base、discard_block_engine
knuth_b instantiations shuffle_order_engine

 从上表中可以看出,在依赖类(第三列)中有一个”待定“,它的类是default_random_engine;它也是一个实例化的类。它的实现会和编译器有很大的关系,有的编译可能会用linear_congruential_engine,也可能会用mersenne_twister_engine。代码如下所示:

#include <iostream>
#include <random>

int main(int argc, char**argv){
  
	std::default_random_engine engine;
    for (int i = 0; i < 10; ++i ){
  
        std::cout << engine() << " ";
    }
    std::cout << std::endl;
	return 0;
}

2. 随机数分布类型

  1. 均匀分布
     uniform_int_distribution 整数均匀分布
     uniform_real_distribution 浮点数均匀分布
  2. 伯努利类型分布
     bernoulli_distribution 伯努利分布
     binomial_distribution 二项分布
     geometric_distribution 几何分布
     negative_binomial_distribution 负二项分布
  3. Rate-based 分布
     poisson_distribution 泊松分布
     exponential_distribution 指数分布
     gamma_distribution 伽马分布
     weibull_distribution 威布尔分布
     extreme_value_distribution 极值分布
  4. 正态分布
     normal_distribution 正态分布
     lognormal_distribution 对数正态分布
     chi_squared_distribution 卡方分布
     cauchy_distribution 柯西分布
     fisher_f_distribution 费歇尔F分布
     student_t_distribution t分布
  5. 分段分布
     discrete_distribution 离散分布
     piecewise_constant_distribution 分段常数分布
     piecewise_linear_distribution 分段线性分布
  6. 其它
     seed_seq 种子序列
     generate_canonical 生成归一化
#include <iostream>
#include <random>
#include <ctime>

int main(int argc, char**argv){
  
	const int nrolls = 10000;
    const int nstars = 100;
    std::minstd_rand engine1(time(nullptr));
    int param = 10;
    std::poisson_distribution<int> distribution(param);
    int p[20] = {
  };
    for(int i = 0; i < nrolls; ++i){
  
        int tmp = distribution(engine1);
        if (20 > tmp) {
  
            ++p[tmp];
        }
    }

    std::cout << "poisson_distribution param: " << param << std::endl;
    for (int i = 0; i < 20; ++i){
  
        std::cout << i << ": " << std::string(p[i] * nstars/nrolls, '*') << std::endl;
    }
	return 0;
}


result:
poisson_distribution param: 10
0: 
1: 
2: 
3: 
4: *
5: ***
6: ******
7: ********
8: ***********
9: ************
10: ************
11: ***********
12: **********
13: *******
14: *****
15: ***
16: **
17: *
18: 
19: 

3. random_device

 random_device是标准库提供的一个非确定性随机数生成设备,是所有生成器中唯一一个不需要随机数种子的方式。在Linux中,是需要读取/dev/urandom设备。需要注意的是random_device在某些操作系统中是无法使用的,会在构造函数或者调用operator()函数时抛出异常,因此在进行代码移植时,需要格外注意。

// random_device 说明
// random_device::min() --- 范围最小值
// random_device::max() --- 范围最大值
// operator() --- 生成一个随机数
// entropy() --- 计算operator()调用生成的随机数的熵,如果random库使用随机数引擎(即伪随机数算法)实现而不是真随机数生成器,那么该值为0
#include <iostream>
#include <random>
#include <ctime>

int main(int argc, char**argv){
  
std::random_device rd;
    std::cout << "range: " << rd.min() << '\t' << rd.max() << std::endl;
    std::cout << "random1: " << rd() << std::endl;
    std::cout << "random2: " << rd() << std::endl;
    std::cout << "random3: " << rd() << std::endl;
    std::cout << "entropy: " << rd.entropy() << std::endl;
	return 0;
}

result:
range: 0	4294967295
random1: 2104889535
random2: 1299396987
random3: 1841354037
entropy: 0