跳到主要内容

C++ 随机数函数

在编程中,随机数是一个非常有用的工具,可以应用于游戏开发、模拟、加密和测试等多个领域。C++提供了多种生成随机数的方法,从传统的C风格函数到现代C++的随机数库。本文将全面介绍C++中的随机数函数及其使用方法。

传统随机数生成:rand() 函数

基本用法

最简单的随机数生成方法是使用C标准库中的rand()函数,它包含在<cstdlib>头文件中。

cpp
#include <iostream>
#include <cstdlib> // 包含rand()函数
#include <ctime> // 包含time()函数

int main() {
// 设置随机数种子
srand(time(0));

// 生成一个随机数
int randomNumber = rand();

std::cout << "随机数: " << randomNumber << std::endl;

return 0;
}

输出示例:

随机数: 1804289383
注意

每次运行程序时,如果不设置种子或使用相同的种子,rand()函数会生成相同的随机数序列。这就是为什么我们使用srand(time(0))来基于当前时间设置种子。

生成指定范围内的随机数

rand()函数生成的是0到RAND_MAX(通常是32767)之间的随机数。要生成特定范围内的随机数,可以使用以下公式:

cpp
int randomInRange = min + rand() % (max - min + 1);

示例:生成1到100之间的随机数

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

int main() {
srand(time(0));

// 生成1到100之间的随机数
int randomNumber = 1 + rand() % 100;

std::cout << "1到100之间的随机数: " << randomNumber << std::endl;

return 0;
}

输出示例:

1到100之间的随机数: 42

现代C++随机数生成

从C++11开始,<random>头文件引入了更强大、更灵活的随机数生成工具。这些工具包括随机数引擎和分布。

随机数引擎与分布

随机数生成过程分为两步:

  1. 使用随机数引擎生成均匀分布的随机数
  2. 使用分布对象将这些随机数转换为所需的分布
cpp
#include <iostream>
#include <random>

int main() {
// 创建一个随机数引擎
std::random_device rd; // 用于获取真随机数种子
std::mt19937 gen(rd()); // Mersenne Twister 随机数引擎

// 创建一个均匀分布
std::uniform_int_distribution<> distrib(1, 100);

// 生成随机数
int randomNumber = distrib(gen);

std::cout << "1到100之间的随机数: " << randomNumber << std::endl;

return 0;
}

输出示例:

1到100之间的随机数: 73

常用的随机数引擎

  1. std::random_device: 生成非确定性随机数,适合作为种子
  2. std::mt19937: Mersenne Twister 引擎,生成高质量的伪随机数
  3. std::default_random_engine: 默认随机数引擎,具体实现取决于编译器

常用的分布类型

  1. 均匀分布
    • std::uniform_int_distribution: 均匀分布的整数
    • std::uniform_real_distribution: 均匀分布的实数
cpp
// 生成1到100之间的均匀分布整数
std::uniform_int_distribution<int> intDistrib(1, 100);

// 生成0.0到1.0之间的均匀分布实数
std::uniform_real_distribution<double> realDistrib(0.0, 1.0);
  1. 正态分布
    • std::normal_distribution: 正态(高斯)分布的数值
cpp
// 均值为0,标准差为1的正态分布
std::normal_distribution<double> normalDistrib(0.0, 1.0);
  1. 其他分布
    • std::bernoulli_distribution: 伯努利分布
    • std::binomial_distribution: 二项分布
    • std::poisson_distribution: 泊松分布

生成随机字符和字符串

利用随机数函数,我们可以生成随机字符和字符串:

cpp
#include <iostream>
#include <random>
#include <string>

std::string generateRandomString(int length) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(97, 122); // ASCII for 'a' to 'z'

std::string result;
for (int i = 0; i < length; ++i) {
result += static_cast<char>(distrib(gen));
}

return result;
}

int main() {
std::string randomStr = generateRandomString(8);
std::cout << "随机字符串: " << randomStr << std::endl;

return 0;
}

输出示例:

随机字符串: wdhmtxpq

实际应用案例

案例1:模拟掷骰子游戏

cpp
#include <iostream>
#include <random>

class DiceRoller {
private:
std::random_device rd;
std::mt19937 gen;
std::uniform_int_distribution<> distrib;

public:
DiceRoller() : gen(rd()), distrib(1, 6) {}

int roll() {
return distrib(gen);
}

std::pair<int, int> rollTwo() {
return {roll(), roll()};
}
};

int main() {
DiceRoller dice;

std::cout << "掷一次骰子: " << dice.roll() << std::endl;

auto [dice1, dice2] = dice.rollTwo();
std::cout << "掷两次骰子: " << dice1 << " 和 " << dice2 << std::endl;

return 0;
}

输出示例:

掷一次骰子: 4
掷两次骰子: 2 和 6

案例2:简单密码生成器

cpp
#include <iostream>
#include <random>
#include <string>

class PasswordGenerator {
private:
std::random_device rd;
std::mt19937 gen;

const std::string lowercase = "abcdefghijklmnopqrstuvwxyz";
const std::string uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string numbers = "0123456789";
const std::string special = "!@#$%^&*()-_+=";

std::string charset;

public:
PasswordGenerator(bool useLower = true, bool useUpper = true,
bool useNumbers = true, bool useSpecial = false) : gen(rd()) {
if (useLower) charset += lowercase;
if (useUpper) charset += uppercase;
if (useNumbers) charset += numbers;
if (useSpecial) charset += special;
}

std::string generate(int length) {
std::uniform_int_distribution<> distrib(0, charset.length() - 1);

std::string password;
for (int i = 0; i < length; ++i) {
password += charset[distrib(gen)];
}

return password;
}
};

int main() {
// 创建一个包含小写字母、大写字母和数字的密码生成器
PasswordGenerator pwdGen;

// 创建一个包含所有字符类型的密码生成器
PasswordGenerator strongPwdGen(true, true, true, true);

std::cout << "标准密码 (8位): " << pwdGen.generate(8) << std::endl;
std::cout << "强密码 (12位): " << strongPwdGen.generate(12) << std::endl;

return 0;
}

输出示例:

标准密码 (8位): a3XcDp7R
强密码 (12位): %f9ZJm2@pQxT

重要的随机数生成最佳实践

  1. 避免使用旧的rand()函数:在现代C++中,尽量使用<random>头文件中的设施,因为它们提供了更好的随机性和更多的控制。

  2. 避免常见的随机数陷阱

    • 不要在循环中重复设置种子
    • 不要使用固定的种子值(除非出于测试目的)
    • 了解rand() % n可能带来的分布不均问题
  3. 对于密码学应用:标准C++随机数生成器不适用于密码学用途。对于密码学安全的随机数,请使用专门的密码学库。

小结

C++提供了多种生成随机数的方法:

  • 传统的rand()函数简单易用,但质量有限
  • 现代C++的<random>库提供了高质量的随机数生成工具

随机数在游戏开发、模拟、测试和加密等多个领域都有广泛应用。掌握如何有效地生成和使用随机数是每个C++程序员的必备技能。

练习

  1. 编写一个程序,生成10个1到100之间的随机数并计算它们的平均值。
  2. 创建一个函数,返回一个包含10个随机整数的std::vector
  3. 编写一个程序,模拟掷硬币1000次,并统计正面和反面出现的次数。
  4. 创建一个石头剪刀布游戏,其中计算机的选择是随机的。
  5. 使用正态分布生成100个随机数,并将它们的分布绘制出来。

进一步阅读资源

  • C++ 标准库参考:<random>头文件
  • C++随机数生成的更多细节和性能考虑
  • 密码学安全随机数生成方法