mt19937的使用
网上讲的都不是很清楚,啃了一下CF上的Don't use rand()还有网上杂七杂八的博客,大概归纳一下它的用法
mt19937产生的随机数值域大,周期长,耗时短(约是rand()的rand()的存在
定义
首先,定义mt19937的头文件为#include<random>,同时也被包括在了万能头里面
然后我们可以这样写:
mt19937 a;
你发现他长得很像一个定义类型的形式,可以理解为我定义了一个变量名为a的mt19937类型的变量。
你还可以这样写:
mt19937 a(114514);
其中括号内为随机种子。
直接使用
那么我们定义出来以后怎么用呢:
直接
cout<<a()<<"\n";
这里a又长得像一个函数,可以直接输出以114514为随机种子的随机值了。
顺带一提,它的值域为 unsigned int的存储范围。
随机序列
这只是它最简单的应用,单有这个还不能取代 random_shuffle 这些东西。
于是我们还可以这样写(假设
shuffle(b+1,b+1+n,a);
注意shuffle位于#include<algorithm>库中
就可以对b进行一个随机打乱的操作,第三个参数为一个mt19937变量(暂且这么称呼它),表示你随机打乱所用的随机数生成器
再注意一下你在进行操作之后a会变化,意思是说你对两个一样的数组
shuffle(b+1,b+1+n,a);
shuffle(c+1,c+1+n,a);
具体地,你可以参考下列代码:
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<random>
using namespace std;
int b[15],c[15];
mt19937 a;
int main()
{
int n=10;
for(int i=1;i<=n;i++)b[i]=c[i]=i;
shuffle(b+1,b+1+n,a);
shuffle(c+1,c+1+n,a);
for(int i=1;i<=n;i++)cout<<b[i]<<" ";cout<<"\n";
for(int i=1;i<=n;i++)cout<<c[i]<<" ";cout<<'\n';
}
产生范围内的随机数
我们当然可以用mt19937后再取模,但是概率其实多半不均等。
这个我们需要用到另一个东西:
uniform_int_distribution
它也存在于random库中。
我们定义类似于mt19937,也可以理解为定义了一个函数。
uniform_int_distribution<>a(114,514)
这里的尖括号不能省,圆括号框定了a产生随机数的范围。
然后使用的话我们也是类似函数,但是要传一个参数,为一个mt19937变量
然后就可以随机产生范围的数了
其他
-
除了
uniform_int_distribution以外,你还能用normal_distribution产生正态分布,exponential_distribution产生指数分布,但是太偏门就不讲了 -
假设你想产生
long long范围内的随机数。你可以使用mt19937_64,它产生随机数的范围为[0,2^{64}-1] ,使用方法大同小异 -
你可以不引用
mt19937 a(time(0))来产生随机数,而是使用random_device a,这个使用方法与上一个基本无异,但是每时每刻种子都在变,而不会像time(0)一秒变一次但是需要注意的是,
random_device产生的数是否变动(即有可能你使用它会一直得到一个一样的值) 与 你的编译器版本有关。目前据我所知,NOI Iinux中G++的版本MinGW 9.3.0是会变动的,其他版本不太清楚。你可以用以下程序验证(有点考验手速,你可以再写个调用程序)
#include<iostream> #include<cstdio> #include<random> #include<ctime> using namespace std; int main() { freopen("last.in","r",stdin); int la,lb; cin>>la>>lb; random_device a; mt19937 b(time(0)); int na=a(),nb=b(); if(la==na)cout<<"a"; if(lb==nb)cout<<"b"; freopen("last.in","w",stdout); cout<<na<<" "<<nb<<"\n"; }但是据cpp_reference说
random_device用多了会导致性能下降,所以一般只把他当作mt19937的种子