当前位置: 首页 > news >正文

C++ 中的随机数生成及其应用

C++ 中的随机数生成及其应用

在C++中,随机数生成可以通过多种方式实现。传统上,C语言风格的随机数生成使用rand()函数,但这种方法存在一些局限性(如随机性不足、需要手动管理种子等)。从C++11开始,标准库引入了更强大、更灵活的随机数生成机制,位于<random>头文件中。如果你使用的是 GCC 或 Clang 编译器,请确保在编译时启用 C++11 支持。

1. C语言风格的随机数生成使用rand()函数

早期的C语言风格的rand()函数生成的随机数是伪随机数,即通过一个确定的算法从一个初始值(种子)生成的数列。虽然这些数看起来是随机的,但它们实际上是可预测的,因为相同的种子会导致相同的随机数序列。

(1)随机性不足

#include <cstdlib>
#include <iostream>

int main() {
    for (int i = 0; i < 5; i++) {
        std::cout << rand() << " ";
    }
    return 0;
}

你可以试一下,如果多次运行这个程序,每次输出的随机数序列是相同的,因为rand()的种子默认是固定的(通常是1)。这表明rand()生成的随机数可预测性高,不适合对随机性要求较高的场景(如加密、游戏抽奖等)。编译运行效果类似如:

(2)需要手动管理种子

为了避免每次运行程序时生成相同的随机数序列,通常需要手动设置种子。这通常是通过time(0)来实现的:

#include <cstdlib>
#include <iostream>
#include <ctime>

int main() {
	srand(time(0));  // 初始化种子
    for (int i = 0; i < 5; i++) {
        std::cout << rand() << " ";
    }
    return 0;
}

本例存在问题:

种子设置的局限性:虽然time(0)可以提供不同的种子,但如果程序在短时间内多次运行(例如在同一个秒内),time(0)返回的值可能相同,导致生成的随机数序列仍然相同。

线程不安全:srand()和rand()是全局函数,它们共享同一个种子。在多线程环境中,多个线程同时调用rand()可能导致种子被意外修改,从而影响随机数的生成。

(3) rand() % N 的均匀性偏差问题,使得某些数字出现的概率更高

当使用 rand() % N 生成 [0, N-1] 的随机数时,如果 N 不是 RAND_MAX + 1 的整数因子,会导致某些数字出现的概率更高。

‌具体原因‌:

rand() 的返回值范围是 [0, RAND_MAX](通常 RAND_MAX = 32767)。

当用 rand() % N 时,实际上是将 [0, RAND_MAX] 映射到 [0, N-1]。

如果 RAND_MAX + 1 不能被 N 整除,余数分布会不均匀。

假设 RAND_MAX = 7,生成 [0, 2] 的随机数:

rand() 的输出范围:0,1,2,3,4,5,6,7 → 共 8 个数。

N = 3 → 余数分布:

0,3,6 % 3 = 0 → 概率 3/8

1,4  % 3 = 1 → 概率 2/8

2,5  % 3 = 2 → 概率 2/8

即:

0 的概率是 3/8

1 的概率是 2/8

2 的概率是 2/8

2. C++11开始引入的<random>头文件

从C++11开始,标准库引入了<random>头文件,解决了上述问题。提供了更强大、更灵活的随机数生成机制。它包括:

‌1)多种高质量算法‌引擎,常见的引擎包括:

  • std::default_random_engine:默认随机数引擎。
  • std::mt19937:基于Mersenne Twister(mt19937 梅森旋转)算法的随机数引擎,随机性较好。
  • std::linear_congruential_engine:线性同余随机数引擎,性能较高但随机性稍差。

2)明确分布控制‌(如 uniform_int_distribution 保证均匀性)。常见的分布器包括:

  • std::uniform_int_distribution:生成均匀分布的整数。
  • std::uniform_real_distribution:生成均匀分布的浮点数。
  • std::normal_distribution:生成正态分布的随机数。
  • std::bernoulli_distribution:生成伯努利分布的随机数。

3) std::random_device

std::random_device用于生成高质量的随机种子。std::random_device 是 C++ 标准库 <random> 头文件中定义的一个‌非确定性随机数生成器(Non-deterministic Random Number Generator)‌,其设计目的是提供‌不可预测的高质量随机性‌,通常依赖于底层操作系统的硬件熵源。

使用<random>库生成随机数,示例代码

#include <iostream>
#include <random>

int main() {
    // 随机数引擎
    std::mt19937 engine(std::random_device{}()); // 使用随机设备生成种子

    // 均匀分布的整数
    std::uniform_int_distribution<int> dist(1, 100); // 生成1到100之间的随机整数

    // 生成10个随机数
    for (int i = 0; i < 10; ++i) {
        std::cout << dist(engine) << " "; // 使用分布器和引擎生成随机数
    }
    std::cout << std::endl;

    return 0;
}

编译运行效果类似如:

使用<random>库的优势

  • 更好的随机性:现代随机数引擎(如std::mt19937)提供了高质量的随机性。
  • 灵活的分布:可以生成多种分布的随机数,满足不同需求。
  • 线程安全:每个线程可以独立使用随机数引擎,避免线程间干扰。
  • 可重复性:通过固定种子,可以重复生成相同的随机数序列,便于测试和调试。

在现代C++中,推荐使用C++11引入的<random>库,它提供了更高质量的随机数生成机制,解决了rand()的一些局限性。

3.应用:C++实现小学100以内整数四则运算出题机

以下是一个用C++实现的小学100以内整数四则运算出题机的代码示例。该程序会随机生成加法、减法、乘法和除法题目,确保减法不会出现不够减的情况,除法的结果能够整除。源码如下:

#include <iostream>
#include <random>
#include <chrono>
#include <string>

int main() {
    // 初始化随机数生成器
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::mt19937 gen(seed);

    // 生成10道随机题目
    for (int i = 0; i < 10; ++i) {
        std::uniform_int_distribution<> op_dist(0, 3);
        int operation = op_dist(gen);  // 随机选择运算符
        
        int a, b, result;
        std::string symbol;

        switch (operation) {
            case 0: {  // 加法:a + b ≤ 100
                std::uniform_int_distribution<> a_dist(0, 100);
                a = a_dist(gen);
                std::uniform_int_distribution<> b_dist(0, 100 - a);
                b = b_dist(gen);
                symbol = "+";
                result = a + b;
                break;
            }
            case 1: {  // 减法:a ≥ b
                std::uniform_int_distribution<> a_dist(0, 100);
                a = a_dist(gen);
                std::uniform_int_distribution<> b_dist(0, a);
                b = b_dist(gen);
                symbol = "-";
                result = a - b;
                break;
            }
            case 2: {  // 乘法:a × b ≤ 100
                std::uniform_int_distribution<> a_dist(1, 100);
                a = a_dist(gen);
                std::uniform_int_distribution<> b_dist(1, 100 / a);
                b = b_dist(gen);
                symbol = "×";
                result = a * b;
                break;
            }
            case 3: {  // 除法:商≥2,且被除数 = 除数 × 商
                std::uniform_int_distribution<> q_dist(2, 50); // 限制商范围
                int q = q_dist(gen);
                int max_b = 100 / q;
                std::uniform_int_distribution<> b_dist(1, max_b);
                b = b_dist(gen);
                a = b * q;  // 保证被除数能整除
                symbol = "÷";
                result = q;
                break;
            }
        }

        // 输出题目和答案
        std::cout << "问题" << i + 1 << ": " << a << " " << symbol << " " << b << " = ?\n";
        std::cout << "答案" << i + 1 << ": " << result << "\n\n";
    }

    return 0;
}

该代码特别适合用于生成小学数学练习题,既能保证题目有效性,又能通过随机性保持练习的新鲜感。

其中,两句:

unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();

std::mt19937 gen(seed);

解释

前一句种子生成

std::chrono::system_clock::now():获取当前系统时间点

.time_since_epoch():计算从"纪元时间"(1970-01-01 00:00:00 UTC)到当前时间的时长

.count():将时长转换为整数形式的纳秒值

作用:用时间戳作为随机数种子,确保每次运行程序生成不同的题目序列

后一句随机数引擎

std::mt19937:基于梅森旋转算法的高质量伪随机数生成器

gen(seed):用种子初始化生成器,相比传统rand()函数具有更好的随机性和更长的周期

编译运行效果如:

OK!

相关文章:

  • Dify+DeepSeek | Excel数据一键可视化(创建步骤案例)(echarts助手.yml)(文档表格转图表、根据表格绘制图表、Excel绘制图表)
  • Hive-数据倾斜优化
  • JetBrains学生申请
  • 【冗余路径——求边双连通分量】
  • 理解数学概念——稠密性(density)
  • 【前端基础】Day 9 PC端品优购项目
  • 洛谷P1334
  • cocodataset数据集可视化
  • Libgdx游戏开发系列教程(6)——游戏暂停
  • 一文学会Spring
  • 模块和端口
  • Linux设备驱动开发之摄像头驱动移植(OV5640)
  • DeepSeek 角色设定与风格控制
  • threejs:着色器onBeforeCompile给导入的模型添加光带扫描效果
  • 运维实战---多种方式在Linux中部署并初始化MySQL
  • Java面试常见问题总结
  • socket基础学习以及java搭建
  • GaussianCity:实时生成城市级数字孪生基底的技术突破
  • 小模型和小数据可以实现AGI吗
  • 黄昏时间户外街拍人像Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • 刘元春在《光明日报》撰文:以法治护航民营经济高质量发展
  • 习近平结束对俄罗斯国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典回到北京
  • 总粉丝破亿!当网络大V遇见硬核科技,互联网时代如何书写上海故事?
  • 三星“七天机”质保期内屏幕漏液被要求自费维修,商家:系人为损坏
  • 晋级中部非省会第一城,宜昌凭什么
  • 中华人民共和国和俄罗斯联邦关于全球战略稳定的联合声明