伪随机数生成
随机数库提供生成随机和伪随机数的类。这些类包括:
- 均匀随机位生成器,包含随机数引擎,它们是生成均匀分布的整数序列的伪随机数生成器,以及真随机数生成器(如果可用)。
- 随机数分布(例如均匀、正态或泊松分布),它们将 URBG 生成器的输出转换为各种统计分布。
均匀随机位生成器和分布被设计为一起使用以生成随机值。所有随机数引擎都可以进行明确播种、序列化和反序列化,以用于可重复的模拟器。
均匀随机位生成器
均匀随机位生成器 是函数对象,它返回无符号整数值,并使得每个值在可能结果的范围中拥有(理想上)相等的返回概率。
所有均匀随机位生成器都满足均匀随机位生成器 (UniformRandomBitGenerator) 要求。
C++20 也定义了 uniform_random_bit_generator
概念。
在标头
<random> 定义 | |
(C++20) |
指定类型具备作为均匀随机位生成器的资格 (概念) |
随机数引擎
随机数引擎(通常简称为引擎)是以种子数据为熵源生成伪随机数的均匀随机位生成器。
在任何给定时间点,E
类型的引擎 e 都具有对于某个非负整数 i 的状态 ei
。构造完成时,e 具有初始状态 e0
,它由引擎参数和初始种子(或种子序列)确定。
任何引擎类型 E
都有定义以下属性:
-
E
的状态的大小,以E::result_type
的大小的倍数表示(即 (sizeof ei
) / sizeof(E::result_type))。 - 迁移算法 TA,它将 e 的状态 e
i
递进到它的后继状态 ei+1
(即 TA(ei
) == ei+1
)。 - 生成算法 GA,它将 e 的状态映射到一个
E::result_type
类型的值,结果是一个伪随机数。
通过交替调用 TA 和 GA 就可以生成伪随机数序列。
标准库以类模板的形式提供了三种不同类别的伪随机数生成算法,这使得这些算法可以定制。需要进行各种权衡以确定应该使用哪种引擎:
- 线性同余引擎一般很快,并对状态的存储要求非常小。
- 梅森缠绕器引擎较慢且拥有较大的状态存储要求,但只要有正确的参数,就会有最长的的不可重复序列,且拥有最可取的谱特性(对于给定的“可取”定义)。
- 带进位减法引擎在无先进算术指令集的处理器上也可以非常快,但状态存储较为庞大,有时有不太可取的谱特性。
|
(C++26 起) |
这些随机数引擎都不是加密安全的。在需要进行安全操作的情况下应该使用加密安全库(例如 OpenSSL RAND_bytes
)。
从这些模板实例化的所有类型都满足随机数引擎 (RandomNumberEngine) 要求。
在标头
<random> 定义 | |
(C++11) |
实现线性同余算法 (类模板) |
(C++11) |
实现梅森缠绕器算法 (类模板) |
(C++11) |
实现带进位减(一种延迟斐波那契)算法 (类模板) |
(C++26) |
基于计数器的可并行化引擎 (类模板) |
随机数引擎适配器
随机数引擎适配器使用另一随机数引擎作为熵源来生成伪随机数。它们通常用于改换底层引擎的谱特性。
在标头
<random> 定义 | |
(C++11) |
舍弃随机数引擎的某些输出 (类模板) |
(C++11) |
将一个随机数引擎的输出打包为指定位数的块 (类模板) |
(C++11) |
以不同顺序发送一个随机数引擎的输出 (类模板) |
预定义随机数生成器
预定义了数个具体的流行算法。
在标头
<random> 定义 | |
类型 | 定义 |
minstd_rand0 (C++11)
|
std::linear_congruential_engine<std::uint_fast32_t, |
minstd_rand (C++11)
|
std::linear_congruential_engine<std::uint_fast32_t, |
mt19937 (C++11)
|
std::mersenne_twister_engine<std::uint_fast32_t, |
mt19937_64 (C++11)
|
std::mersenne_twister_engine<std::uint_fast64_t, |
ranlux24_base (C++11)
|
std::subtract_with_carry_engine<std::uint_fast32_t, 24, 10, 24> |
ranlux48_base (C++11)
|
std::subtract_with_carry_engine<std::uint_fast64_t, 48, 5, 12> |
ranlux24 (C++11)
|
std::discard_block_engine<std::ranlux24_base, 223, 23> 24 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994 |
ranlux48 (C++11)
|
std::discard_block_engine<std::ranlux48_base, 389, 11> 48 位 RANLUX 生成器,由 Martin Lüscher 与 Fred James 设计于 1994 |
knuth_b (C++11)
|
std::shuffle_order_engine<std::minstd_rand0, 256> |
philox4x32 (C++26)
|
std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xD2511F53, 0x9E3779B9, 0xCD9E8D57, 0xBB67AE85> |
philox4x64 (C++26)
|
std::philox_engine<std::uint_fast64_t, 64, 4, 10, 0xD2E7470EE14C6C93, 0x9E3779B97F4A7C15, 0xCA5A826395121157, 0xBB67AE8584CAA73B> |
default_random_engine (C++11)
|
由实现定义 |
非确定随机数
std::random_device 是非确定的均匀随机位生成器,尽管当不支持非确定随机数生成时,允许实现用伪随机数引擎实现 std::random_device。
(C++11) |
使用硬件熵源的非确定随机数生成器 (类) |
随机数分布
随机数分布对 URBG 的输出进行后处理,以使得输出结果按照定义的统计概率密度函数分布。
随机数分布满足随机数分布 (RandomNumberDistribution) 。
在标头
<random> 定义 | |
均匀分布 | |
(C++11) |
产生在一个范围上均匀分布的整数值 (类模板) |
(C++11) |
产生在一个范围上均匀分布的实数值 (类模板) |
伯努利分布 | |
(C++11) |
产生伯努利分布上的 bool 值 (类) |
(C++11) |
产生二项分布上的整数值。 (类模板) |
产生负二项分布上的整数值。 (类模板) | |
(C++11) |
产生几何分布上的整数值。 (类模板) |
泊松分布 | |
(C++11) |
产生泊松分布上的整数值。 (类模板) |
(C++11) |
产生指数分布上的实数值。 (类模板) |
(C++11) |
产生 Γ 分布上的实数值 (类模板) |
(C++11) |
产生威布尔分布上的实数值。 (类模板) |
(C++11) |
产生极值分布上的实数值。 (类模板) |
正态分布 | |
(C++11) |
产生标准正态(高斯)分布上的实数值。 (类模板) |
(C++11) |
产生对数正态分布上的实数值。 (类模板) |
(C++11) |
产生 χ2 分布上的实数值。 (类模板) |
(C++11) |
产生柯西分布上的实数值。 (类模板) |
(C++11) |
产生费舍尔 F 分布上的实数值。 (类模板) |
(C++11) |
产生学生 t 分布上的实数值。 (类模板) |
采样分布 | |
(C++11) |
产生离散分布上的随机整数。 (类模板) |
产生分布在常子区间上的实数值。 (类模板) | |
产生分布在定义的子区间上的实数值。 (类模板) |
工具
在标头
<random> 定义 | |
(C++11) |
给定精度的均匀分布在 [ 0, 1) 上的实数值 (函数模板) |
(C++11) |
通用的偏差消除的混淆种子序列生成器 (类) |
随机数算法
在标头
<random> 定义 | |
(C++26) |
用来自均匀随机位发生器的随机数填充范围 (niebloid) |
C 随机库
除了上述的引擎和分布,也可以使用来自 C 随机库的函数和常量,尽管不推荐如此:
在标头
<cstdlib> 定义 | |
生成伪随机数 (函数) | |
初始化伪随机数生成器 (函数) | |
std::rand 生成的最大可能值 (宏常量) |
示例
#include <cmath> #include <iomanip> #include <iostream> #include <map> #include <random> #include <string> int main() { // 以真随机值播种,若可能的话 std::random_device r; // 选择 1 与 6 间的随机数 std::default_random_engine e1(r()); std::uniform_int_distribution<int> uniform_dist(1, 6); int mean = uniform_dist(e1); std::cout << "随机选择的平均值:" << mean << '\n'; // 生成围绕平均值的正态分布 std::seed_seq seed2{r(), r(), r(), r(), r(), r(), r(), r()}; std::mt19937 e2(seed2); std::normal_distribution<> normal_dist(mean, 2); std::map<int, int> hist; for (int n = 0; n < 10000; ++n) ++hist[std::round(normal_dist(e2))]; std::cout << "围绕 " << mean << " 的正态分布:\n" << std::fixed << std::setprecision(1); for (auto [x, y] : hist) std::cout << std::setw(2) << x << ' ' << std::string(y / 200, '*') << '\n'; }
可能的输出:
随机选择的平均值:4 围绕 4 的正态分布: -4 -3 -2 -1 0 * 1 *** 2 ****** 3 ******** 4 ********* 5 ******** 6 ****** 7 *** 8 * 9 10 11 12