C++Primer_Chap17_标准库特殊设施_List04_随机数_笔记

本文深入探讨C++中随机数生成的机制,包括rand函数的局限性,以及C++11引入的随机数库,涵盖了随机数引擎、分布类的使用,如何生成均匀和非均匀分布的随机数,以及如何通过种子实现随机数序列的控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  C和C++在老版本都依赖一个简单的C库函数rand生成随机数。rand函数生成均匀分布的伪随机整数,每个随机数的范围在0和一个系统相关的最大值之间。

#include <random>

  定义在头文件random中的随机数库通过一组协作的类来解决非随机性的问题:随机数引擎(random-number enginers)随机数分布类(random-number distribution)

随机数库的组成
引擎类型,生成随机unsigned整数序列
分布类型,使用引擎返回服从概率分布的随机数

  C++程序不应该使用库函数rand,而应使用default_random_engine类和恰当的分布类对象。

随机数引擎和分布

  随机数引擎是函数对象类,它们定义了一个调用运算符,该运算符不接受参数并返回一个随机unsigned整数。我们可以通过调用一个随机数引擎对象来生成原始随机数:

#include <random>

default_random_engine e;
for(size_t i = 0; i < 10; ++i)
    cout << e() << " ";

  标准库定义了多个随机数引擎类,区别在于性能和随机性治疗不同。每个编译器都会指定其中一个作为default_random_engine类型。

随机数引擎操作
Engine e;默认构造函数;使用该引擎类型默认的种子
Engine e(s);使用整型值s作为种子
e.seed(s)使用种子s重置引擎状态

e.min()

e.max()

此引擎可生成的最小值和最大值
Engine::result_type此引擎生成的unsigned整型类型
e.discard(u)将引擎推荐u步;u的类型是unsigned long long
uniform_int_distribution<unsigned> u(0, 9);
default_random_engine e;
for( size_t i = 0; i < 10; ++i)
    cout << u(e) << " ";

  随机数分布类会使用包含的范围,从而可以得到给定整型类型的每个可能值。类似引擎类型,分布类型也是函数对象类。分布类型定义了一个调用运算符,它接受一个随机数引擎做参数。分布对象使用它的引擎参数生成随机数,并将其映射到指定的分布。

  引擎数发生器对于一个给定的发生器,每次运行程序它都会返回相同的数值序列。即一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其(包括引擎和分布对象)定义为static的。否则,每次调用函数都会生成相同的序列:

vector<unsigned> good_randVec()
{
    static default_random_engine e;
    static uniform_random_distribution<unsigned> u(0, 9);
    vector<unsigned> ret;
    for( size_t i = 0; i< 100; ++i)
        ret.push_back(u(e));
    return ret;
}

  希望每次运行程序会生成不同的随机结果,可以通过提供一个种子(seed)来达到这一目的。种子就是一个数值,引擎可以利用它从序列中一个新位置重新开始生成随机数。

  为引擎设置种子有两种方式:在创建引擎对象时提供种子,或者调用引擎的seed成员:

default_random_engine e1;
default_random_engine e2(2147483646);
default_random_engine e2;
e3.seed(32767);
default_random_engine e4(32767);
for(size_t i = 0; i < 100; ++i)
{
    if(e1() == e2())
        cout << "unseeded match at iteration:" << i << endl;
    if(e3() == e4())
        cout << "seeded differs at iteration:" << i << endl;
}
#include <ctime>
#include <random>

default_random_engine e(time(0));

其他随机数分布

  随机数引擎生成unsigned数,范围内的每个数被生成的概率都是相同的。而应用程序常常需要不同类型或不同分布的随机数。标准库通过定义不同随机数分布对象来满足这些要求。

生成随机实数

default_random_engine e;

uniform_real_distribution<double> u(0, 1);
for(size_t i = 0; i < 10; ++i)
    cout << u(e) << " ";
分布类型的操作
Dist d;

默认构造函数;

其他构造函数依赖Dist的类型

分布类型的构造函数是explicit的

d(e);用相同的e连续调用d的话,会根据d的分布式类型生成一个随机数序列;

d.min()

d.max()

返回d(e)能生成的最小值和最大值
d.reset()重建d的转态,使得随后对d的使用不依赖d已经生成的值

  可以通过在模板名之后使用空尖括号来使用默认随机数类型(浮点值默认double,整型默认int)

uniform_random_distribution<> ui(0, 9);
uniform_real_distribution<> ud(0, 1);

生成非均匀分布的随机数

  除了正确生成在指定范围内的数之外,C++11标准库还可以生成非均匀分布的随机数。实际上,定义了20中分布类型。

  作为一个例子,我们将生成一个正态分布的值的序列,并画出值的分布。由于normal_distribution生成浮点值,我们的程序使用头文件cmath的lround函数将每个随机数舍入到最接近的整数。我们将生成200个数,以均值4为中心,标准差为1.5。

#include <random>
#include <cmath>

default_random_engine e;
normal_distribution<> n(4, 1.5);
vector<unsigned> vals(9);
for( size_t i = 0; i != 200; ++i)
{
    unsigned v = lround(n(e));
    if( v < vals.size())
        ++vals[v];
}

for( size_t j = 0; j != vals.size(); ++j)
    cout << j << ":" << string(vals[j], "*") << endl;

bernoulli_distribution类

  我们注意到一个分布不接受模板参数,即bernoulli_distribution。因为它是一个普通类,而非模板。此分布总是返回一个bool值。它返回true的概率是一个常数,默认值为0.5.

string resp;
default_random_engine e;
bernoulli_distribution b;
do {
    bool first = b(e);
    cout << ( first ? "We go first"
                    : "You get to go first") << endl;
    cout << ((play(first)) ? "sorry, you lost"
                           : "congrats, you won") << endl;
    cout << "play again? Enter 'yes' or 'no'" << endl;
}while( cin >> resp && resp[0] =='y');

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值