这东西的名字名字很怪(全名应该叫)。maxint2^19937-1
作为C++语言里的东西,它的作用是用来生成随机数。
I
我们不妨从这东西的原理开始说起。
它位于的C++头文件为 #include<random>
在这个头文件里,有着这么一些话:
/**
* A generalized feedback shift register discrete random number generator.
*
* This algorithm avoids multiplication and division and is designed to be
* friendly to a pipelined architecture. If the parameters are chosen
* correctly, this generator will produce numbers with a very long period and
* fairly good apparent entropy, although still not cryptographically strong.
*
* The best way to use this generator is with the predefined mt19937 class.
*
* This algorithm was originally invented by Makoto Matsumoto and
* Takuji Nishimura.
*
* balabala...
*/
大致意思:
这是一个线性反馈移位寄存器,用梅森旋转算法实现,该算法无需乘除,如果选择的种子正确,该算法生成的数字周期会很长,但是还不够强(自谦)。
使用这个算法的最佳方式是使用预定义的std::mt19937
类。
也就是这个东西:
std::mersenne_twister_engine<std::uint_fast32_t, 32, 624, 397, 31,
0x9908b0df, 11,
0xffffffff, 7,
0x9d2c5680, 15,
0xefc60000, 18, 1812433253>
这就是 std::mt19937
。
这个是 std::mt19937_64
:
std::mersenne_twister_engine<std::uint_fast64_t, 64, 312, 156, 31,
0xb5026f5aa96619e9, 29,
0x5555555555555555, 17,
0x71d67fffeda60000, 37,
0xfff7eee000000000, 43, 6364136223846793005>
在random.h
中:
template<
class UIntType,
std::size_t w, std::size_t n, std::size_t m, std::size_t r,
UIntType a, std::size_t u, UIntType d, std::size_t s,
UIntType b, std::size_t t,
UIntType c, std::size_t l, UIntType f
> class mersenne_twister_engine;
II
我们知道,rand作为一个随机数生成器,它的性能是不够强。
它所对应的 RAND_MAX
范围不过在 32767
(short
型的最大值) 到 2147483647
(int
型的最大值)之间,如果生成更大的数字(用rand()*rand()
之类的东西)并不均匀,速度也不够快,所以我们可以用到 std::mt19937
和std::mt19937_64
两个东西来生成一个比较大的数字(毒瘤出题人狂喜)。
由于它所生成的数在最大到 \(2^{19937}-1\),我们可以把它的范围看作无限制。
III
使用之前,我们知道,作为一个伪随机数生成器,它需要一个种子。这就好比rand()
需要srand()
一样。
那我们怎么播种子呢(不写种子,直接用默认的)?
比较常见的方法是使用ctime.h
里的time(0)
,用这个函数返回的秒数作为一个种子:
mt19937 example(time(0))
这样我们就选好了种子,接下来直接使用就可以了
cout<<example()<<'\n';
发现这样生成的数相当均匀,质量也相当高。
你觉得这种做法不够精妙,你觉得以秒为单位似乎不是很好。
怎么办?
C++里还有另外一个东西 std::random_device
,这个东西呢,在linux系统下是按照熵池来生成的,“在熵池耗尽前可以高速生成随机数”。
不知道什么人出了用 std::random_device
给 std::mt19937
生成种子的奇怪招数:
int main()
{
random_device seeds;
mt19937 example(seeds());
cout << example() << endl;
return 0;
}
这么写也是可以的(time(0)不可以):
int main()
{
random_device seeds;
for(int i=1;i<=20;i++)
{
mt19937 example(seeds());
cout << example() << endl;
}
return 0;
}
IV
我们随机去生成指定区间内的数字。
这里又要用到另一个东西:std::uniform_int_distribution
。
它的作用呢,是去生成随机区间的,用法:
std::uniform_int_distribution<> exp(l,r)
我们传入参数,它就可以去形成 \([l,r]\) 之间的整数。
更具体的:
int main()
{
mt19937 example(time(0));
uniform_int_distribution<> distrib(114,514);
for (int i=1; i<=10;i++)
{
cout<<distrib(example)<<' ';
}
return 0;
}
由于成员函数(随机数引擎)的关系,它需要传入一个 std::mt19937
即梅森缠绕器(mersenne_twister_engine
) 来使用。
V
我个人用的大概如下:
uniform_real_distribution
与uniform_int_distribution
这两个属于均匀分布,前者可以生成均匀分布的实数,后者生成均匀分布的整数。bernoulli_distribution
用于产生 \(bernoulli\) 分布上的bool
数。用法:
int main()
{
mt19937 example(time(0));
bernoulli_distribution distrib;
for (int i=1; i<=100;i++)
{
cout<<distrib(example)<<' ';
}
return 0;
}
生成100个随机的 bool
数(性能高)。
normal_distribution
,创建一个有特定期望值和方差的正态分布。传入的两个值是期望和方差。
int main()
{
mt19937 example(time(0));
normal_distribution<double> distrib(5,6);
for (int i=1; i<=100;i++)
{
cout<<distrib(example)<<' ';
}
return 0;
}
生成随机实数。
generate_canonical
,指定位数生成随机的,\([0,1)\) 之间的实数。传入两个值是类型和最后的 \(bit\) 位。
int main()
{
mt19937 example(time(0));
for (int i=1; i<=100;i++)
{
cout<<generate_canonical<double,50>(example)<<' ';
}
return 0;
}
VI
关于这个东西,前边的日报有讲,感兴趣可以看不感兴趣只用不看。
更多的一些随机数分布:
函数名称 | 分布类型 | 作用 |
---|---|---|
uniform_int_distribution<> (x,y) |
均匀分布 | 产生一定范围内的整数 |
uniform_real_distribution<> (x,y) |
均匀分布 | 产生一定范围内的实数 |
bernoulli_distribution |
伯努利分布 | 产生伯努利分布上的 bool 值 |
binomial_distribution<> (x,y) |
伯努利分布 | 产生二项分布上的整数值 |
negative_binomial_distribution<> (x,y) |
伯努利分布 | 产生负二项分布上的的整数值 |
geometric_distribution<> |
伯努利分布 | 产生几何分布上的的整数值 |
poisson_distribution<> (x) |
泊松分布 | |
exponential_distribution<> (x) |
泊松分布 | 产生指数分布上的实数值。 |
gamma_distribution<> (x,y) |
泊松分布 | 产生伽马分布上的实数值 |
weibull_distribution |
泊松分布 | 产生威布尔分布上的实数值。 |
normal_distribution<> {x,y} |
正态分布 | 产生标准正态分布上的实数值。 |
lognormal_distribution<> (x,y) |
正态分布 | 产生对数正态分布上的实数 |
discrete_distribution<> {(w,x,y,z)} |
采样分布 | 产生离散分布上的随机整数。 |
VII
这些东西需要在 C++11及以上标准中使用。
编译时请自行更改标准。
标签:std,指北,mt19937,int,生成,分布,distribution From: https://www.cnblogs.com/SoN3ri/p/CCF_NMSL_WCNMD.html