C++ 之 伪随机数生成 <random>
C++ 标准库提供了生成随机和伪随机数的类。这些类包括:
- 随机数生成类:生成均匀分布整数序列的伪随机数生成器,包括随机数引擎、随机数引擎适配器以及预定义随机数生成器。
- 随机数分布类:将生成器生成的数字序列转换为遵循特定随机变量分布(如均匀分布、正态或泊松分布)的数字序列的对象。
随机数引擎
随机数引擎可以以种子数据为熵源生成伪随机数。
随机种子:初始化随机数生成器,然后利用算法不停迭代产生随机数。最简单的使用方式便是将其作为第一个随机数产生下一个随机数。
类模板 | 作用 |
---|---|
linear_congruential_engine | 实现线性同余算法 |
mersenne_twister_engine | 实现梅森缠绕器算法 |
subtract_with_carry_engine | 实现带进位减(一种延迟斐波那契)算法 |
其中线性同余引擎一般地快,并对状态的存储要求非常小。延迟斐波那契生成器在无先进算术指令集的处理器上非常快,但状态存储较为庞大,有时有不太想要的谱特性。梅森缠绕器较慢且拥有较大的状态存储要求,但只要有正确的参数,就会有最长的的不可重复序列,且拥有最想要的谱特性
这里介绍一下线性同余引擎和梅森缠绕器的详细实现过程。
线性同余算法
线性同余算法的实现较为简单,数学表达如下:
但是其参数 a,b,c
需要满足一定条件,这里给出网上的一种C++代码实现如下:
static uint32 prngState = 0;
uint32 init_rand(uint32 seed)
{
//Seed the pseudo-random number generator
prngState += seed;
//Successful processing
return SUCCESS;
}
uint32 _rand(void)
{
uint32 value;
//Use a linear congruential generator (LCG) to update the state of the PRNG
prngState *= 1103515245;
prngState += 12345;
value = (prngState >> 16) & 0x07FF;
prngState *= 1103515245;
prngState += 12345;
value <<= 10;
value |= (prngState >> 16) & 0x03FF;
prngState *= 1103515245;
prngState += 12345;
value <<= 10;
value |= (prngState >> 16) & 0x03FF;
//Return the random value
return value;
}
int32 rand_range(int32 min, int32 max)
{
int32 value;
//Valid parameters?
if (max > min)
{
//Pick up a random value in the given range
value = min + (_rand() % (max - min + 1));
}
else
{
//Use default value
value = min;
}
//Return the random value
return value;
}
该代码摘自知乎博文
梅森缠绕器
梅森缠绕器的实现过程较为复杂
- 第一阶段:初始化,根据随机种子获得初始的梅森旋转链;
- 第二阶段:根据已有梅森旋转链进行旋转算法获取下一次循环的旋转链;
- 第三阶段:根据梅森旋转链计算随机数;
其C++代码实现如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <time.h>
using namespace std;
bool isInit;
int index;
int MT[624]; //624 * 32 - 31 = 19937
void srand(int seed)
{
index = 0;
isInit = 1;
MT[0] = seed;
for(int i=1; i<624; i++)
{
int t = 1812433253 * (MT[i-1] ^ (MT[i-1] >> 30)) + i;
MT[i] = t & 0xffffffff; //取最后的32位
}
}
void generate()
{
for(int i=0; i<624; i++)
{
// 2^31 = 0x80000000
// 2^31-1 = 0x7fffffff
int y = (MT[i] & 0x80000000) + (MT[(i+1) % 624] & 0x7fffffff);
MT[i] = MT[(i + 397) % 624] ^ (y >> 1);
if (y & 1)
MT[i] ^= 2567483615;
}
}
int rand()
{
if(!isInit)
srand((int)time(NULL));
if(index == 0)
generate();
int y = MT[index];
y = y ^ (y >> 11);
y = y ^ ((y << 7) & 2636928640);
y = y ^ ((y << 15) & 4022730752);
y = y ^ (y >> 18);
index = (index + 1) % 624;
return y; //y即为产生的随机数
}
int main()
{
srand(0); //设置随机种子
int cnt = 0;
for(int i=0; i<1000000; i++) //下面的循环是用来判断随机数的奇偶概率的
{
if(rand() & 1)
cnt++;
}
cout<<cnt / 10000.0<<"%"<<endl;
return 0;
}
该代码摘自it610网,该博文包含原理解析。
随机数引擎适配器
随机数引擎适配器生成以另一随机数引擎为熵源的伪随机数,以随机数引擎为基础产生过更为多样性的随机序列。
类模板 | 作用 |
---|---|
discard_block_engine | 舍弃随机数引擎的某些输出 |
independent_bits_engine | 将一个随机数引擎的输出打包为指定位数的块 |
shuffle_order_engine | 以不同顺序发送一个随机数引擎的输出 |
预定义随机数生成器
如果有需要可以根据上述的随机数引擎和随机数引擎适配器构建属于自己的随机数生成器。同时C++的标准库也提供了需要经典的随机数生成器如下:
类型 | 定义 |
---|---|
minstd_rand0 | std::linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647> 由 Lewis、Goodman 及 Miller 发现于 1969,由 Park 与 Miller 于 1988 采纳为“最小标准” |
minstd_rand | std::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647> 较新的“最小标准”,为 Park、 Miller 及 Stockmeyer 于 1993 推荐 |
mt19937 | std::mersenne_twister_engine<std::uint_fast32_t, 32, 624, 397, 31, 0x9908b0df, 11, 0xffffffff, 7, 0x9d2c5680, 15, 0xefc60000, 18, 1812433253> 32 位梅森缠绕器,由松本与西村设计于 1998 |
mt19937_64 | std::mersenne_twister_engine<std::uint_fast64_t, 64, 312, 156, 31, 0xb5026f5aa96619e9, 29, 0x5555555555555555, 17, 0x71d67fffeda60000, 37, 0xfff7eee000000000, 43, 6364136223846793005> 64 位梅森缠绕器,由松本与西村设计于 2000 |
ranlux24_base | std::subtract_with_carry_engine<std::uint_fast32_t, 24, 10, 24> |
ranlux48_base | std::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12> |
ranlux24 | std::discard_block_engine<std::ranlux24_base, 223, 23> 24 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994 |
ranlux48 | std::discard_block_engine<std::ranlux48_base, 389, 11> 48 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994 |
knuth_b | std::shuffle_order_engine<std::minstd_rand0, 256> |
default_random_engine | 实现定义 |
随机数分布
处理随机数生成器的输出,以使得输出结果按照定义的统计概率密度函数进行分布。
均匀分布 | |
---|---|
uniform_int_distribution | 产生在一个范围上均匀分布的整数值 |
uniform_real_distribution | 产生在一个范围上均匀分布的实数值 |
伯努利分布 | |
---|---|
bernoulli_distribution | 产生伯努利分布上的 bool 值 |
binomial_distribution | 产生二项分布上的整数值 |
negative_binomial_distribution | 产生负二项分布上的整数值 |
geometric_distribution | 产生几何分布上的整数值 |
泊松分布 | |
---|---|
poisson_distribution | 产生泊松分布上的整数值 |
exponential_distribution | 产生指数分布上的实数值 |
gamma_distribution | 产生 |
weibull_distribution | 产生威布尔分布上的实数值 |
extreme_value_distribution | 产生极值分布上的实数值 |
正态分布 | |
---|---|
normal_distribution | 产生标准正态(高斯)分布上的实数值 |
lognormal_distribution | 产生对数正态分布上的实数值 |
chi_squared_distribution产生 | χ2 |
cauchy_distribution | 产生柯西分布上的实数值 |
fisher_f_distribution | 产生费舍尔 |
student_t_distribution | 产生学生 |
采样分布 | |
---|---|
discrete_distribution | 产生离散分布上的随机整数 |
piecewise_constant_distribution | 产生分布在常子区间上的实数值 |
piecewise_linear_distribution | 产生分布在定义的子区间上的实数值 |
其他工具
std::uniform_random_bit_generator
:均匀随机位生成器是函数对象,它返回无符号整数值,并使得每个值在可能结果的范围中拥有(理想上)相等的被返回概率。
std::random_device
:使用硬件熵源的非确定随机数生成器
std::generate_canonical
: 使通过随机数生成器获得的数据均匀分布在 [0, 1) 上
seed_seq
:通用的偏差消除的混淆种子序列生成器,通过提高种子的多样性降低随机序列的重复出现