跳转至

C++时间和随机数相关库

约 2667 个字 346 行代码 1 张图片 预计阅读时间 13 分钟

基础:编译时有理数算术(<ratio>

编译时有理数与ratio

在接下来介绍ratio类之前,先了解何为「编译时有理数」

「编译时有理数」指的是其值在程序编译期间就已经确定并且可以进行计算的有理数(分数)

在C++中,std::ratio就是用来表示这种编译时有理数的工具。关键点在于:

  1. 模板定义std::ratio<Num, Den>使用模板参数Num(分子)和Den(分母)来定义一个分数。这些参数必须是编译时就能确定的整数常量
  2. 类型表示std::ratio<1, 1000>本身是一个类型,而不是一个运行时变量。它的值(1/1000)是这个类型的一个属性
  3. 编译期计算:所有涉及std::ratio的算术(如std::ratio_add)和比较(如 std::ratio_less)操作都在编译时完成。编译器会计算出结果,并生成一个新的 std::ratio类型
  4. 无运行时开销:由于计算在编译时完成,直接操作std::ratio类型本身(例如,确定std::milli::numstd::milli::den)在运行时没有计算开销
  5. 用于元编程和常量:它们非常适合用于模板元编程和定义固定的、在编译时就需要确定的常量比例,就像<chrono>库中定义时间单位(毫秒、微秒等)那样

简单来说,它是一种在编译代码时由编译器处理和计算的分数表示方法,而不是等到程序运行时再计算

基本介绍

<chrono>库广泛使用<ratio>库来表示编译时的分数,这对于定义时间单位(例如秒、毫秒)至关重要

std::ratio 是一个模板类,用于表示编译时的有理数 Num / Den

C++
1
2
template <std::intmax_t Num, std::intmax_t Den = 1>
class ratio;

其中 Num 是分子(numerator),Den 是分母(denominator),分母必须非零,且分数会自动约简

成员变量

成员 类型 描述
num std::intmax_t 约简后的分子
den std::intmax_t 约简后的分母

关于类型的定义,可以在C语言头文件<stdint.h>中查看

预定义Ratio类型

标准库提供了一些常用的SI单位前缀的 ratio 类型别名,例如(由小到大) std::attostd::femtostd::picostd::nanostd::microstd::millistd::centistd::decistd::decastd::hectostd::kilostd::megastd::gigastd::terastd::petastd::exa

单位对照表如下:

Ratio算术

<ratio>提供了编译时算术运算的类型别名,包括如下:

  1. std::ratio_add(加法)
  2. std::ratio_subtract(减法)
  3. std::ratio_multiply(乘法)
  4. std::ratio_divide(除法)

Ratio比较

<ratio>提供了编译时比较运算的类型别名,包括 1. std::ratio_equal(等于) 2. std::ratio_not_equal(不等于) 3. std::ratio_less(小于) 4. std::ratio_less_equal(小于等于) 5. std::ratio_greater(大于) 6. std::ratio_greater_equal(大于等于)

示例代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <ratio>

int main() 
{
    // 定义ratio类型
    using one_third = std::ratio<1, 3>;
    using two_fourths = std::ratio<2, 4>; // 会被约简为1/2

    // 访问num和den
    std::cout << "one_third: " << one_third::num << "/" << one_third::den << std::endl;
    std::cout << "two_fourths: " << two_fourths::num << "/" << two_fourths::den << std::endl;

    // Ratio算术
    using sum = std::ratio_add<one_third, two_fourths>; // 1/3 + 1/2 = 5/6
    std::cout << "Sum: " << sum::num << "/" << sum::den << std::endl;

    using product = std::ratio_multiply<one_third, std::kilo>; // (1/3) * (1000/1) = 1000/3
    std::cout << "Product: " << product::num << "/" << product::den << std::endl;

    // Ratio比较
    // 比较1/3和1/2
    bool less = std::ratio_less<one_third, two_fourths>::value;
    std::cout << "Is 1/3 less than 1/2? " << std::boolalpha << less << std::endl; // 输出rue

    return 0;
}

时间工具(<chrono>

<chrono> 库提供了处理时间的三大核心组件:时钟(Clocks)、时间段(Durations)和时间点(Time points)

时钟(Clocks)

时钟提供了对当前时间点的访问,C++标准库定义了三种主要时钟:

  1. std::chrono::system_clock代表系统范围的实时时钟(挂钟时间),它是唯一可以映射到C风格time_t的时钟,但不一定是单调递增的
  2. std::chrono::steady_clock代表单调递增的时钟,时间点永不减少,最适合用于测量时间间隔
  3. std::chrono::high_resolution_clock提供具有最小可用时钟周期的时钟,通常是 system_clocksteady_clock的别名

单调递增时钟与非单调递增时钟

  • 单调递增的时钟就像一个秒表计数器**:它只管从某个起点开始一直往前数时间,不受外界干扰,非常适合测量经过了多长时间
  • 不单调递增的时钟就像电脑右下角的系统时间**:它显示的是当前的“挂钟时间”,这个时间可能会因为人为修改、网络同步、夏令时等原因向前或向后跳变

system_clock 成员

  1. now() noexcept:(静态)返回表示当前时间的time_point
  2. to_time_t(const time_point& t) noexcept:(静态)将time_point转换为 std::time_t
  3. from_time_t(std::time_t t) noexcept:(静态)将std::time_t转换为 time_point
  4. is_steady:(静态)false,表示时钟不是单调递增的

steady_clock 成员

  1. now() noexcept:(静态)返回表示当前时间的time_point
  2. is_steady:(静态)true,表示时钟是单调递增的

high_resolution_clock 成员

  1. now() noexcept:(静态)返回表示当前时间的time_point
  2. is_steady:(静态)取决于它所别名的时钟

示例代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <chrono>
#include <ctime> // for std::time_t, std::gmtime, std::put_time
#include <iomanip> // for std::put_time
#include <thread> // for std::this_thread::sleep_for

int main() 
{
    // --- system_clock ---
    std::chrono::system_clock::time_point sys_now = std::chrono::system_clock::now();
    std::time_t sys_now_c = std::chrono::system_clock::to_time_t(sys_now);
    // 将 time_t 转换为 UTC 时间结构
    std::tm sys_now_tm = *std::gmtime(&sys_now_c);
    std::cout << "System clock time: " << std::put_time(&sys_now_tm, "%Y-%m-%d %H:%M:%S UTC") << std::endl;

    // --- steady_clock ---
    std::chrono::steady_clock::time_point steady_start = std::chrono::steady_clock::now();
    std::cout << "Starting steady clock measurement..." << std::endl;
    // 模拟一些工作
    // 线程睡眠
    std::this_thread::sleep_for(std::chrono::milliseconds(150));
    std::chrono::steady_clock::time_point steady_end = std::chrono::steady_clock::now();
    // 计算时间差 (duration)
    std::chrono::duration<double, std::milli> elapsed_ms = steady_end - steady_start;
    std::cout << "Elapsed time (steady_clock): " << elapsed_ms.count() << " ms" << std::endl;

    // --- high_resolution_clock ---
    std::chrono::high_resolution_clock::time_point high_res_now = std::chrono::high_resolution_clock::now();
    // high_resolution_clock通常没有直接转换为time_t的方法,因为它可能是 steady_clock
    // 但我们可以获取自纪元以来的时间
    auto high_res_epoch = high_res_now.time_since_epoch();
    // 将其转换为纳秒
    long long ns = std::chrono::duration_cast<std::chrono::nanoseconds>(high_res_epoch).count();
    std::cout << "High resolution clock (nanoseconds since epoch): " << ns << std::endl;
    std::cout << "High resolution clock is steady: " << std::boolalpha << std::chrono::high_resolution_clock::is_steady << std::endl;


    return 0;
}

put_time函数

std::put_time是C++标准库<iomanip>中的一个输出流操纵符(output stream manipulator)

它的作用是:

  1. 接收一个指向std::tm结构体的指针std::tm结构体包含了年、月、日、时、分、秒等日历时间信息
  2. 接收一个格式化字符串:这个字符串指定了如何将std::tm中的时间信息转换成文本表示。它使用与C语言函数strftime相同的格式代码(例如%Y代表四位数年份,%m代表月份,%d代表日期,%H代表小时(24小时制),%M代表分钟,%S代表秒)
  3. 格式化时间:根据提供的格式字符串,将std::tm结构体中的时间信息格式化成一个字符串
  4. 插入到输出流:将格式化后的时间字符串插入到它所应用的输出流中(例如 std::cout

时间段(Durations)

std::chrono::duration 表示一个时间间隔,由一个计数值和一个表示计数的单位(例如秒、毫秒)的ratio组成

C++
1
2
template <class Rep, class Period = std::ratio<1>>
class duration;
其中 Rep 是用于表示计数值的算术类型(例如 intlongdouble),Period 是一个std::ratio类型,表示每个计数值代表的秒数(例如std::milli表示每个计数值是\(\frac{1}{1000}\)秒)

成员函数

成员 描述
count() 返回duration的计数值
zero() (静态 C++11/14)返回零长度的duration
min() (静态 C++11/14)返回可表示的最小duration
max() (静态 C++11/14)返回可表示的最大duration

预定义Duration类型

标准库提供了常用的duration类型别名,包括:

  1. std::chrono::nanoseconds
  2. std::chrono::microseconds
  3. std::chrono::milliseconds
  4. std::chrono::seconds
  5. std::chrono::minutes
  6. std::chrono::hours
  7. std::chrono::days(C++20)
  8. std::chrono::weeks(C++20)
  9. std::chrono::months(C++20)
  10. std::chrono::years(C++20)

Duration运算

Duration支持算术运算(+-*(与数值)、/(与数值或duration)、%(与数值或duration)和比较运算(==!=<<=>>=),比较会自动处理不同单位的转换

duration类型转换

std::chrono::duration_cast 用于在不同类型的duration之间进行显式转换,转换可能会截断(不会四舍五入)

C++
1
2
template <class ToDuration, class Rep, class Period>
constexpr ToDuration duration_cast(const duration<Rep, Period>& d);

示例代码

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <chrono>

int main() 
{
    // 创建 durations
    std::chrono::seconds s(5);                     // 5 秒
    std::chrono::milliseconds ms = std::chrono::milliseconds(1500); // 1500 毫秒
    std::chrono::duration<double, std::ratio<1, 3>> third_of_sec(1.0); // 1/3 秒

    // 访问count()
    std::cout << "Seconds count: " << s.count() << std::endl;
    std::cout << "Milliseconds count: " << ms.count() << std::endl;
    std::cout << "Third of second count: " << third_of_sec.count() << std::endl;

    // Duration 算术
    auto total_ms = s + ms; // 5s + 1500ms = 6500ms
    // 注意:结果类型是两个操作数周期的最大公约数,表示类型是std::common_type
    // 在这里,结果类型可能是milliseconds或更精细的类型
    // 为了明确,我们通常使用duration_cast
    std::chrono::milliseconds total_ms_casted = std::chrono::duration_cast<std::chrono::milliseconds>(s) + ms;
    std::cout << "Total milliseconds: " << total_ms_casted.count() << std::endl;

    auto half_s = s / 2; // 结果是2秒 (整数除法)
    std::cout << "Half of 5 seconds (integer division): " << half_s.count() << std::endl;

    std::chrono::duration<double> s_double = s; // 隐式转换为double类型的秒
    auto precise_half_s = s_double / 2.0; // 结果是2.5秒
    std::cout << "Half of 5 seconds (double division): " << precise_half_s.count() << std::endl;

    // Duration比较
    if (ms < s) {
        std::cout << "1500 ms is less than 5 s" << std::endl;
    }

    // duration_cast
    std::chrono::seconds ms_to_s = std::chrono::duration_cast<std::chrono::seconds>(ms); // 1500ms -> 1s (截断)
    std::cout << "1500 ms casted to seconds: " << ms_to_s.count() << std::endl;

    std::chrono::microseconds s_to_us = std::chrono::duration_cast<std::chrono::microseconds>(s); // 5s -> 5,000,000 us
    std::cout << "5 s casted to microseconds: " << s_to_us.count() << std::endl;

    // C++14 字面量 (需要 using namespace std::chrono_literals)
    using namespace std::chrono_literals;
    auto literal_duration = 1h + 30min + 5s + 100ms + 20us + 300ns;
    std::cout << "Literal duration in nanoseconds: "
              << std::chrono::duration_cast<std::chrono::nanoseconds>(literal_duration).count() << std::endl;


    return 0;
}

时间点(Time Points)

std::chrono::time_point 表示一个特定的时间点,它关联到一个时钟,并表示为从该时钟纪元(epoch)开始的一段 duration

C++
1
2
template <class Clock, class Duration = typename Clock::duration>
class time_point;
其中 Clock 是关联的时钟类型(例如 system_clocksteady_clock),Duration 是用于表示从纪元开始的时间间隔的 duration 类型(默认为关联时钟的 duration 类型)

成员函数

成员 描述
time_since_epoch() 返回从关联时钟的纪元开始到此时间点的 duration
min() (静态 C++11/14)返回可表示的最早时间点
max() (静态 C++11/14)返回可表示的最晚时间点

Time Point 运算

Time Point 支持与 duration 的加减运算(time_point + durationtime_point - duration),以及相同类型时钟的 time_point 之间的减法(time_point - time_point,结果为 duration)和比较运算(==!=<<=>>=

std::chrono::time_point_cast

std::chrono::time_point_cast 用于在具有相同关联时钟但不同 duration 类型的 time_point 之间进行显式转换

C++
1
2
3
template <class ToDuration, class Clock, class Duration>
constexpr std::chrono::time_point<Clock, ToDuration>
time_point_cast(const std::chrono::time_point<Clock, Duration>& tp);

代码示例

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <chrono>
#include <thread> // for sleep_for

int main() {
    using namespace std::chrono; // 方便使用 chrono 下的类型

    // 获取当前时间点
    system_clock::time_point tp_sys_now = system_clock::now();
    steady_clock::time_point tp_steady_now = steady_clock::now();

    // 获取自纪元以来的时间
    auto sys_epoch_duration = tp_sys_now.time_since_epoch();
    auto steady_epoch_duration = tp_steady_now.time_since_epoch();

    // 转换为特定单位
    long long sys_ms_count = duration_cast<milliseconds>(sys_epoch_duration).count();
    long long steady_ns_count = duration_cast<nanoseconds>(steady_epoch_duration).count();

    std::cout << "System clock time_point (ms since epoch): " << sys_ms_count << std::endl;
    std::cout << "Steady clock time_point (ns since epoch): " << steady_ns_count << std::endl;

    // Time point 算术
    system_clock::time_point tp_sys_later = tp_sys_now + minutes(10); // 10 分钟后的时间点
    auto diff_duration = tp_sys_later - tp_sys_now; // 计算差值,结果是 duration
    std::cout << "Difference is: " << duration_cast<seconds>(diff_duration).count() << " seconds" << std::endl;

    // Time point 比较
    steady_clock::time_point tp_steady_start = steady_clock::now();
    std::this_thread::sleep_for(50ms); // 暂停 50 毫秒
    steady_clock::time_point tp_steady_end = steady_clock::now();

    if (tp_steady_end > tp_steady_start) {
        std::cout << "Steady clock time point increased." << std::endl;
    }

    // time_point_cast
    // 将 system_clock 的 time_point 精度改为秒
    time_point<system_clock, seconds> tp_sys_seconds = time_point_cast<seconds>(tp_sys_now);
    // 再次获取自纪元以来的时间 (以秒为单位)
    std::cout << "System clock time_point (seconds since epoch): "
              << tp_sys_seconds.time_since_epoch().count() << std::endl;


    return 0;
}

C++14 时间字面量

C++14 引入了方便的字面量来创建 duration 对象,例如 h(小时)、min(分钟)、s(秒)、ms(毫秒)、us(微秒)和 ns(纳秒),使用时需要 using namespace std::chrono_literals;using namespace std::literals::chrono_literals;

C++
1
2
3
using namespace std::chrono_literals;
auto my_duration = 2h + 45min + 10s;
std::cout << "Duration in seconds: " << my_duration.count() << std::endl; // 输出 9910

随机数生成(<random>

<random> 库提供了一套用于生成伪随机数的组件,主要包括引擎(Engines,生成原始伪随机整数序列)、分布(Distributions,将引擎序列转换为特定统计属性的随机数)和工具(Utilities,例如种子生成器)

随机数引擎(Engines)

引擎是函数对象,调用 operator() 会返回序列中的下一个伪随机数

常用引擎

常用的引擎包括 linear_congruential_engine(线性同余生成器,速度快但随机性一般,常用别名 minstd_rand0minstd_rand)、mersenne_twister_engine(梅森旋转算法,高质量随机数,常用别名 mt19937mt19937_64)和 subtract_with_carry_engine(带借位减法生成器,常用别名 ranlux24_baseranlux48_base

引擎通用成员

引擎通常包含接受种子值进行初始化的构造函数、生成下一个随机数的 operator()、使用新种子重新初始化的 seed() 方法、返回生成范围的静态 min()max() 方法,以及跳过序列中若干数字的 discard(n) 方法

std::random_device

std::random_device 是一个特殊的随机数生成器,它尝试从硬件(或其他非确定性来源)获取熵来生成随机数,通常用于为伪随机数引擎提供种子,其 operator() 返回一个随机数,entropy() 返回熵的估计值(位数,若为零则可能使用伪随机数引擎作为后备)

代码示例(引擎和 seeding)

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <random>
#include <vector>
#include <numeric> // for std::iota
#include <algorithm> // for std::shuffle

int main() {
    // --- 使用 random_device 获取种子 ---
    std::random_device rd; // 获取一个随机设备
    unsigned int seed = rd(); // 用它生成一个种子
    std::cout << "Seed generated by random_device: " << seed << std::endl;
    std::cout << "random_device entropy: " << rd.entropy() << std::endl;

    // --- 创建和使用引擎 ---
    // 使用 random_device 的种子初始化梅森旋转引擎
    std::mt19937 engine(seed);

    std::cout << "Generating 5 numbers using mt19937:" << std::endl;
    for (int i = 0; i < 5; ++i) {
        // 调用 operator() 生成原始随机整数
        std::cout << engine() << " ";
    }
    std::cout << std::endl;

    // 引擎的范围
    std::cout << "mt19937 min: " << engine.min() << std::endl;
    std::cout << "mt19937 max: " << engine.max() << std::endl;

    // discard
    engine.discard(10); // 跳过 10 个数
    std::cout << "Next number after discarding 10: " << engine() << std::endl;

    // 重新 seeding
    engine.seed(12345); // 使用固定种子
    std::cout << "First number after seeding with 12345: " << engine() << std::endl;
    engine.seed(12345); // 再次使用相同种子
    std::cout << "First number after seeding with 12345 again: " << engine() << std::endl; // 输出应与上一个相同


    return 0;
}

随机数分布(Distributions)

分布是函数对象,它接受一个引擎作为输入,并使用引擎生成的原始随机数来产生符合特定概率分布的随机数

通用成员

分布通常包含接受分布参数(例如范围、均值、标准差等)的构造函数、接受引擎引用并返回符合分布随机数的 operator()、重置分布内部状态的 reset() 方法、获取或设置分布参数对象的 param() 方法、返回分布可能生成范围的 min()max() 方法,以及访问特定分布参数的函数(例如 mean()stddev()p()lambda() 等)

常用分布类别

常用的分布类别包括:

  • 均匀分布(Uniform Distributions):例如 std::uniform_int_distribution(整数)和 std::uniform_real_distribution(浮点数)
  • 伯努利分布(Bernoulli Distributions):例如 std::bernoulli_distributionstd::binomial_distributionstd::negative_binomial_distributionstd::geometric_distribution
  • 泊松分布(Poisson Distributions):例如 std::poisson_distributionstd::exponential_distributionstd::gamma_distributionstd::weibull_distributionstd::extreme_value_distribution
  • 正态分布(Normal Distributions):例如 std::normal_distributionstd::lognormal_distributionstd::chi_squared_distributionstd::cauchy_distributionstd::fisher_f_distributionstd::student_t_distribution
  • 抽样分布(Sampling Distributions):例如 std::discrete_distributionstd::piecewise_constant_distributionstd::piecewise_linear_distribution

代码示例(引擎与分布结合)

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <random>
#include <vector>
#include <map>
#include <iomanip> // for std::fixed, std::setprecision

int main() {
    // --- 设置引擎 ---
    std::random_device rd;
    std::mt19937 gen(rd()); // 使用 random_device 播种

    // --- 均匀分布 ---
    std::uniform_int_distribution<> distrib_int(1, 6); // [1, 6] 整数
    std::uniform_real_distribution<> distrib_real(0.0, 1.0); // [0.0, 1.0) 浮点数

    std::cout << "Rolling a die 5 times: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << distrib_int(gen) << " "; // 使用引擎生成符合分布的数
    }
    std::cout << std::endl;

    std::cout << "Generating 5 random doubles [0.0, 1.0): ";
    std::cout << std::fixed << std::setprecision(4); // 设置输出格式
    for (int i = 0; i < 5; ++i) {
        std::cout << distrib_real(gen) << " ";
    }
    std::cout << std::endl;

    // --- 正态分布 ---
    double mean = 5.0;
    double stddev = 2.0;
    std::normal_distribution<> distrib_normal(mean, stddev);

    std::cout << "Generating 10 numbers from normal distribution (mean=5.0, stddev=2.0):" << std::endl;
    std::map<int, int> hist; // 用于统计直方图
    for (int i = 0; i < 10000; ++i) {
        double num = distrib_normal(gen);
        ++hist[std::round(num)]; // 四舍五入并计数
    }
    // 简单打印直方图
    for(auto const& [val, count] : hist) {
        // 限制输出范围,避免过多输出
        if (val >= 0 && val <= 10) {
             std::cout << std::setw(2) << val << ": " << std::string(count / 100, '*') << '\n';
        }
    }


    // --- 离散分布 ---
    std::vector<double> weights = {10.0, 20.0, 70.0}; // 权重对应索引 0, 1, 2
    // 索引 0 的概率是 10/(10+20+70) = 10%
    // 索引 1 的概率是 20/(10+20+70) = 20%
    // 索引 2 的概率是 70/(10+20+70) = 70%
    std::discrete_distribution<> distrib_discrete(weights.begin(), weights.end());

    std::cout << "Generating 10 numbers from discrete distribution (weights 10, 20, 70): ";
    std::map<int, int> discrete_counts;
    for (int i = 0; i < 1000; ++i) {
        discrete_counts[distrib_discrete(gen)]++;
    }
     for(auto const& [val, count] : discrete_counts) {
        std::cout << "Index " << val << ": " << count << " times ";
    }
    std::cout << std::endl;

    // 访问分布参数
    auto params = distrib_normal.param();
    std::cout << "Normal distribution mean: " << params.mean() << ", stddev: " << params.stddev() << std::endl;


    return 0;
}

随机数工具(Utilities)

<random> 库还提供了一些工具:std::seed_seq 用于从一系列整数生成高质量的种子序列,特别适合初始化需要多个种子值的引擎或多个不同的引擎;std::generate_canonical<RealType, bits>(engine) 用于生成一个 [0, 1) 范围内具有指定位数随机性(精度)的浮点数;std::shuffle(first, last, engine) 用于使用给定的引擎随机打乱指定范围内的元素,是 std::random_shuffle(C++17 中弃用)的推荐替代品

代码示例(Utilities)

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <random>
#include <vector>
#include <numeric>   // for std::iota
#include <algorithm> // for std::shuffle, std::copy
#include <iterator>  // for std::ostream_iterator
#include <array>     // for std::array

int main() {
    // --- seed_seq ---
    std::seed_seq sseq{1, 2, 3, 4, 5}; // 从整数列表创建 seed_seq
    std::array<std::uint32_t, 8> seeds; // 准备接收生成的种子
    sseq.generate(seeds.begin(), seeds.end()); // 生成种子序列

    std::cout << "Generated seeds using seed_seq: ";
    for (std::uint32_t seed : seeds) {
        std::cout << seed << " ";
    }
    std::cout << std::endl;

    // 使用 seed_seq 初始化引擎
    std::mt19937 engine_from_sseq(sseq);
    std::cout << "First number from engine seeded by seed_seq: " << engine_from_sseq() << std::endl;

    // --- generate_canonical ---
    std::random_device rd_canon;
    std::mt19937 gen_canon(rd_canon());
    std::cout << "Generating 5 canonical doubles [0, 1): ";
    std::cout << std::fixed << std::setprecision(10);
    for (int i = 0; i < 5; ++i) {
        // 生成具有 64 位随机性的 double
        double canonical_rand = std::generate_canonical<double, 64>(gen_canon);
        std::cout << canonical_rand << " ";
    }
    std::cout << std::endl;

    // --- shuffle ---
    std::vector<int> v(10);
    std::iota(v.begin(), v.end(), 1); // 用 1, 2, ..., 10 填充向量

    std::cout << "Original vector: ";
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;

    std::random_device rd_shuffle;
    std::mt19937 g_shuffle(rd_shuffle());

    std::shuffle(v.begin(), v.end(), g_shuffle); // 使用引擎打乱向量

    std::cout << "Shuffled vector: ";
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;


    return 0;
}