C++标准库——random

来源:互联网 发布:unity3d 动态加载 编辑:程序博客网 时间:2024/06/12 19:36

C++11引入了random头文件,可以更加得到更精确和功能更完善的随机数以及相关领域问题。这个标准库分为两大部分,分别是:
- 生成器:定义了用来产生均匀分布的伪随机数的机制,也称为随机数引擎(engine)。
- 分布:以生成器得到的均匀分布的随机数序列转换为某种特定数学概率分布的序列,如均匀分布、正态分布、泊松分布等。

生成器

random_device

这是标准库提供的一个非确定性随机数生成设备,是所有生成器中唯一不需要随机数种子的生成方式。在Linux的实现中,是读取/dev/urandom设备;在Windows的实现中是调用rand_s。这种生成器基于随机过程来产生均匀分布的随机数序列,可以视为一个真随机数。
random_device在某些系统中可无法使用,会在构造函数或者调用operator()函数时抛出异常,因此对于移植性而言需要格外注意。

结构

class random_device;//Member/** random_device::min() --- 随机数范围的最小值 random_device::max() --- 随机数范围的最大值 operator() --- 生成一个真随机数 entropy() --- 计算operator()调用生成的数的熵,如果random库使用随机数引擎(伪随机数算法)实现而不是真随机数生成器,那么这个值为0 */

示例

 #include <random> #include <iostream> int main() {     std::random_device rd;     std::cout << rd.min() << '\t' << rd.max() << std::endl;     std::cout << rd() << '\t' << rd.entropy() << std::endl;     std::cout << rd() << '\t' << rd.entropy() << std::endl;     return 0; }

结果如下:

0 4294967295
1282567443 0
3103481474 0

伪随机数引擎

random库提供了三种常用的随机数生成引擎,都以模版类的方式定义。分别是:
- linear_congruential_engine:线性同余生成引擎,是最常用也是速度最快的,但随机效果一般
- mersenne_twister_engine:梅森旋转算法,随机效果最好。
- subtract_with_carry_engine:滞后Fibonacci算法。

这些生成随机数的数学算法的原理需要查阅相关数论等数学知识,在此不做涉及。主要关注在标准库的设计和使用上。

结构

在上述三种生成器模版外,random库还定义了三种引擎适配器,通过与上述模版的某个实例进行组合,从而定义了10个随机数生成器模版实例(类)。

///线性同余引擎的实例类//x = x * 48271 % 2147483647typedef linear_congruential_engine<uint_fast32_t, 48271, 0, 2147483647> minstd_rand;//x = x * 16807 % 2147483647typedef linear_congruential_engine<uint_fast32_t, 16807, 0, 2147483647> minstd_rand0;///梅森旋转引擎的实例类//状态大小为19937bits,生成32bits的随机数typedef mersenne_twister_engine<uint_fast32_t, 32,624,397,31,0x9908b0df,11,0xffffffff,7,0x9d2c5680,15,0xefc60000,18,1812433253> mt19937;//状态大小为19937bits,生成64bits的随机数typedef mersenne_twister_engine<uint_fast64_t,  64,312,156,31,0xb5026f5aa96619e9,  29,0x5555555555555555,  17,0x71d67fffeda60000,  37,0xfff7eee000000000,  43,6364136223846793005> mt19937_64;///滞后Fibonacci引擎实例类typedef subtract_with_carry_engine <uint_fast32_t, 24, 10, 24> ranlux24_base;//生成24bits的随机数typedef subtract_with_carry_engine <uint_fast64_t, 48, 5, 12> ranlux48_base;//生成48bits的随机数///将随机数引擎Engine生成的随机数序列块中p个元素的r选中,其余的不使用template <class Engine, size_t p, size_t r>class discard_block_engine;///适配 subtract_with_carry_engine,得到ranlux24,ranlux48两个生成器模版实例类typedef discard_block_engine <ranlux24_base, 223, 23> ranlux24;typedef discard_block_engine <ranlux48_base, 389, 11> ranlux48;///将Engine生成的随机数适配到w个位大小的值template <class Engine, size_t w, class UIntType>class independent_bits_engine;///将Engine产生的k个随机数的顺序打乱,内部维护了长度为k的生成的随机数的buffertemplate <class Engine, size_t k> class shuffle_order_engine;//适配minstd_rand0typedef shuffle_order_engine <minstd_rand0,256> knuth_b;///这是经过对实现平台的各种判断后,得到的默认随机数生成引擎class std::default_random_engine;

示例

所有生成器引擎,或者经过adapter修饰后的类实例,都提供如下接口供使用
- min:返回最小值,静态函数
- max:返回最大值,静态函数
- seed:设置随机数生成的种子
- operator():产生随机数
- void discard (unsigned long long z):调用z次operator()函数

另外,都定义了输入输出操作符和关系运算的非成员函数。
随机数引擎接收一个整数作为种子,不提供就会使用默认值。一般可以使用chrono库中的时间或者random_device生成一个随机数作为种子。

using clock = std::chrono::high_resolution_clock;clock::time_point begin = clock::now();auto seed = begin.time_since_epoch().count();std::minstd_rand0 rand_gen(seed);cout << rand_gen() << endl;

分布

通过强大的生成器引擎得到了均匀分布的随机数之后,为了满足特定场景的需求,random提供的分布可以以生成器为输入,得到满足不同分布的随机数序列,广义上看,可以认为是对生成器的进一步修饰和包装。主要有三个作用:
- 利用模版参数,改变生成值的类型
- 利用构造函数参数,改变生成值的区间范围
- 选择不同的分布得到满足不同分布的随机数序列

不同的分布涉及到相应的数学知识,因此仅列出支持的分布:

//均匀分布:uniform_int_distribution           //整数均匀分布uniform_real_distribution         //浮点数均匀分布//伯努利类型分布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 //分段线性分布

对于每种分布,都定义了相同的接口:
- 构造函数:针对不同分布提供所需参数,如二项分布需要给出实验次数N和每次实验成功的概率p,其他分布针对具体需要给出
- operator():使用函数调用运算符生成随机数,这个函数有一个参数,需要提供使用的生成器对象
- param:返回该分布的参数
- min、max:最大最小的范围
- reset:重置分布的状态,这样后续生成的随机序列不会依赖与之前生成的随机数

auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();std::default_random_engine generator(seed);std::normal_distribution<double> normalDist(5.0, 2.0);int p[10] = {};for(int i = 0; i < 10000; ++i){    auto r = normalDist(generator);    if (r >= 0.0 && r < 10) ++p[int(r)];}for(int i = 0; i < 10; ++i){    std::cout << i << '-' << i+1 << ':';    std::cout << std::string(p[i]/100, '*') << '\n';}

结果如下:

0-1: *1-2: ****2-3: *********3-4: ***************4-5: ******************5-6: *******************6-7: ***************7-8: ********8-9: ****9-10: *

附加设施

random另外提供了seed_seqgenerate_canonical两个额外的设施可以辅助进行随机数的生成。
- seed_seq:一个专门用来生成随机数种子序列的类,生成的都是32bit大小的无符号整数序列,用来在生成器引擎的对象构造时可以作为参数
- generate_canonical:将均匀分布的随机数映射到[0,1)区间内的一个模版函数。

0 0