随机数引擎是一个函数对象类,他们定义了一个调用运算符,不接受任何参数并且返回一个unsigned整数。我们可以适用其生成随机数。
为什么称其为随机数引擎,因为我们通常不会直接适用引擎生成随机数,而是将其传入一个分布类对象中对齐进行分布生成,所以称其为引擎。
分布类型也是一个函数对象。定义了一个调用运算符,不接受任何参数,返回一个unsigned值
创建一个default_random_engine类的对象,即随机数引擎。标准库定义了许多的随机数引擎,他们性能和随机性质不同。每个编译器都会指定其中一个default_random_engine。此类型一般具有常用的性质。
int main()
{
default_random_engine e;
for(int i=0;i<10;++i)
{
cout << e() << ' ';
}
return 0;
}
运行结果
如果不改变种子,那么随机序列也不会该变。
生成特定分布的随机数那么就需要分布类了
uniform_int_distribution 均匀随机整型分布类
int main()
{
default_random_engine e;
uniform_int_distribution<unsigned > u(0,9);
for(int i =0;i<10;++i)
{
cout << u(e) << ' ';
}
return 0;
}
运行结果
其中uniform_int_distribution<unsigned > u(0,9)
表示生成随机数的值是unsigned类型的,在0-9之间
在进行随机数生成的时候,将引擎传入进分布对象,生成符合分布的随机数。
分布类型
分布类型也是一个函数对象。定义了一个调用运算符,接受一个随机数引擎作为参数。分布类对象适用引擎生成的随机数,并将其映射到指定的分布。
查看引擎的随机数产生范围
int main()
{
default_random_engine e;
cout << "max num is " << e.max() << endl << "min num is " << e.min();
return 0;
}
运行结果
和c类似,如果不重新导入种子随机数引擎生成的随机数是固定的。
可以将对象定义为static的,这样再次调用函数对象后,会保持状态,返回接下来的随机数。
pair<unsigned,unsigned> get_unum()
{
default_random_engine e;
static default_random_engine static_e;
int a = 0, b = 0;
for(int i=0;i<10;++i)
{
a += e();
b += static_e();
}
return {a,b};
}
int main()
{
pair<unsigned, unsigned> p1;
pair<unsigned, unsigned> p2;
p1 = get_unum();
p2 = get_unum();
p1.first == p2.first ? cout << "equal" : cout << "not equal";
cout << endl;
p1.second == p2.second ? cout << "equal" : cout << "not equal";
return 0;
}
运行结果
重置随机数引擎种子
default_random_engine e;
static default_random_engine static_e;
static int flag = 1;
if(flag == 2) e.seed(114514);
int a = 0, b = 0;
运行结果
当我们说随机数发生器,是指随机数引擎和分布对象的组合。
简单的来说,生成随机数并不是引擎或者分布类的单一责任,而是二者互相配合。
我们所需要的随机,是真正的随机,即随机数区间中的任何一个数被生成的概率都是相同的。C语言通常适用rand()函数来产生随机数,导入时间种子,这样做虽然相对随机,但其实并不是真正的随机。并且,再处理浮点数的时候会遇到一些看似正确的错误。
假设要写一个蒙特卡洛随机模拟实验,需要用到一个0-1之间的随机数。
C语言会这样写,实际上,我也确实是这样写的。
double num = (double)rand() / (double)RAND_MAX;
就实验结果来说,精准度确实比一些奇奇怪怪的方法要准确,可以说差强人意。那么缺陷在哪里呢。
因为随机整数相除后的精度下降。在C++ Primer里是这样说的
这正方法不正确的原因是随机整数的精度通常低于随机浮点数,这样,有一些浮点值就永远不会被生成。
使用C++标准库,我们就会轻松的得到一些浮点值了。
int main()
{
default_random_engine e;
uniform_real_distribution<double> u (0,1);
for(int i=0;i<10;++i)
{
cout << u(e) << ' ';
}
return 0;
}
运行结果
有些分布类是有默认结果类型的
uniform_real_distribution<> u (0,1);
这样和上述的的作用是一样的,因为这个分布类的默认结果为double类型。
有时候还会用到非均匀分布的随机数,像正态分布。标准库提供了这样的功能。实际上,标准库提供了20种分布类型。
适用正态分布类,生成正态分布的随机数并进行可视化操作
normal_distribution<> 正态分布
正态分布模板类,产生符合正态分布的浮点数,默认随机数类型为double。
#include <iostream>
#include <random>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
default_random_engine e;
normal_distribution<> n(4,1.5); //均值是4 标准差是1.5
vector<unsigned> vec(9);
for(int i=0;i!=500;++i)
{
unsigned v = lround(n(e)); //舍入到最接近的整数
if(v<vec.size())
++vec.at(v);
}
for_each(vec.begin(), vec.end(),[](unsigned u){while(u--) cout << "*"; cout << endl;});
return 0;
}
运行结果
上面提到了分布类是模板类,但是,有一个分布类是例外。
bernoulli_distribution 伯努利分布
这个类也是非常的有意思,他返回一个bool值,默认情况下返回true的概率为0.5。
可以用它写出一些有意思的小玩应
#include <iostream>
#include <random>
using namespace std;
int main()
{
const unsigned tot_num = 10;
default_random_engine e;
bernoulli_distribution b;
cout << "欢迎来到鱿鱼游戏" << endl;
char sel;
bool flag;
for(int i=0; i!=tot_num;++i)
{
flag = b(e);
cout << "left or right (l/f): ";
cin >> sel;
if(flag == true && sel == 'l')
{
cout << "YOU DIED";
exit(0);
}
else if(flag == false && sel == 'r')
{
cout << "YOU DIED";
exit(0);
}
else
cout << "请继续选择" << endl;
}
cout << "你成功存活了下来";
return 0;
}
运行结果
也可以看一下生成的随机情况
int main()
{
const unsigned tot_num = 1000000;
default_random_engine e;
bernoulli_distribution b;
cout << boolalpha;
unsigned flag = 0;
for(int i=0;i<tot_num;++i)
{
if(b(e)) ++flag;
}
cout << static_cast<double>(flag) / static_cast<double> (tot_num);
return 0;
}
运行结果
很接近0.5了
在tot_num = 1000000000时
我们可以看见
一些链接
default_random_engine - C++ Reference (cplusplus.com)http://www.cplusplus.com/reference/random/default_random_engine/?kw=default_random_engineuniform_int_distribution - C++ Reference (cplusplus.com)
http://www.cplusplus.com/reference/random/uniform_int_distribution/?kw=uniform_int_distributionbernoulli_distribution - C++ Reference (cplusplus.com)
http://www.cplusplus.com/reference/random/bernoulli_distribution/?kw=bernoulli_distributionnormal_distribution - C++ Reference (cplusplus.com)
http://www.cplusplus.com/reference/random/normal_distribution/?kw=normal_distributionlround - C++ Reference (cplusplus.com)
http://www.cplusplus.com/reference/cmath/lround/?kw=lround