rand() 因随机质量差、模偏差、线程不安全且依赖全局状态,现已不推荐使用;应改用 std::mt19937 配合 std::random_device 和分布类,确保线程安全与高质量随机性。

rand() 为什么现在不推荐用了
因为 rand() 是 C 标准库遗留函数,生成的伪随机数质量差、范围固定(RAND_MAX 通常只有 32767),且无法控制分布类型。更严重的是,它依赖全局状态,多线程下不安全,且种子只能用 srand() 设一次——哪怕你反复调用 srand(time(nullptr)),在快速循环中极易得到重复序列。
常见错误现象:rand() % 100 看似生成 0–99,但若 RAND_MAX 不是 100 的整数倍,低值概率会略高(模偏差);用 time(nullptr) 在一秒内多次初始化,结果全一样。
- 不要用
rand()做密码学、模拟、游戏逻辑等对随机性有要求的场景 - 不要在循环里反复调用
srand() - 避免
rand() % N,改用rand() / (RAND_MAX / N + 1)(仍不推荐,仅作对比理解)
mt19937 是什么,怎么正确初始化
std::mt19937 是 C++11 引入的 Mersenne Twister 引擎,周期长(2¹⁹⁹³⁷−1)、统计性质好、速度快,是当前最常用的标准随机引擎。但它本身只产生均匀分布的 32 位无符号整数(uint32_t),必须配合分布类(如 std::uniform_int_distribution)才能得到指定范围或类型的随机值。
关键点:引擎要独立实例化,不能全局共享;种子应使用 std::random_device 获取真随机熵,而非 time(nullptr)。
立即学习“C++免费学习笔记(深入)”;
std::random_device rd; // 真随机源(通常读 /dev/urandom 或 CryptGenRandom) std::mt19937 gen(rd()); // 用真随机数初始化 mt19937 std::uniform_int_distributiondis(1, 100); // 定义 [1, 100] 均匀分布 int x = dis(gen); // 每次调用生成一个 int
-
std::random_device可能不可用或退化为伪随机(如 MinGW),可用rd.entropy() == 0判断 - 若需可复现结果(如单元测试),可传固定种子:
std::mt19937 gen(42) - 引擎对象(
gen)建议按需创建或作为局部静态变量,避免跨线程共享
生成不同范围和类型的随机数
分布类负责把引擎输出映射到目标范围与类型,不是引擎本身决定的。同一个 std::mt19937 实例可搭配多个分布对象,互不影响。
- 生成 [0, 1) 的浮点数:
std::uniform_real_distribution(注意右边界不包含)dis(0.0, 1.0) - 生成 char 范围的字母:
std::uniform_int_distribution,再转dis('a', 'z') char(dis(gen)) - 生成负数区间 [-10, 10]:
std::uniform_int_distributiondis(-10, 10) - 避免写
dis(gen) % 10——分布类已处理模偏差,直接用它即可
性能提示:分布对象构造开销小,可复用;引擎调用是主要耗时,但 mt19937 很快,一般无需缓存结果。
多线程环境下怎么安全用
每个线程必须拥有自己的 std::mt19937 实例。共享引擎会导致数据竞争,结果不可预测,且破坏随机性。
典型错误:把 gen 声明为全局或静态变量,然后多线程并发调用 dis(gen)。
- 推荐方式:在线程函数内创建引擎和分布,或使用线程局部存储(
thread_local std::mt19937 gen(rd())) -
std::random_device通常是线程安全的,但某些实现可能不是;若担心,可在主线程生成一个种子,再用它初始化各线程的mt19937 - 不要试图用 mutex 保护单个引擎——锁开销大,且违背随机数生成的并发意图
真正容易被忽略的是:即使你只用一个线程,如果对象生命周期管理不当(比如分布对象比引擎先销毁),也可能引发未定义行为。确保分布对象的生存期不长于它所使用的引擎。







