std::uniform_int_distribution配合std::mt19937是C++11起唯一推荐的高性能无偏整数随机生成方式;它用拒绝采样保证闭区间均匀性,需以std::random_device初始化引擎、复用分布对象,多线程下须隔离引擎实例。

用 std::uniform_int_distribution 生成整数范围随机数
直接用 rand() 取模会严重破坏均匀性,尤其当范围不能整除 RAND_MAX+1 时,小数值概率明显偏高。C++11 起标准推荐用 std::uniform_int_distribution 配合真随机引擎(如 std::mt19937)。
关键点:分布对象应复用,不要每次生成都新建;引擎需用非确定性种子初始化(如 std::random_device)。
-
std::mt19937比rand()周期长、统计特性好,且线程安全(引擎实例不共享时) - 分布对象构造时传入闭区间
[a, b],例如std::uniform_int_distribution<int>(1, 6)</int>生成 1–6 的整数 - 避免把分布对象定义在循环内——它内部有状态缓存,频繁构造会丢弃部分随机性
std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<int> dist(10, 99); // 生成两位随机整数 int x = dist(gen); // 每次调用都产生新值
生成浮点数范围随机数用 std::uniform_real_distribution
整数分布不能直接用于浮点需求,比如 [0.0, 1.0) 或 [-π, π)。必须换用 std::uniform_real_distribution,它默认生成左闭右开区间,且对 float/double 类型行为明确。
注意:该分布不接受整数类型模板参数;若传入 int 会编译失败;边界值必须是浮点字面量(如 0.0 而非 0)。
立即学习“C++免费学习笔记(深入)”;
- 区间为
[a, b),不是数学常用闭区间;若需闭区间 [a,b],需手动处理(如对double加极小偏移) - 引擎仍用
std::mt19937即可,无需改用std::mt19937_64—— 浮点分布只取引擎输出的部分位 - 不要用
static_cast<double>(rand()) / RAND_MAX</double>,精度低、范围窄、非均匀
std::uniform_real_distribution<double> dist(0.0, 1.0); double r = dist(gen); // 生成 [0.0, 1.0) 内的 double
常见错误:重复使用同一种子或全局共享引擎
如果用固定种子(如 std::mt19937 gen(42)),每次运行程序都得到完全相同的随机数序列;若多个函数共用同一个 gen 实例又没加锁,多线程下会触发未定义行为。
- 单线程场景下,把
gen和dist定义为static局部变量是安全且高效的 - 多线程场景下,每个线程应持有独立的
gen实例(std::random_device构造成本低,可在线程入口处调用) - 绝对不要在头文件里定义非 const 全局随机引擎——会导致 ODR 违规和链接冲突
兼容旧代码时如何安全替换 rand()
遗留项目中若大量使用 rand() % N + M,不能简单替换为 dist(gen) 就完事。要确认原逻辑是否依赖 rand() 的低质量周期或特定偏差(极少数测试/仿真场景),否则替换后行为变化可能暴露隐藏 bug。
- 先用
#define rand() __rand_replacement()临时拦截,记录原始输出分布,再对比新实现 - 若原范围是
rand() % 100,对应新写法是std::uniform_int_distribution<int>(0, 99)</int>,不是(1, 100) - Windows 上
RAND_MAX是 32767,而 Linux 通常是 2147483647,跨平台时取模逻辑结果本就不一致——这反而是升级的正当理由
真正麻烦的不是语法转换,而是那些把随机数当成“伪唯一 ID”或“简易哈希”来用的隐式假设;这些地方改完必须重测。


















