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

摘要:
C++标准库提供了生成随机和伪随机数的类。随机数引擎随机数引擎可以以种子数据为熵源生成伪随机数。最简单的使用方式便是将其作为第一个随机数产生下一个随机数。

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

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

随机数引擎

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

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

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

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

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

线性同余算法

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

[X_{n+1} = ext{mod} left(left(a*X_n+b ight), c ight) ]

但是其参数 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_rand0std::linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>
由 Lewis、Goodman 及 Miller 发现于 1969,由 Park 与 Miller 于 1988 采纳为“最小标准”
minstd_randstd::linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>
较新的“最小标准”,为 Park、 Miller 及 Stockmeyer 于 1993 推荐
mt19937std::mersenne_twister_engine<std::uint_fast32_t,32,624,397,31,
0x9908b0df,11,
0xffffffff,7,
0x9d2c5680,15,
0xefc60000,18,1812433253>
32位梅森缠绕器,由松本与西村设计于1998
mt19937_64std::mersenne_twister_engine<std::uint_fast64_t,64,312,156,31,
0xb5026f5aa96619e9,29,
0x5555555555555555,17,
0x71d67fffeda60000,37,
0xfff7eee000000000,43,6364136223846793005>
64位梅森缠绕器,由松本与西村设计于2000
ranlux24_basestd::subtract_with_carry_engine<std::uint_fast32_t, 24, 10, 24>
ranlux48_basestd::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12>
ranlux24std::discard_block_engine<std::ranlux24_base, 223, 23>
24 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994
ranlux48std::discard_block_engine<std::ranlux48_base, 389, 11>
48 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994
knuth_bstd::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 :通用的偏差消除的混淆种子序列生成器,通过提高种子的多样性降低随机序列的重复出现

免责声明:文章转载自《C++ 之 伪随机数生成 <random>》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇控制Windows音量C#-web用户控件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

ZH奶酪:【Python】random模块

Python中的random模块用于随机数生成,对几个random模块中的函数进行简单介绍。如下:random.random() 用于生成一个0到1的随机浮点数。如: import random random.random() 输出: 0.3701787746508932 random.uniform(a,b) 用于生成一个指定范围内的随机浮点数,两个参...

Redis内存回收:LRU算法

Redis技术交流群 481804090 Redis:https://github.com/zwjlpeng/Redis_Deep_Read Redis中采用两种算法进行内存回收,引用计数算法以及LRU算法,在操作系统内存管理一节中,我们都学习过LRU算法(最近最久未使用算法),那么什么是LRU算法呢 LRU算法作为内存管理的一种有效算法,其含义是在...

C++中随机数的生成

1.随机数由生成器和分布器结合产生 生成器generator:能够产生离散的等可能分布数值 分布器distributions: 能够把generator产生的均匀分布值映射到其他常见分布,如均匀分布uniform,正态分布normal,二项分布binomial,泊松分布poisson 2.分布器利用运算符()产生随机数,要传入一个generator对象作...

C# 中实现随机时间的获取

原理其实非常简单,取出两个时间差的秒数,再在0到该秒数之间随机获取一个整数,将其做为秒添加到较小的时间上,可以说实现上并没什么技术难点,可以在数据类型的边界条件上却需要格外的注意,比如将大于 System.Int32.MaxValue 或小于 System.Int32.MinValue 的值转成 int 时,如果直接在变量前加上类型名转换((int)d),...

明解C语言

本文为阅读书籍《明解C语言-中级篇》所积累的知识点及编译书本代码时遇到的问题。部分对应代码在Code_2018BK_明解C语言目录下。每个代码内都含有程序功能、思路、疑惑点等内容,如有疑问指出。 rand() 头文件:#include<stdlib.h> 格式:int rand(void); 功能:生成伪随机数,基于种子值(seed,...

【Lua】使用随机数(转)

游戏中创建角色有个随机取名功能,用到了随机数,网上找了篇在lua中使用随机数的文章,mark一下。 Lua 生成随机数需要用到两个函数:math.randomseed(xx), math.random([n [, m]]) 1. math.randomseed(n) 接收一个整数 n 作为随机序列种子。2. math.random([n [, m]]) 有...