%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** %% # 简单随机数生成:rand() 在 C&C++11 之前,通过以下两个 API 生成随机数: - `void srand(unsigned int seed);`: **初始化随机数种子** - `int rand(void);` :**生成 `[0, RAND_MAX]` 之间==均匀分布==的伪随机数**。 - `RAND_MAX` 取决于编译器实现,C++标准保证其至少为 32767。 使用示例: ```cpp // 使用rand()生成[a, b)区间的随机数. vector<int> rand_exam(int n, int a, int b) { srand((unsigned)time(0)); // 设置随机数种子 vector<int> vec; for (int i = 0; i < n; ++i) { int val = rand() % (b - a) + a; // 生成[a, b)区间的随机数. vec.push_back(val); } return vec; } ``` > [!caution] 自 C++11 起,应当使用 `<random>` 头文件中提供的 "**随机数引擎类**" 与 "**随机数分布类**" 来获得更可靠的随机数生成。 > [!info] 计算机中生成的随机数是 "**伪随机数**"——给定种子 `seed`,生成的 "**随机数序列**" 是确定的。 > > 该序列具有周期性,例如 65535,即即每次利用一个随机种子生成的随机数的周期是65535,随后重复。 #### 简单随机数生成的映射公式 生成**指定区间内均匀分布**的随机数: | 映射公式 | 随机数区间 | | --------------------------------- | -------------------- | | $\text{rand()}$ | `[0, RAND_MAX]` 整数 | | $\text{rand()}\%n$ | $[0, n)$ 整数 | | $a+\text{rand()}\%(b-a))$ | $[a,b)$ 整数 | | $a+\text{rand()}\%(b-a+1))$ | $[a,b]$ 整数 | | `rand()/(double)RAND_MAX ` | $[0.0,1.0]$ 区间浮点数 | | `a + (b-a)*rand()/RAND_MAX` | $[a, b]$ 区间浮点数 | | `a + (b-a)*rand()/(RAND_MAX + 1)` | $[a, b)$ 区间浮点数 <br> | #### 简单随机数生成存在的问题 🚨 - `rand()%n` 生成的 $[0,n)$ 区间随机整数实际上并不是 "**==均匀分布==**",因为 **`[0, RAND_MAX]` 中每个数并不一定能均等映射到 `[0, n)`**。 - 例如,`[0, 4]` 映射到区间 `[0, 1]`,实际上是 `0,2,4->0` 与 `1,3->1`。 - `rand()/RAND_MAX` 生成 **`[0, 1]` 范围内随机浮点数**时,由于**随机整数的精度低于随机浮点数**,因此**一部分浮点值永远不会被生成**。 <br><br> # random 头文件 `<random>` 头文件引入了一组协作类来提供 "**随机数生成**" 支持(since C++11)[^1]: - (1)**==随机数引擎类==**(random-number engines):**生成 `unsigned` 随机数序列** - (2)**==随机数分布类==**(random-number distribution):基于一个引擎类,生成 **==指定类型==的、==给定范围==内的、==服从特定概率分布==** 的随机数。 - 将**随机数引擎生成的随机数**映射为特定的分布,例如**均匀分布**或**正态分布**。 ## 使用说明 - 随机数引擎类:提供调用运算符 `operator()` 来**返回一个==随机 `unsigned` 整数==**(原始随机数); - 随机数分布类:提供 "**以随机数引擎 `e` 为参数**" 的调用运算符 `operator()(e)` 来返回**该分布下的随机数**。 使用示例: ```cpp // 生成[min,max]区间内均匀分布的随机整数. vector<int> genRandomInt(int n, int min, int max) { default_random_engine e(time(0)); uniform_int_distribution<> u(min, max); vector<int> res; for (int i = 0; i < n; ++i) { res.push_back(u(e)); } return res; } // 生成[min, max]区间内均匀分布的随机浮点数 vector<double> genRandomReal(int n, double min = 0.0, double max = 1.0) { default_random_engine e(time(0)); uniform_real_distribution<> u(min, max); vector<double> res; for (int i = 0; i < n; ++i) { res.push_back(u(e)); } return res; } // 生成正太分布的随机数 void getNormalReal() { double mean = 4, stde = 1.5; default_random_engine e(time(0)); normal_distribution<> norm(mean, stde); // 生成均值4, 标准差1.5的正态分布. vector<unsigned> bucket(9); // 舍入到整数, 若位于[0, 8]区间, 则统计入桶中. for (int i = 0; i < 200; ++i) { unsigned v = lround(norm(e)); // 舍入到最接近的整数 if (v < bucket.size()) { ++bucket[v]; } } for (int i = 0; i != bucket.size(); ++i) { cout << i << ": " << string(bucket[i], '*') << endl; } } int main() { auto vec_int = genRandomInt(10, 0, 10); auto vec_double = genRandomReal(10, 0.0, 1.0); auto print = [](auto& vec) { for_each(vec.begin(), vec.end(), [](auto& val) { cout << val << " "; }); cout << endl; }; print(vec_int); print(vec_double); getNormalReal(); } ``` <br> ## 随机数引擎类 随机数引擎类是 "**函数对象**",提供了调用运算符 `()` 来**返回一个==随机 `unsigned` 整数==**。 随机数引擎 `e` 生成的 **`unsigned` 整数范围取决于==编译器实现==**,可由引擎类对象的 `e.min()` 与 `e.max()` 成员函数获取。 随机数引擎支持的操作: ![[_attachment/02-开发笔记/01-cpp/库使用/cpp-随机数.assets/IMG-cpp-随机数-C46D0E2ACA6A8EA23F199B31DE5C6156.png|700]] 标准库提供了**三个引擎类**(采用不同算法生成随机数)以及 **三个适配器**,均为**模版**,参见 [^2]。 同时,为这些引擎或适配器提供了 "**==完全特例化==**" 的版本: | 引擎类(模版) | 说明 | 完全特例化的类 | | ------------------------------ | ---------------- | ----------------------------------------- | | `linear_congruential_engine<>` | 采用 "**线性同余法**" | `std::minstd_rand`,`std::minstd_rand0` | | `mersenne_twister_engine<>` | 采用 "**梅森旋转算法**" | `std::mt19937`、`std::mt19937_64` | | `subtract_with_carry_engine<>` | 采用 "**借位减法生成器**" | `std::ranlux24_base`、`std::ranlux48_base` | > [!info] `default_random_engine` 是**某个具体引擎类型的==别名==**,视为**默认引擎**,适用于大多数情况。 ```cpp int main() { // 默认随机数引擎 default_random_engine e; // `e()`返回一个无符号随机整数, 范围在`[e.min(), e.max()]`内. for (int i = 0; i < 10; ++i) { cout << e() << endl; } // [1, 2147483646] cout << "[" << e.min() << ", " << e.max() << "]" << endl; } ``` ## 随机数分布类 随机数分布类用以将 "**随机数引擎生成的随机数**" 转换到**特定分布**。 除伯努利分布外,其他分布类型均为 "**模版**",均接受 **==单个类型参数==**,指出分布生成的结果类型。 标准库提供了 20 种的**随机数分布类** 参见[^3],包括: - **均匀分布** - `std::uniform_int_distribution<IntT> u(m, n)`:生成在 `[m, n]` 区间均匀分布的**整数**(`m` 默认为 0,`n`默认为`IntT` 类型可表示的最大值)。 - `std::uniform_real_distribution<RealT> u(x, y)`:生成在 `[x, y]` 区间均匀分布的**浮点数**(`x` 默认为`0.0` ,`y` 默认 `1.0`)。 - **伯努利分布** - `std::bernoulli_distribution<IntT> g(p)`:**以给定概率 `p` 生成 `true`**(`p` 默认为`0.5`)。 - **正态分布** - `std::normal_distribution<RealT> n(m, s)`:生成均值为 `m`,标准差为 `s` 的**正态分布浮点数**。 - **泊松分布** - **抽样分布** > [!info] 各个随机数分布类模版可接受的 "模版类型参数" 不同 > - `IntT` 表示只接受整型模版参数(除 `bool` 和 `char` 系列),默认模版参数是 `int` 类型。 > - `RealT` 表示只接受浮点类型,例如 `float`,`double`,`long double`,默认模版参数是 `double` 类型。 > 随机数分布类支持的操作: ![[_attachment/02-开发笔记/01-cpp/库使用/cpp-随机数.assets/IMG-cpp-随机数-67B8857B55FD1A241039F29056A0987D.png|780]] ## 随机数设备 标准库提供了一个类 `std::random_device`,用于代表一个 "**真正的随机数生成设备**"(指可用的硬件设备)。 - 当 **"非确定性源" 设备**可用时,**从该设备处获取随机值**。 - 若不可用,根据实现定义的**伪随机数引擎**来提供 `std::random_device`。该情况下,每个 `std::random_device` 对象将**生成相同的数字序列**。 > [!info] `std::random_device` 的实现 > > Accoding to the libc++ source code for implementation of `std::random_device`, it's just a thin wrapper over `std::fopen("/dev/urandom")`。 > [!caution] `std::random_device` 通常只**用于为随机数引擎提供一个==随机 seed==**, 而不是直接用于生成随机数. > > 原因说明: "the performance of many implementations of random_device degrades sharply once the entropy pool is exhausted.  For practical use random_device is generally only used to seed a PRNG such as mt19937". 使用示例: ```cpp // 若设备可用,从一个真实的随机数生成设备上获得一个真正的随机数. 否则, 得到一个伪随机数. // rd通常只用来为其它随机数生成器提供一个随机seed std::random_device rd; std::default_random_engine gen(rd()); std::uniform_real_distribution<> dis; // [0, 1] 均匀分布浮点数. double random_gen_0_1_real() { return dis(gen); // 使用`dis`将`gen`生成的unsigned int转换到[0, 1]之间的浮点数均匀分布中. } ``` <br><br> # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later # ♾️参考资料 - [用C++的random库生成更好的随机数\_c++ random-CSDN博客](https://blog.csdn.net/henry_23/article/details/113623544) # Footnotes [^1]: 《C++ Primer》P660,17.4 节 [^2]: 《C++ Primer》P783,附录 A.3.2 随机数引擎 [^3]: 《C++ Primer》P781,附录 A.3.1 随机数分布