用C++ TR1 生成随机数
原作 :[英文原文]
翻译 :Orbit(阿猫阿狗)
本文将介绍如何使用C++ TR1(C++ 标准委员会 Technical Report 1) 提供的随机数生成功能
除了介绍基本的一致随机数生成之外,还会介绍随机样本的可能分布,包括:柏努利分布,二项分布,指数分布,伽马分布,几何分布,正态分布和泊松分布。我会指出一些针对特定分布需要注意的地方,比如参数约定,最后还会给出一些提示,比如如何使用TR1不直接支持的分布产生随机数,比如柯西分布,chi-squared分布和Student t。
Visual Studio 2008 现在通过
feature pack支持TR1扩展(orbit注:微软已经发布了Visual Studio 2008的Services Pack 1,它包含了此前发布的feature pack,以及完整的TR1支持),其它支持TR1的软件或库有
Boost和Dinkumware。
GCC 从4.3版本开始支持 C++ TR1。
为了清楚起见,本文的例子代码都使用了完整的名字空间(namespace)限定,你可以使用using
语句来消除这些限定。想要了解更多的例子代码可以看看这篇文章
测试 C++ TR1 随机数生成机制 ,此文还介绍了一个不太成熟的测试框架。
提纲
C++ 的随机数生成类和函数都在 <random>
头文件中定义,并包含在名字空间(namespace)std::tr1
中,注意在C++中
tr
是小写字母,在英语文本中“TR”是大写的。
任何伪随机数生成软件的核心就是一个生成均匀分布的随机整数的例程,它被用在引导过程中产生均匀分布的实数,这些均匀分布的实数再通过各种变换(算法)产生其它分布,例如“接受-拒绝”算法,不过这些都需要一个原始的随机数做为起始。
在 C++ TR1 中,有几种不同的(随机数)生成内核,或称之为“引擎”供你选择,下面就是Visual
Studio 2008 feature pack所支持的四个引擎类。
-
linear_congruential
循环使用公式 x(i) = (A * x(i-1) + C) mod M 线性叠加
-
mersenne_twister
使用了
马特赛旋转演算 算法
-
subtract_with_carry
(带进位线性同余算法)对整数算法循环使用公式
x(i) = (x(i - R) - x(i - S) - cy(i - 1)) mod M
-
subract_with_carry_01
(改进的带进位线性同余算法)对浮点数算法循环使用公式
x(i) = (x(i - R) - x(i - S) - cy(i - 1)) mod 1
每种引擎都有一个 seed()
函数,它接受一个
unsigned long
型参数作为随机数产生的种子。 也可以通过引擎之间独特的模版参数设置种子。
uniform_int
类以相同的概率在一个范围内抽取整数,它的构造函数有两个参数,分别表示抽取范围的最大值和最小值,需要注意的是抽取范围是个闭区间,也就是这两个值也可能被抽取到。
例子:
下面面的代码将打印5个从集合:1,2,3,...,52随即抽取的数字,这可能就是一个从一副牌中抽取五张牌的算法模型,每次抽取一张牌,之后归还、洗牌,再抽取下一张牌。
std::tr1::uniform_int<int> unif(1, 52);
for (int i = 0; i > 5; ++i)
std::cout << unif(eng) << std::endl;
uniform_real
类用于从一个区间产生一个浮点数,它的构造函数有两个参数,就是区间的两个端点,默认值是从0到1。这个类的(随机)值是从半开区间产生的,对于参数 a 和 b,随机值产生的范围是区间:[a, b),换句话说,就是产生的值x满足条件: a <= x < b。
下面的代码从区间 [3, 7) 生成随机数。
std::tr1::uniform_real<double> reif(3,7);
C++ TR1 库还支持根据各种分布类(算法)产生非均匀分布的随机数,这些类通过
operator()
方法返回随机样本值。这个方法使用一个引擎类作为参数,例如下面的代码就演示了使用正态分布产生10个随机数。
std::tr1::mt19937 eng; // a core engine class
std::tr1::normal_distribution<double> dist;
for (int i = 0; i < 10; ++i)
std::cout << dist(eng) << std::endl;
尽管 TR1 的规范和Visual Studio的文档都明确表示分布类自带一个默认的模版参数,但事实是必须显式指定这个参数,因此上面的 normal_distribution
类被声明为带一个 double
类型的返回值,即使double
就是默认类型。
下面的例子都假设引擎类 eng
正在使用。
柏努利随机值以概率 p 返回1, 以概率 1-p 返回0值,这个类从柏努利分布类bernoulli_distribution
产生样本,它的构造函数只有一个参数,p,这个参数的默认值是0.5。有趣的是bernoulli_distribution
类返回值类型不是 int
,而是bool
类型, 所以它返回true表示概率返回false表示概率1-p,这是唯一的一个返回布尔值的分布类,也正式这个原因,它成为唯一的一个没有返回值类型模版参数的分布类。
例子:
下面的代码假设已经定义了引擎 eng
。
这个例子将在四分之一的时间打印 “six of one” ,剩下的时间打印“half dozen of another”。
std::tr1::bernoulli_distribution bern(0.25);
std::cout << (bern(eng) ? "six of one" : "half dozen of another") << std::endl;
二项分布将n次独立柏努利测试中成功的次数作为返回值,成功的概率是p,产生二项分布随机数的类是
binomial_distribution
,它代两个模版参数,就是n的类型和p的类型,默认值分别是int
和 double
,构造函数使用参数 n = 1 和 p = 0.5做为默认值。
例子:
下面的代码将模拟掷5次骰子,统计骰子上面是1的次数。(所有的结果都很相似,你可以用来计算其它值出现的次数)
std::tr1::binomial_distribution<int, double> roll(1.0/6.0);
std::cout << roll(eng) << std::endl;
指数分布有两个公共的参数表示: 斜率和平均值,注意C++ TR1 标准已经指定了斜率的参数表示,使用指定的参数表示,密度函数就可以表示为 f(x) = λ e-λ x ,并且平局值就是 1/λ。
用于产生指数分布的类是
exponential_distribution
,如果没有指定斜率,构造函数就使用默认值 λ = 1。
例子:
下面的代码就是从平均值是13的指数分布中找到10个值,并求和。
const double mean = 13.0;
double sum = 0.0;
std::tr1::exponential_distribution<double< exponential(1.0/mean);
for (int i = 0; i < 10; ++i)
sum += exponential(eng);
伽马分布在 TR1 已经被参数化,它的密度函数是f(x) = xα-1e-x/Γ(α)。
α 参数就是形态参数,伽马分布的更普遍的参数化表示应该包含比例参数 β,这样伽马分布的密度函数实际上是 f(x) = (x/β)α-1e-x/β/(βΓ(α)),TR1的参数化实际上就是令β = 1而已。
还有一个不太普遍的参数化表示是使用参数1/β,我猜想标准委员会接受只有一个参数的伽马分布版本是为了避免与第二个参数混淆,除了这个动机之外,没有理由要采用只有一个参数的版本。要使用比例参数 β 产生伽马分布的随机样本,只要使用比例参数1产生伽马分布的随机样本,然后乘以β就行了。
用于产生伽马分布的类是 gamma_distribution
,构造函数有一个参数,就是形态参数 α,这个参数默认是1。
例子:
下面的代码用形态参数3和比例1产生10个伽马分布的随机数。
std::tr1::gamma_distribution<double> gamma(3.0);
for (int i = 0; i < 10; ++i)
std::cout << gamma(eng) << std::endl;
例子:
下面的代码用形态参数3和比例5产生10个伽马分布的随机数。
std::tr1::gamma_distribution<double> gamma(3.0);
for (int i = 0; i < 10; ++i)
std::cout << 5.0*gamma(eng) << std::endl;
几何分布有一个参数p,使用这个p作为柏努利测试的成功概率,连续进行柏努利测试直到第一次出现成功时统计之前所进行的柏努利测试的次数。
产生几何分布的类是 geometric_distribution
,它的构造函数有一个参数,就是成功的概率p。
例子:
下面的代码演示如何使用十分之一的成功概率获取一个几何分布的随机样本。
std::tr1::geometric_distribution<int, double> geometric(0.1);
std::cout << geometric(eng) << std::endl;
正态分布 (高斯分布)有两个参数,平局值 μ 和标准偏差 σ。
产生正态分布的类是
normal_distribution
,它的构造函数有两个参数, μ 和 σ,默认值分别是 0 和 1。
注意正态分布类使用的是标准偏差,不是方差
例子:
下面的代码用平均值3和标准差4(如果是方差就是16)。
std::tr1::normal_distribution<double> normal(3.0, 4.0);
std::cout << normal(eng) << std::endl;
泊松分布用于表示在一段给定的时间间隔内,各种可能的比率或独立事件可能出现的概率。
用于产生泊松分布的类是
poisson_distribution
,它的构造函数有一个参数,就是分布的平均值 λ 。
例子:
下面的代码演示了用平均值7产生一个泊松分布的样本
std::tr1::poisson_distribution<double> poisson(7.0);
std::cout << poisson(eng) << std::endl;
C++ TR1 规范只是包含了一些常见的分布,其它分布可以通过直接支持的分布相互组合实现,下面的几种分布就是这样组合的例子,统计学书籍中可能会有更多的分布。
要产生柯西分布的样本,首先要产生(-π/2, π/2)区间上的平均分布样本,然后取正切值(tangent)。
const double pi = 3.14159265358979;
std::tr1::uniform_real<double> uniform(-0.5*pi, 0.5*pi);
double u = uniform(eng);
std::cout << tan(u) << std::endl;
上面的代码有些诡秘的地方需要说明一下,由于uniform_real
产生的值是一个半开区间,它就有可能返回区间最左端的值,正切函数(tangent)对于精确的值-π/2会溢出。然而,我们的值 pi
要比真的 π 值小那么一点,正式这被阶段的一点使得tan
在uniform
返回最小值时也没有问题。
卡方分布 (χ2) 是伽马分布的一种特殊形式,有 ν 自由度的卡方分布和形态参数是ν/2 ,比例参数是2 的伽马分布是一样的,由于C++ TR1的实现只是直接支持比例参数是1的伽马分布,这就需要先产生形态参数是 ν/2,比例参数是1的伽马分布,然后乘以2。
下面的代码演示可使用5自由度的卡方分布。
double nu = 5.0;
std::tr1::gamma_distribution<double> gamma(0.5*nu);
std::cout << 2.0*gamma(eng) << std::endl;
要产生 ν 自由度的T分布,首先产生一个区间(0,1)的正态分布样本 X 和一个ν 自由度的卡方分布样本 Y,然后用 X/sqrt(Y/ν)计算出T分布样本。
下面的代码演示了使用7自由度的T分布产生样本
double nu = 7.0;
std::tr1::normal_distribution<double> normal;
std::tr1::gamma_distribution<double> gamma(0.5*nu);
double x = normal(eng);
double y = 2.0*gamma(eng);
std::cout << x/sqrt(y/nu) << std::endl;
分享到:
相关推荐
std::tr1现在已经广泛应用了,shared_ptr, function, bind, mem_fn, tuple都是很重要的工具。
这是C++ TR1的官方report。里面详细的介绍了TR1新引入的一些类和库,非常的实用,讲解也比较详细。如果大家比较感兴趣TR1的话,可以下载《C++标准库扩展权威指南》这本书。
C++ reference chm 参考手册(包含STL tr1等)
IPD技术评审(TR1-TR6)知识分享IPD技术评审(TR1-TR6)知识分享IPD技术评审(TR1-TR6)知识分享IPD技术评审(TR1-TR6)知识分享IPD技术评审(TR1-TR6)知识分享
原因十分显明:scott meyers 极富实践意义的c++ 研讨方式,描述出专家用以产出干净、正确、高效代码的经验法则和行事法则——也就是他们几乎总是做或不做的某些事。 这本书不是读完一遍就可以束之高阁的快餐读物,...
TR1 的 ros_control硬件接口_C++_代码_相关文件_下载
《Effective C++中文版(第3版改善程序与设计的55个具体做法)》不是读完一遍就可以束之高阁的快餐读物,也不是用以解决手边问题的参考手册,而是需要您去反复阅读体会的,C++是真正程序员的语言,背后有着精深的...
Applications of new "TR1" standard library functionality, along with comparisons to existing standard library components. Insights into differences between C++ and other languages (e.g., Java, C#, C)...
·崭新的 TR1 标准程序库功能应用,以及与既有标准程序库组件的比较。 ·洞察 C++和其他语言(例如Java、C#、C)之间的不同。此举有助于那些来自其他语言阵营的开发人员消化吸收 C++ 式的各种解法。
在送审的C++标准库TR1中,有十个Boost库成为标准库的候选方案。在更新的TR2中,有更多的Boost库被加入到其中。从某种意义上来讲,Boost库成为具有实践意义的准标准库。 可下载Boost C++ Libraries安装boost库。大...
* 崭新的 "TR1" 标准程序库功能应用,以及与既有标准程序库组件的比较。 * 洞察 C++和其他语言(例如Java、C#、C)之间的不同。此举有助于那些来自其他语言阵营的开发人员消化吸收 C++ 式的各种解法。
用 C++ 实现的一个 B 树类,有注释,但不知道我写得能不能看明白。使用时先要从类 CDataTypeForBtree 派生一个类出来,在派生类中实现数据比较函数 Compare,数据输出函数 Print。然后在堆上创建一个数据类对象,把...
定位节点采用NA5TR1为无线定位节点的核心器件,该器件内部集成了具有双边两路测距功能的模块和2.45 GHz ISM RF收发器,利用ATmega644v单片机为控制器,完成无线定位节点的整体设计,并给出无线定位节点电路原理图。
使用方法: #include <string h> #include <stdio h> #include "DataTypeForBtree h" #include "BTree h" struct tree data { char tid[37]; char tname[63]; ...
TCT40-16TR1 压电陶瓷超声传感器(通用型) TCT40-16TR1 压电陶瓷超声传感器(通用型)
Other C++ Features Reference Arguments Function Overloading Default Arguments Variable-Length Arrays and alloca() Friends Exceptions Run-Time Type Information (RTTI) Casting Streams Preincrement and ...
超越c++标准库——boost程序库导论 对于学习boost库或TR1库是最有用的文档...
此应用程序是使用SAP Fiori工具套件中的SAP Fiori工具-App Generator生成的。 为了启动生成的应用程序,只需在生成的应用程序根文件夹中运行以下命令: npm start 先决条件: Active NodeJS LTS(长期支持)版本和...
原因十分显明:scott meyers 极富实践意义的c++ 研讨方式,描述出专家用以产出干净、正确、高效代码的经验法则和行事法则——也就是他们几乎总是做或不做的某些事。 这本书不是读完一遍就可以束之高阁的快餐读物,...