1. 传统方法:rand()srand()(C语言风格,不推荐在新项目中使用)

工作原理

  • rand(): 返回一个伪随机数,范围通常是 0RAND_MAX(一个常量,如 32767)。
  • srand(seed): 设置随机数种子。如果使用相同的种子,rand()会产生相同的序列。

缺点

  • 低质量:生成的随机数序列质量通常不高。
  • 范围固定:范围固定为 [0, RAND_MAX],需要手动取模来调整范围,但这会引入偏差。
  • 全局状态:使用全局状态,可能影响程序其他部分。
  • 性能:在某些实现中性能不佳。

示例

#include <iostream>
#include <cstdlib> // 需要包含的头文件
#include <ctime>

int main()
{
// 用当前时间作为种子
std::srand(std::time(nullptr));

// 生成一个 0 到 RAND_MAX 之间的随机数
int random_num = std::rand();
std::cout << "Random number: " << random_num << std::endl;

// 生成一个 0 到 99 之间的随机数(有偏差!)
int dice_roll = std::rand() % 100;
std::cout << "0-99: " << dice_roll << std::endl;

// 生成一个 1 到 6 之间的随机数(模拟骰子)
int dice_roll_2 = std::rand() % 6 + 1;
std::cout << "Dice roll: " << dice_roll_2 << std::endl;

return 0;
}

2. 现代方法:<random>库 (C++11 推荐)

<random>库将随机数生成分为两部分:

  1. 随机数引擎 (Engine):负责生成原始的随机数序列(通常是 unsigned integers)。
  2. 随机数分布 (Distribution):负责将引擎产生的数字映射到特定的范围或分布。

这种分离使得它非常灵活和强大。

2.1 随机数引擎

常用的引擎:

引擎类 说明 特点
std::default_random_engine 默认随机引擎 实现定义,通常是一个平衡的选择
std::mt19937 梅森旋转算法 最常用,高质量,周期极长 (2^19937-1)
std::mt19937_64 64位梅森旋转算法 同上,但生成64位随机数
std::minstd_rand 线性同余算法 较老,质量不如梅森旋转
std::random_device 真随机数引擎 尝试使用硬件熵源(如RdRand),用于生成种子

2.2 随机数分布

常用的分布:

分布类 说明 范围/分布
std::uniform_int_distribution<int> 均匀整数分布 [a, b]之间的整数,每个数概率相等
std::uniform_real_distribution<double> 均匀实数分布 [a, b)之间的浮点数
std::normal_distribution<double> 正态分布(高斯分布) 均值 μ,标准差 σ
std::bernoulli_distribution 伯努利分布 truefalse,像抛硬币
std::binomial_distribution<int> 二项分布
std::poisson_distribution<int> 泊松分布

3. 现代方法的通用步骤和最佳实践

  1. 创建引擎:通常选择 std::mt19937
  2. 获取优质种子:使用 std::random_device获取真随机数作为种子。
  3. 初始化引擎:用种子初始化引擎。
  4. 创建分布:选择你需要的分布(如 uniform_int_distribution)。
  5. 生成随机数:将引擎传递给分布对象。

高质量示例

#include <iostream>
#include <random> // 现代随机数库的核心头文件

int main()
{
// 1. 创建一个随机设备来获取真随机种子
std::random_device rd;

// 2. 使用梅森旋转引擎,并用随机设备的输出初始化它
std::mt19937 gen(rd()); // 种子序列:rd()

// 3. 定义一个分布(例如:生成 1 到 6 的均匀整数)
std::uniform_int_distribution<int> dis(1, 6);

// 4. 生成随机数:将引擎传递给分布
for (int n = 0; n < 10; ++n) {
std::cout << dis(gen) << ' '; // 每次调用 dis(gen) 生成一个随机数
}
std::cout << '\n';

// 生成 0.0 到 1.0 之间的均匀分布实数
std::uniform_real_distribution<double> real_dis(0.0, 1.0);
std::cout << "Random double: " << real_dis(gen) << std::endl;

return 0;
}
// 可能的输出: 3 1 6 2 5 4 2 3 1 4
// 每次运行都不同

4. 重要技巧和注意事项

  • 避免在循环中创建引擎和分布:引擎和分布对象可以重复使用。在循环内部创建它们会严重影响性能且可能破坏随机性。

    // 错误示范(性能极差)
    for (int i = 0; i < 100; i++)
    {
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> dis(1, 6);
    std::cout << dis(gen) << ' ';
    }

    // 正确示范(高效)
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> dis(1, 6);
    for (int i = 0; i < 100; i++)
    {
    std::cout << dis(gen) << ' ';
    }
  • 生成无偏的整数范围:现代分布会自动处理范围而无偏差,无需再使用 %

    // 旧方法(有偏差):
    int bad_range = std::rand() % 10; // 生成 0-9,但概率不完全相等

    // 新方法(无偏差):
    std::uniform_int_distribution<int> good_dist(0, 9);
    int good_range = good_dist(gen); // 真正均匀的 0-9

总结

特性 传统 rand() 现代 <random>
推荐度 不推荐(遗留代码) 强烈推荐(新项目)
灵活性 极高(引擎和分布可自由组合)
随机性质量 通常较低 非常高(多种高质量算法)
易用性 简单但不安全(取模有偏差) 稍复杂但安全可靠(分布无偏差)
性能 通常较快 可能稍慢,但质量换性能是值得的

对于任何新的 C++ 项目,都应该使用 <random>,并首选 std::mt19937 引擎和 std::uniform_int_distribution/std::uniform_real_distribution 来满足基本的均匀分布需求。