C++ 之 伪随机数生成 <random>

C++ 标准库提供了生成随机和伪随机数的类。这些类包括:

  • 随机数生成类:生成均匀分布整数序列的伪随机数生成器,包括随机数引擎、随机数引擎适配器以及预定义随机数生成器。
  • 随机数分布类:将生成器生成的数字序列转换为遵循特定随机变量分布(如均匀分布、正态或泊松分布)的数字序列的对象。

随机数引擎

随机数引擎可以以种子数据为熵源生成伪随机数。

随机种子:初始化随机数生成器,然后利用算法不停迭代产生随机数。最简单的使用方式便是将其作为第一个随机数产生下一个随机数。

类模板 作用
linear_congruential_engine 实现线性同余算法
mersenne_twister_engine 实现梅森缠绕器算法
subtract_with_carry_engine 实现带进位减(一种延迟斐波那契)算法

其中线性同余引擎一般地快,并对状态的存储要求非常小。延迟斐波那契生成器在无先进算术指令集的处理器上非常快,但状态存储较为庞大,有时有不太想要的谱特性。梅森缠绕器较慢且拥有较大的状态存储要求,但只要有正确的参数,就会有最长的的不可重复序列,且拥有最想要的谱特性

这里介绍一下线性同余引擎和梅森缠绕器的详细实现过程。

线性同余算法

线性同余算法的实现较为简单,数学表达如下:

\[X_{n+1} = \text{mod} \left(\left(a*X_n+b\right), c\right) \]

但是其参数 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 :通用的偏差消除的混淆种子序列生成器,通过提高种子的多样性降低随机序列的重复出现

posted @ 2021-04-28 16:50  FlameAlpha  阅读(792)  评论(0编辑  收藏  举报