文章目录
- [C++11 随机数生成------告别 rand()](#C++11 随机数生成——告别 rand())
-
- [一、C 语言的 rand() 有什么问题](#一、C 语言的 rand() 有什么问题)
- [二、C++11 的三大组件](#二、C++11 的三大组件)
- 三、实战例子
-
- [3.1 模拟温度:42°C 上下波动 2°C](#3.1 模拟温度:42°C 上下波动 2°C)
- [3.2 模拟误码率:极小数值](#3.2 模拟误码率:极小数值)
- [3.3 生成整数范围](#3.3 生成整数范围)
- 四、逐行拆解
-
- `std::random_device{}()`
- [`std::mt19937 rng{种子}`](#
std::mt19937 rng{种子}) - [`std::uniform_real_distribution<double>(min, max)(rng)`](#
std::uniform_real_distribution<double>(min, max)(rng))
- [五、为什么用 static 修饰引擎](#五、为什么用 static 修饰引擎)
- 六、为什么加锁
- [七、C++11 还支持其他分布](#七、C++11 还支持其他分布)
- [八、C vs C++11 对比](#八、C vs C++11 对比)
- 九、面试该怎么说
C++11 随机数生成------告别 rand()
最近在嵌入式项目里看到了一种从来没用过的随机数写法,研究了一下发现这竟然是 C++11 标准的正式写法。比 C 语言的 rand() 好用太多了,记录下来。
一、C 语言的 rand() 有什么问题
cpp
#include <cstdlib>
#include <ctime>
srand(time(NULL)); // 用当前时间做种子
// 想生成 40.0 到 44.0 之间的浮点数:
double temp = 40.0 + (rand() % 400) / 10.0;
// 一坨计算,不好读
// 而且 rand() 有几个硬伤
| rand() 的问题 | 说明 |
|---|---|
| 随机质量差 | 周期短(通常是 2^31-1),低位不够随机 |
| 指定范围麻烦 | 要自己用 % 和乘法算,容易出错 |
| 线程不安全 | 内部有全局状态,多线程同时调会乱 |
| 分布单一 | 只有均匀分布,没法生成正态分布等 |
二、C++11 的三大组件
C++11 把随机数拆成了三个组件,各司其职:
① 真随机数设备(random_device) → 生成种子(从硬件噪声获取)
↓
② 随机数引擎(mt19937) → 根据种子生成高质量伪随机序列
↓
③ 分布(distribution) → 把引擎的原始输出映射到你需要的范围
三、实战例子
3.1 模拟温度:42°C 上下波动 2°C
cpp
#include <random>
#include <mutex>
double simulateTemperature() {
static std::mt19937 rng{std::random_device{}()}; // 引擎只创建一次
static std::mutex mtx; // 保护 rng,它不是线程安全的
std::lock_guard<std::mutex> lock(mtx);
return 42.0 + std::uniform_real_distribution<double>(-2.0, 2.0)(rng);
// ↑ ↑ ↑
// 基准温度 均匀分布 用 rng 引擎
// 区间 [-2.0, 2.0]
}
3.2 模拟误码率:极小数值
cpp
double simulateBER() {
static std::mt19937 rng{std::random_device{}()};
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
return 1.2e-7 + std::uniform_real_distribution<double>(-2e-8, 2e-8)(rng);
// ↑ 0.00000012 ± 0.00000002
}
3.3 生成整数范围
cpp
// 模拟 1 到 100 之间的随机整数
std::uniform_int_distribution<int>(1, 100)(rng);
// 等价于 rand() % 100 + 1,但分布绝对均匀
四、逐行拆解
std::random_device{}()
cpp
std::random_device{} // 创建一个 random_device 临时对象
() // 调用它的 operator(),拿到一个真随机数作为种子
真随机数不是算出来的------它从 CPU 的硬件噪声(热噪、时钟抖动)获得,不可预测。
std::mt19937 rng{种子}
cpp
std::mt19937 rng{std::random_device{}()};
// ↑
// Mersenne Twister 19937 算法
// 周期 = 2^19937 - 1(天文数字)
// 性能和质量都是顶尖水平
std::uniform_real_distribution<double>(min, max)(rng)
cpp
std::uniform_real_distribution<double>(-2.0, 2.0)(rng)
// ↑ 均匀分布 ↑范围 ↑用哪个随机引擎
// 每个值出现的概率完全相等
五、为什么用 static 修饰引擎
cpp
static std::mt19937 rng{std::random_device{}()};
// 如果用 static:第一次调用时初始化一次 → 之后复用一个引擎
// 如果不用 static:每次调用都重建引擎,重新播种
// 高频调用下性能差异巨大
引擎只创建一次、一直复用。 每次调用 dist(rng) 时,引擎继续向前生成下一个随机数,保证随机性。
六、为什么加锁
cpp
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
std::mt19937 不是线程安全的------多个线程同时用同一个 rng 对象会导致内部状态错乱,产生相同的"随机"值。加锁保证同一时刻只有一个线程在用 rng。
七、C++11 还支持其他分布
cpp
// 正态分布:均值 50,标准差 10
std::normal_distribution<double>(50.0, 10.0)(rng);
// 伯努利分布:70% 返回 true
std::bernoulli_distribution(0.7)(rng);
// 整数均匀分布:1~6(骰子)
std::uniform_int_distribution<int>(1, 6)(rng);
这是 rand() 完全做不到的------你需要正态分布数据来模拟传感器噪声?一行搞定。
八、C vs C++11 对比
cpp
// C 语言:模拟温度 40~44
srand(time(NULL));
double temp1 = 40.0 + (rand() % 400) / 100.0;
// C++11:模拟温度 40~44
std::mt19937 rng{std::random_device{}()};
double temp2 = 40.0 + std::uniform_real_distribution<double>(0.0, 4.0)(rng);
// C:模拟掷骰子
int dice1 = rand() % 6 + 1; // 有偏差!低位不够随机
// C++11:模拟掷骰子
int dice2 = std::uniform_int_distribution<int>(1, 6)(rng); // 完全均匀
九、面试该怎么说
"项目中用 C++11 的
<random>库代替 C 的rand()。采用std::mt19937引擎 +std::uniform_real_distribution分布的三层架构------硬件真随机种子、高质量伪随机引擎、均匀分布映射到指定范围。用static保证引擎只初始化一次,用std::mutex保护非线程安全的引擎。这种写法比rand()随机质量更高、API 更清晰、支持多种概率分布。"
嵌入式项目里也常常要用随机数------模拟传感器噪声、测试数据生成、通信超时抖动。C++11 的 <random> 是标准库里的,任何平台都能用。