rand()是伪随机且默认种子为1,需用srand(time(nullptr))初始化;其范围是[0,RAND_MAX],取模易致分布偏差;C++11后应改用<random>库的mt19937等现代方案。

rand() 生成的不是真随机,是伪随机,而且默认种子固定
每次运行程序,rand() 都会输出完全相同的数字序列——这不是 bug,是设计如此。它依赖一个内部种子值,而 srand() 不调用的话,系统默认用 1 初始化种子。
实操建议:
- 必须在程序开头(且仅一次)调用
srand(time(nullptr)),让种子随时间变化 - 别在循环里反复调用
srand(),否则可能因time(nullptr)分辨率低(秒级),导致多次调用得到相同种子,反而重复出同一串数 -
time(nullptr)在 Windows 下需包含<ctime>,Linux/macOS 同样适用
rand() 返回值范围是 0 到 RAND_MAX,不是 0–1 或 1–100
很多人误以为 rand() 默认返回小数或常用区间整数,其实它只保证返回 [0, RAND_MAX] 内的整数,而 RAND_MAX 至少是 32767(常见实现为 2147483647),远超预期。
常见错误现象:直接写 rand() % 100 想取 0–99,看似可行,但会引入分布偏差——因为 RAND_MAX + 1 很难被 100 整除,余数部分被“截断”,小数字出现概率略高。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 若只要整数区间
[a, b],用a + rand() % (b - a + 1)最简,但注意b - a别太大(比如超过RAND_MAX / 2),否则偏差明显 - 更公平的做法是丢弃超出对齐范围的值:
int r; do { r = rand(); } while (r > RAND_MAX - (RAND_MAX % range) - 1); return a + r % range; - 不要用
(double)rand() / RAND_MAX做浮点归一化——虽然能得 0–1,但分辨率受限于RAND_MAX位数,且double转换不改变底层离散性
C++11 后该用 <random>,不是 <cstdlib> 里的 rand()
rand() 是 C 遗留接口,无状态、不可复现、分布控制弱、线程不安全。C++11 引入的 <random> 才是现代正确用法。
使用场景:需要可重现实验(固定种子)、多线程并发生成、指定分布(正态/泊松/均匀)、避免低比特位周期短等问题时,rand() 必须替换。
实操建议:
- 用
std::mt19937替代rand()(梅森旋转,周期长、速度快、质量高) - 配
std::uniform_int_distribution<int>或std::uniform_real_distribution<double>控制范围和类型 - 种子用
std::random_device获取真熵(若不可用,退回到std::chrono::steady_clock::now().time_since_epoch().count()) - 示例:
std::random_device rd;<br>std::mt19937 gen(rd());<br>std::uniform_int_distribution<int> dis(1, 6);<br>int roll = dis(gen); // 掷骰子
Windows 下 rand() 的低位周期极短,别用 % 取小模数
MSVC 的 rand() 实现中,低位比特变化非常缓慢——rand() % 2 可能连续几十次都返回 0 或 1,% 4、% 8 同样危险。这是经典 LCG 算法的固有缺陷,不是 Windows 特有,但 MSVC 暴露得更明显。
性能 / 兼容性影响:用低位做分支判断(如模拟抛硬币)会导致逻辑严重失真;跨平台代码若依赖 rand() % N,在 Linux 和 Windows 上行为可能不一致。
实操建议:
- 绝对避免
rand() % 2、rand() % 4这类小模数操作 - 若必须用
rand(),至少用高位:(rand() / (RAND_MAX / N + 1))(但不如换<random>彻底) - CI/CD 或测试环境若发现随机行为不一致,优先检查是否混用了
rand()和<random>,或忘了srand()
真正麻烦的不是怎么生成随机数,而是忘记「种子只设一次」「低位不能信」「旧接口不该再用」这三件事。尤其在调试多线程服务或复现实验时,错用 rand() 会让问题藏得很深。










