应使用 std::uniform_int_distribution 配合 std::mt19937 生成指定范围整数,引擎和分布需分离定义,避免循环中重复构造,范围是闭区间 [a, b]。

为什么 rand() 生成的随机数总是一样的?
因为没调用 srand(time(nullptr)) 初始化种子,或者在循环里反复调用 srand 导致种子重复。C++11 之前靠 rand() + srand(),但它的分布不均匀、周期短、线程不安全,现在基本不该用了。
- 每次程序启动时只调用一次
srand(time(nullptr)),别放在循环或频繁调用的函数里 -
rand() % N会产生模偏差(低比特位周期更短),尤其当N不是 2 的幂时,小数字出现概率偏高 - 多线程环境下,
rand()共享全局状态,可能引发数据竞争 —— 即使加锁也难保质量
C++11 后该用哪个类生成指定范围整数?
用 std::uniform_int_distribution 配合 std::mt19937,这是目前最可靠、标准、可移植的选择。
- 必须分开定义引擎和分布对象:引擎负责产原始随机比特,分布负责映射到目标范围
- 引擎建议用局部静态或类成员变量,避免在循环里反复构造 ——
std::mt19937构造开销不小 - 范围是闭区间:
dist(gen)返回的是[a, b]内的整数,不是[a, b)
示例:生成 [10, 99] 的随机整数
std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<int> dist(10, 99); int r = dist(gen); // 每次调用都得到一个新值
生成 [0.0, 1.0) 浮点随机数要注意什么?
用 std::uniform_real_distribution,但注意模板参数类型要匹配引擎输出精度 —— std::mt19937 是 32 位,对应 float;std::mt19937_64 才适合 double。
立即学习“C++免费学习笔记(深入)”;
- 写成
std::uniform_real_distribution<double>(0.0, 1.0)时,如果引擎是std::mt19937,会损失精度,且某些实现可能报错或截断 - 区间是左闭右开:
(0.0, 1.0)不包含 1.0,但可能返回 0.0 - 别用
rand() / (double)RAND_MAX—— 它只有约 15 位有效精度,且分布仍不均
跨平台生成可复现的随机序列怎么做?
用固定种子初始化 std::mt19937,比如 std::mt19937 gen(12345)。只要引擎、分布、编译器标准库一致,结果就确定。
-
std::random_device在 Windows 上常退化为伪随机(基于时间),不能用于复现;Linux/macOS 通常读取/dev/urandom,也不稳定 - 不要依赖
std::random_device::entropy()判断是否真随机 —— 它返回值无统一语义,MSVC 总返回 0 - 测试或调试时,固定种子比“看起来更随机”重要得多;上线再换回
std::random_device
真正麻烦的是分布对象的构造参数顺序、引擎类型、甚至标准库实现细节 —— 比如 libstdc++ 和 libc++ 对同一段代码的浮点分布结果可能有微小差异,别假设“标准=完全一致”。










