C++——随机数引擎和分布

随机数引擎是一个函数对象类,他们定义了一个调用运算符,不接受任何参数并且返回一个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)icon-default.png?t=M276http://www.cplusplus.com/reference/random/default_random_engine/?kw=default_random_engineuniform_int_distribution - C++ Reference (cplusplus.com)icon-default.png?t=M276http://www.cplusplus.com/reference/random/uniform_int_distribution/?kw=uniform_int_distributionbernoulli_distribution - C++ Reference (cplusplus.com)icon-default.png?t=M276http://www.cplusplus.com/reference/random/bernoulli_distribution/?kw=bernoulli_distributionnormal_distribution - C++ Reference (cplusplus.com)icon-default.png?t=M276http://www.cplusplus.com/reference/random/normal_distribution/?kw=normal_distributionlround - C++ Reference (cplusplus.com)icon-default.png?t=M276http://www.cplusplus.com/reference/cmath/lround/?kw=lround

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值