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