乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

std::random 随机数库

定义与作用

<random> 是 C++11 引入的随机数标准库,通过"随机数引擎 + 分布"的分离架构,提供高质量、可复现、多场景的随机数生成能力,全面替代 C 风格的 rand() / srand()。

#include <random>

std::mt19937 engine{std::random_device{}()};     // 梅森旋转引擎
std::uniform_int_distribution<int> dist(1, 6);   // 均匀分布 [1, 6]
int dice = dist(engine);                         // 掷骰子
对比rand() / srand()<random>
质量实现简陋,周期短多种高质量引擎可选
范围控制手动取模(有偏)分布类精确控制
可复现性依赖全局状态 srand每个引擎独立状态
线程安全全局状态,不安全每个引擎独立,天然安全
分布类型仅均匀分布均匀/正态/伯努利/泊松等

核心原理

引擎与分布分离架构

常用引擎

引擎特点推荐场景
std::default_random_engine实现定义(通常 mt19937)通用场景
std::mt19937梅森旋转,周期 2^19937-1,高质量需要高质量伪随机数的场景
std::mt19937_64mt19937 的 64 位版本需要 64 位随机数
std::minstd_rand线性同余,快速但质量低对质量要求不高的快速场景
std::ranlux48带奢侈消偏,极高质量科学计算

常用分布

完整示例

示例一:飞翔科技抽奖系统

场景说明:赵鸣为飞翔科技年会设计抽奖系统,需要从 1000 名员工中公平抽取中奖者。

#include <iostream>
#include <random>
#include <vector>
#include <algorithm>
#include <string>
#include <iomanip>

int main() {
    std::cout << "===== 飞翔科技 2018 年会抽奖 =====" << std::endl;

    // 员工名单(模拟)
    std::vector<std::string> employees = {
        "大翔", "白歌", "小崔", "黄俪", "李眉",
        "孔蓝", "赵鸣", "孙鹤", "高英", "杨英",
        "朱璐", "林鸥"
    };

    // 创建引擎:用 random_device 获取真随机种子
    std::random_device rd;
    std::mt19937 engine(rd());

    // ---- 一等奖 (1名):均匀分布抽样 ----
    std::cout << "\n[一等奖 — 均匀分布]" << std::endl;
    std::uniform_int_distribution<std::size_t> firstPrize(0, employees.size() - 1);
    auto winner1_idx = firstPrize(engine);
    std::cout << "🥇 " << employees[winner1_idx] << " 获得一等奖!" << std::endl;

    // ---- 二等奖 (2名):不重复抽取 ----
    std::cout << "\n[二等奖 — 不重复抽样]" << std::endl;
    std::vector<std::size_t> indices(employees.size());
    for (std::size_t i = 0; i < indices.size(); ++i) indices[i] = i;

    // 用 shuffle 代替逐个抽取避免重复
    std::shuffle(indices.begin(), indices.end(), engine);
    std::cout << "🥈 " << employees[indices[0]] << std::endl;
    std::cout << "🥈 " << employees[indices[1]] << std::endl;

    // ---- 三等奖:正态分布模拟评分 ----
    std::cout << "\n[项目评分 — 正态分布模拟]" << std::endl;
    std::normal_distribution<double> scoreDist(75.0, 10.0);  // 均值75,标准差10

    std::cout << "模拟 10 位评委对孔蓝提案的评分:" << std::endl;
    for (int i = 0; i < 10; ++i) {
        double score = std::clamp(scoreDist(engine), 0.0, 100.0);
        std::cout << "  评委" << (i + 1) << ": "
                  << std::fixed << std::setprecision(1) << score << " 分" << std::endl;
    }

    // ---- 阳光普照奖:伯努利分布决定随机金额 ----
    std::cout << "\n[阳光普照奖 — 伯努利随机金额]" << std::endl;
    std::bernoulli_distribution isBig(0.3);  // 30% 概率获得大红包
    std::uniform_int_distribution<int> smallRed(50, 100);
    std::uniform_int_distribution<int> bigRed(188, 888);

    for (const auto& name : {"黄俪", "李眉", "杨英"}) {
        int amount = isBig(engine) ? bigRed(engine) : smallRed(engine);
        std::cout << "  " << name << ": ¥" << amount << std::endl;
    }

    // ---- 固定种子复现 ----
    std::cout << "\n[复现验证 — 固定种子]" << std::endl;
    std::mt19937 fixedEngine(42);  // 固定种子 = 42
    std::uniform_int_distribution<int> fixedDist(1, 100);
    std::cout << "种子=42 的前5个随机数: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << fixedDist(fixedEngine) << " ";
    }
    std::cout << "\n(每次运行结果相同,便于调试复现)" << std::endl;

    return 0;
}

预期输出(每次运行一等奖获奖者不同,但固定种子部分相同):

===== 飞翔科技 2018 年会抽奖 =====

[一等奖 — 均匀分布]
🥇 小崔 获得一等奖!

[二等奖 — 不重复抽样]
🥈 孔蓝
🥈 林鸥

[项目评分 — 正态分布模拟]
模拟 10 位评委对孔蓝提案的评分:
  评委1: 82.3 分
  评委2: 68.7 分
  ...

[阳光普照奖 — 伯努利随机金额]
  黄俪: ¥550
  李眉: ¥72
  杨英: ¥95

[复现验证 — 固定种子]
种子=42 的前5个随机数: 65 25 80 23 17 
(每次运行结果相同,便于调试复现)

逐段分析:

  • std::random_device{}() 获取硬件熵源的真随机种子——确保每次运行不可预测
  • std::shuffle 配合引擎实现不重复抽样,比手动循环 + 检查更简洁高效
  • normal_distribution(75.0, 10.0) 生成均值为 75、标准差为 10 的正态分布——适合模拟自然评分
  • std::clamp(C++17)将评分裁剪到 [0, 100] 范围内,避免异常值
  • bernoulli_distribution(0.3) 以 30% 概率返回 true——实现概率性决策
  • 固定种子 engine(42) 使序列可复现——调试和测试时的关键需求

示例二:随机数生成性能对比与陷阱

场景说明:小崔测试随机数性能并演示常见陷阱。

#include <iostream>
#include <random>
#include <chrono>
#include <cstdlib>   // rand / srand
#include <ctime>

// 计时辅助函数
template <typename F>
double measure_ms(F&& func, int iterations) {
    auto start = std::chrono::steady_clock::now();
    func(iterations);
    auto end = std::chrono::steady_clock::now();
    return std::chrono::duration<double, std::milli>(end - start).count();
}

int main() {
    std::cout << "===== 飞翔科技随机数性能测试 =====" << std::endl;
    const int N = 10'000'000;

    // ---- rand() 方案 ----
    auto test_rand = [](int n) {
        volatile long long sum = 0;
        for (int i = 0; i < n; ++i) {
            sum += rand() % 6 + 1;  // 模拟掷骰子
        }
    };

    // ---- mt19937 + uniform_int_distribution ----
    auto test_mt = [](int n) {
        std::mt19937 eng(42);
        std::uniform_int_distribution<int> dist(1, 6);
        volatile long long sum = 0;
        for (int i = 0; i < n; ++i) {
            sum += dist(eng);
        }
    };

    // ---- minstd_rand(快速引擎) ----
    auto test_minstd = [](int n) {
        std::minstd_rand eng(42);
        std::uniform_int_distribution<int> dist(1, 6);
        volatile long long sum = 0;
        for (int i = 0; i < n; ++i) {
            sum += dist(eng);
        }
    };

    std::srand(static_cast<unsigned>(std::time(nullptr)));

    std::cout << "模拟 " << N << " 次掷骰子:" << std::endl;
    std::cout << "  rand():                "
              << measure_ms(test_rand, N) << " ms" << std::endl;
    std::cout << "  mt19937 + distribution: "
              << measure_ms(test_mt, N) << " ms" << std::endl;
    std::cout << "  minstd_rand + distribution: "
              << measure_ms(test_minstd, N) << " ms" << std::endl;

    // ---- 常见陷阱1:每次调用都创建引擎 ----
    std::cout << "\n[陷阱1: 每次创建引擎]" << std::endl;
    auto bad_loop = []() {
        // 每次循环都创建一个新引擎并播种——极慢且随机性差
        for (int i = 0; i < 5; ++i) {
            std::random_device rd;
            std::mt19937 eng(rd());
            std::uniform_int_distribution<int> dist(1, 6);
            std::cout << "  " << dist(eng);
        }
        std::cout << std::endl;
    };
    auto good_loop = []() {
        // 引擎和分布创建一次,复用
        std::random_device rd;
        std::mt19937 eng(rd());
        std::uniform_int_distribution<int> dist(1, 6);
        for (int i = 0; i < 5; ++i) {
            std::cout << "  " << dist(eng);
        }
        std::cout << std::endl;
    };
    std::cout << "错误做法(每次创建新引擎):"; bad_loop();
    std::cout << "正确做法(复用引擎和分布):"; good_loop();

    // ---- 常见陷阱2:每次调用都创建 random_device(某些平台可能抛异常) ----
    std::cout << "\n[陷阱2: random_device 可能不可用]" << std::endl;
    try {
        std::random_device rd;
        std::cout << "random_device 熵: " << rd.entropy() << std::endl;
        std::cout << "生成一个随机数: " << rd() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "random_device 异常: " << e.what()
                  << " — 回退到时间种子" << std::endl;
        std::mt19937 fallback(
            static_cast<unsigned>(std::chrono::steady_clock::now()
                .time_since_epoch().count()));
    }

    // ---- rand() 取模偏差问题 ----
    std::cout << "\n[陷阱3: rand() % N 的取模偏差]" << std::endl;
    std::cout << "rand() 范围: [0, "
              << RAND_MAX << "]" << std::endl;
    std::cout << "对于 rand() % 6 + 1 掷骰子:" << std::endl;
    std::cout << "  1~4 出现的概率略高于 5~6" << std::endl;
    std::cout << "  因为 32767 % 6 = 1,前 1 个余数多出现一次" << std::endl;
    std::cout << "  uniform_int_distribution 无此问题" << std::endl;

    return 0;
}

预期输出:

===== 飞翔科技随机数性能测试 =====
模拟 10000000 次掷骰子:
  rand():                245.3 ms
  mt19937 + distribution: 312.7 ms
  minstd_rand + distribution: 189.5 ms

[陷阱1: 每次创建引擎]
错误做法(每次创建新引擎):  3  3  3  3  3
正确做法(复用引擎和分布):  4  2  6  1  5

[陷阱2: random_device 可能不可用]
random_device 熵: 32
生成一个随机数: 1842304721

[陷阱3: rand() % N 的取模偏差]
rand() 范围: [0, 32767]
对于 rand() % 6 + 1 掷骰子:
  1~4 出现的概率略高于 5~6
  因为 32767 % 6 = 1,前 1 个余数多出现一次
  uniform_int_distribution 无此问题

逐段分析:

  • minstd_rand 是速度最快的标准引擎,适合对随机质量要求不高但速度敏感的场景,性能甚至优于 rand()
  • mt19937 比 rand() 略慢,但质量远超后者——2^19937-1 的周期 vs 通常 2^31 的周期
  • 陷阱 1:每次循环都创建 random_device + mt19937 极慢,且 random_device 的熵可能耗尽,导致连续产生相同"随机数"
  • 陷阱 2:random_device 在某些嵌入式平台或 MinGW 上可能不可用或熵为 0——需要提供 std::chrono 回退方案
  • 陷阱 3:rand() % N 当 N 不能整除 RAND_MAX + 1 时产生取模偏差——uniform_int_distribution 内部使用拒绝采样消除偏差

易错场景与面试考点

易错场景

场景错误表现正确做法
不设种子或种种子为 0每次运行相同随机序列用 std::random_device 或时间戳做种子
每次调用都创建引擎性能极差、随机性被破坏引擎和分布是重对象,应复用
用 rand() % N取模偏差 + 低质量用 uniform_int_distribution
random_device 不可用未处理MinGW 下可能抛异常提供回退方案(时间戳种子)
多线程共享同一引擎数据竞争每个线程独立的引擎实例,或用 thread_local

常见面试问题

  1. <random> 比 rand()/srand() 好在哪?——三方面:(1) 引擎质量高(mt19937 周期远超 rand()),(2) 分布丰富(正态、伯努利等),(3) 引擎状态独立,天然线程安全,可复现。rand() 有全局状态和取模偏差问题。

  2. 引擎和分布为什么要分离?——单一职责。引擎负责产生均匀分布的原始随机比特,分布负责将原始比特映射到目标分布。分离后可以独立替换:换引擎不影响分布逻辑,换分布不影响引擎。

  3. std::random_device 是什么?和伪随机引擎有何区别?——random_device 尽量使用硬件熵源产生真随机数,不可复现,适合做种子。伪随机引擎(如 mt19937)是确定性的,给定种子产出相同序列,适合需要复现的场景。

  4. 为什么推荐用 std::shuffle 而非手写 Fisher-Yates?——std::shuffle 接受 URBG(Uniform Random Bit Generator)接口,可直接传入标准库引擎,避免手写 rand() % N 的偏差问题。

小结

  • 核心架构:引擎(产生随机比特) + 分布(映射到目标分布),两者独立可换
  • std::random_device 提供真随机种子,std::mt19937 是最常用的高质量引擎
  • 引擎和分布对象应复用,不要每次调用都创建
  • 分布类(uniform_int_distribution / normal_distribution / bernoulli_distribution 等)覆盖常用概率模型
  • 固定种子使序列可复现——调试和单元测试的关键能力
  • 多线程中每个线程应有独立的引擎实例,或使用 thread_local 存储
上一页
Lambda 与算法组合
下一页
自定义迭代器开发