现代C++应使用std::random_device和std::mt19937初始化随机引擎,避免rand()的可预测性与线程不安全;需用硬件熵源种子、显式调用seed、配合uniform分布控制范围,并注意引擎线程不安全而分布对象可共享复用。

用 std::random_device 和 std::mt19937 初始化随机引擎
直接用 rand() 生成的数质量差、可预测,且在多线程下不安全。现代 C++ 应该用标准库的随机设施,核心是先选一个真随机种子,再配一个高效的伪随机引擎。
常见错误是只用 time(nullptr) 当种子——它秒级精度,短时间重复运行会得到完全相同的序列;或者漏掉引擎的 seed 步骤,导致每次都是默认种子(固定序列)。
-
std::random_device rd用来获取硬件或系统级熵源,适合做种子 -
std::mt19937 gen(rd())是推荐的主力引擎,速度快、周期长(2¹⁹⁹³⁷−1)、分布均匀 - 如果需要可复现结果(比如单元测试),可以显式传入固定整数种子:
std::mt19937 gen(42)
用 std::uniform_int_distribution 或 std::uniform_real_distribution 控制范围
引擎本身只输出原始整数,真正决定“随机数长什么样”的是分布对象。不指定分布,就只能拿到 gen() 返回的原始大整数(通常是 uint32_t 或 uint64_t),没法直接当数组元素用。
注意:分布对象不是一次性消耗品,它可重用,且线程安全(只要不共享同一个引擎实例)。
立即学习“C++免费学习笔记(深入)”;
- 生成
int数组(如 0–99):std::uniform_int_distribution<int> dist(0, 99)</int> - 生成
double数组(如 [0.0, 1.0)):std::uniform_real_distribution<double> dist(0.0, 1.0)</double> - 范围是闭区间还是左闭右开?
uniform_int_distribution是闭区间,uniform_real_distribution默认左闭右开
填充 std::vector 或原生数组的两种写法
别手写 for 循环调用 dist(gen) 十次——既啰嗦又容易越界。标准库提供了更简洁、更不易出错的方式。
- 对
std::vector<int>:先构造大小,再用std::generate填充std::vector<int> arr(10);<br>std::generate(arr.begin(), arr.end(), [&]() { return dist(gen); }); - 对原生数组(如
int arr[10]):同样可用std::generate,但注意传迭代器时别越界int arr[10];<br>std::generate(std::begin(arr), std::end(arr), [&]() { return dist(gen); }); - 如果用 C++20,还能配合
std::ranges::generate写得更直白,但兼容性需确认
避免常见陷阱:线程安全与性能边界
多个线程共用同一个 std::mt19937 实例会崩溃——它不是线程安全的。但分布对象(dist)是无状态的,可共享。
另一个容易被忽略的点:频繁构造引擎和分布对象有开销。如果只是偶尔生成几个数,没问题;但如果在热循环里反复 new/destroy,就该把它们提到外层作用域,复用实例。
- 不要在循环内写
std::mt19937 gen(rd())—— 每次都重新 seed,可能撞上相同值 - 不要为每个数组元素新建一个分布对象 —— 构造开销小但没必要
- 如果要生成大量数据(比如百万级),考虑用
std::vector::reserve()预分配内存,避免多次 realloc
真正麻烦的不是“怎么生成”,而是“怎么让每次运行都不一样”和“怎么不让并发时崩掉”。这两个点卡住了多数人的实际使用。


















