现代C++推荐用std::random_device和std::mt19937替代rand():前者提供真随机种子,后者为高质量伪随机引擎;须用random_device初始化mt19937,配合uniform_int_distribution等分布对象生成指定范围的均匀随机数,避免取模破坏均匀性。

用 std::random_device 和 std::mt19937 生成高质量随机数
现代 C++ 推荐弃用 rand(),改用 `std::random_device 提供真随机种子(通常来自系统熵),std::mt19937 是高效、周期长的伪随机数引擎。
常见错误是直接用 std::mt19937{} 默认构造——它用固定种子,每次运行结果都一样。
- 正确做法:用
std::random_device初始化引擎,例如std::mt19937 gen{std::random_device{}()} -
std::mt19937适合 32 位整数;若需 64 位,用std::mt19937_64 - 引擎对象应复用,不要每次生成一个新
gen,否则可能因构造开销或种子重复影响性能和随机性
用 std::uniform_int_distribution 生成指定范围整数
引擎只负责产出均匀分布的原始整数(如 uint32_t),要映射到自定义区间(比如 [1, 6] 模拟骰子),必须配合分布对象。直接对 gen() 结果取模(% 6 + 1)会破坏均匀性,尤其当范围不能整除引擎最大值时。
示例:生成 [10, 99] 的随机两位整数
立即学习“C++免费学习笔记(深入)”;
std::mt19937 gen{std::random_device{}()};
std::uniform_int_distribution dist(10, 99);
int x = dist(gen); // 每次调用都返回一个符合分布的 int
- 分布对象可复用,且线程安全(只要引擎不共享)
- 模板参数
int必须与传入的上下界类型一致,否则编译失败 - 闭区间语义:
dist(a, b)包含a和b两个端点
为什么不用 rand() 和 srand(time(nullptr))
rand() 是 C 风格遗留函数,存在多个硬伤:低序位随机性差、最大值由 RAND_MAX 限定(常为 32767)、无法指定分布、且 srand() 只能设一次种子——如果程序启动太快(比如被脚本快速连启多次),time(nullptr) 返回相同值,导致多进程生成完全相同的随机序列。
- 即使加
usleep(1000)或用getpid()混合,也无法解决底层算法缺陷 - 在竞赛或加密场景中,
rand()生成的序列容易被预测,std::mt19937则更可靠 - 某些平台(如 macOS)的
rand()实现甚至不是线性同余,但依然缺乏标准分布支持
生成浮点随机数:用 std::uniform_real_distribution
整数分布不能直接用于 float 或 double;必须显式选用实数分布。注意:它默认生成半开区间 [a, b),即包含 a,不包含 b。
示例:生成 [0.0, 1.0) 的 double
std::mt19937_64 gen{std::random_device{}()};
std::uniform_real_distribution dist(0.0, 1.0);
double d = dist(gen);
- 若需要闭区间 [a, b],可改为
dist(a, std::nextafter(b, INFINITY)),但通常没必要 - 对精度敏感场景(如蒙特卡洛模拟),优先用
double分布而非float,避免舍入偏差累积 - 不要用
static_cast,它分辨率极低且分布不均(rand()) / RAND_MAX
gen.seed() 或序列化其内部数组),而不是分布。









