C++标准库——random
2016-05-28 19:31
966 查看
C++11引入了random头文件,可以更加得到更精确和功能更完善的随机数以及相关领域问题。这个标准库分为两大部分,分别是:
- 生成器:定义了用来产生均匀分布的伪随机数的机制,也称为随机数引擎(engine)。
- 分布:以生成器得到的均匀分布的随机数序列转换为某种特定数学概率分布的序列,如均匀分布、正态分布、泊松分布等。
这是标准库提供的一个非确定性随机数生成设备,是所有生成器中唯一不需要随机数种子的生成方式。在Linux的实现中,是读取
结果如下:
0 4294967295
1282567443 0
3103481474 0
-
-
-
这些生成随机数的数学算法的原理需要查阅相关数论等数学知识,在此不做涉及。主要关注在标准库的设计和使用上。
- min:返回最小值,静态函数
- max:返回最大值,静态函数
- seed:设置随机数生成的种子
- operator():产生随机数
- void discard (unsigned long long z):调用z次operator()函数
另外,都定义了输入输出操作符和关系运算的非成员函数。
随机数引擎接收一个整数作为种子,不提供就会使用默认值。一般可以使用chrono库中的时间或者
- 利用模版参数,改变生成值的类型
- 利用构造函数参数,改变生成值的区间范围
- 选择不同的分布得到满足不同分布的随机数序列
不同的分布涉及到相应的数学知识,因此仅列出支持的分布:
对于每种分布,都定义了相同的接口:
- 构造函数:针对不同分布提供所需参数,如二项分布需要给出实验次数N和每次实验成功的概率p,其他分布针对具体需要给出
- operator():使用函数调用运算符生成随机数,这个函数有一个参数,需要提供使用的生成器对象
- param:返回该分布的参数
- min、max:最大最小的范围
- reset:重置分布的状态,这样后续生成的随机序列不会依赖与之前生成的随机数
结果如下:
-
-
- 生成器:定义了用来产生均匀分布的伪随机数的机制,也称为随机数引擎(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 % 2147483647 typedef linear_congruential_engine<uint_fast32_t, 48271, 0, 2147483647> minstd_rand; //x = x * 16807 % 2147483647 typedef 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的生成的随机数的buffer template <class Engine, size_t k> class shuffle_order_engine; //适配minstd_rand0 typedef 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_seq和
generate_canonical两个额外的设施可以辅助进行随机数的生成。
-
seed_seq:一个专门用来生成随机数种子序列的类,生成的都是32bit大小的无符号整数序列,用来在生成器引擎的对象构造时可以作为参数
-
generate_canonical:将均匀分布的随机数映射到[0,1)区间内的一个模版函数。
相关文章推荐
- 【干货】C++哈希桶(开链法解决哈希冲突)类的实现
- 【代码】c++堆的简单实现
- 【代码】C++实现二叉树基本操作及测试用例
- 【代码】C++实现广义表及其测试用例
- 【干货】C++通过模板特化实现类型萃取实例--实现区分基本类型与自定义类型的memcpy
- 【总结】C++静态成员函数及测试用例
- 【总结】C++静态成员变量的特性总结及测试用例
- 【总结】C++静态成员变量的特性总结及测试用例
- 【总结】C++基类与派生类的赋值兼容规则
- 【C语言位运算的应用】如何按bit位翻转一个无符号整型
- 双向链表的C++实现
- C++单链表的设计与实现
- 求最大公约数的设计与C语言实现
- 用C语言实现二分查找算法
- C++复数类的运算符重载
- c++日期类的实现级运算符的重载
- C++实现链表的进本操作及测试用例
- C语言实现顺序表的增删查改以及排序
- C语言实现C到C++的注释转换
- C语言用结构体写一个通讯录