当前位置:首页 > 单片机 > 程序喵大人

开发过程中,经常会遇到生成随机数的需求,本文会详细介绍C++中生成随机数的方法以及一些注意事项。

随机数核心组件

C++11引入了库,这个库提供了随机数生成工具。

下面是C++中生成随机数的核心组件:

  • 随机数引擎:生成伪随机数的算法。
  • 随机数分布:将随机数引擎生成的数映射到特定范围或分布。
  • 真随机数生成器:如std::random_device,用于生成高质量的随机数。

种子(Seed)

  • 种子是随机数生成器的初始值。
  • 相同的种子会生成相同的随机数序列。
  • 常用种子来源:
    • 当前时间:std::time(0)。
    • 真随机数生成器:std::random_device。

伪随机数引擎

  • std::mt19937:基于Mersenne Twister算法,周期长,随机性好。
  • std::minstd_rand:线性同余生成器,速度快,但随机性较差。
  • std::default_random_engine:默认引擎,实现可能因平台而异。

随机数分布

  • 均匀分布:std::uniform_int_distribution、std::uniform_real_distribution。
  • 正态分布:std::normal_distribution。
  • 伯努利分布:std::bernoulli_distribution。

真随机数生成器

  • std::random_device:依赖于硬件或操作系统提供的随机数源。
  • 适用于生成种子或高安全性场景。

生成随机数的方法

std::rand

std::rand是C标准库中的随机数生成函数,C++中仍然可以使用,但它的随机性较差,且范围固定。

#include  #include // for std::rand and std::srand #include // for std::time int main() { // 使用当前时间作为种子 std::srand(std::time(0)); // 生成一个随机数 int random_value = std::rand(); std::cout << "Random value: " << random_value << std::endl; // 生成一个范围在 [0, 99] 的随机数 int random_in_range = std::rand() % 100; std::cout << "Random value in [0, 99]: " << random_in_range << std::endl; return 0;
}

缺点:

  • std::rand生成的随机数质量较低。
  • 范围限制需要手动调整(如%操作符)。
  • 种子设置不够灵活。

std::random_device真随机数

std::random_device是一个真随机数生成器,通常用于生成高质量的随机数种子。

示例代码:

#include  #include  int main() { std::random_device rd; // 真随机数生成器 std::cout << "Random value: " << rd() << std::endl; return 0;
}

注意:

  • 在某些平台上,std::random_device可能会退化为伪随机数生成器。
  • 大量每次生成随机数都使用std::random_device,性能较差。
  • 通常用于生成种子,而不是直接用于生成大量随机数。

伪随机数引擎和分布

C++11引入了多种伪随机数引擎和分布,可以生成高质量的随机数。

常用随机数引擎:

  • std::default_random_engine:默认的随机数引擎。
  • std::mt19937:Mersenne Twister算法,高质量随机数引擎。
  • std::minstd_rand:线性同余生成器。

常用随机数分布:

  • std::uniform_int_distribution:均匀分布的整数。
  • std::uniform_real_distribution:均匀分布的浮点数。
  • std::normal_distribution:正态分布的浮点数。
  • std::bernoulli_distribution:伯努利分布(布尔值)。

示例代码:

#include  #include  int main() { // 使用 Mersenne Twister 引擎 std::mt19937 rng(std::random_device{}()); // 均匀分布的整数 [1, 100] std::uniform_int_distribution<int> dist(1, 100); int random_int = dist(rng); std::cout << "Random integer in [1, 100]: " << random_int << std::endl; // 均匀分布的浮点数 [0.0, 1.0) std::uniform_real_distribution<double> dist_double(0.0, 1.0); double random_double = dist_double(rng); std::cout << "Random double in [0.0, 1.0): " << random_double << std::endl; // 正态分布的浮点数(均值 0.0,标准差 1.0) std::normal_distribution<double> dist_normal(0.0, 1.0); double random_normal = dist_normal(rng); std::cout << "Random normal value: " << random_normal << std::endl; return 0;
}

优点:

  • 随机数质量高。
  • 分布灵活,支持多种分布类型。

生成随机字符串

可以使用随机数生成器生成随机字符串。

示例代码:

#include  #include  #include  std::string generate_random_string(size_t length) { const std::string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; std::mt19937 rng(std::random_device{}()); std::uniform_int_distribution<size_t> dist(0, charset.size() - 1); std::string result; for (size_t i = 0; i < length; ++i) {
 result += charset[dist(rng)];
 } return result;
} int main() { std::string random_str = generate_random_string(10); std::cout << "Random string: " << random_str << std::endl; return 0;
}

注意事项

std::mt19937 e{std::random_device{}()};

直接这样写有什么问题?

如果你只需要生成一次随机数,这样写没问题。但如果你需要多次生成随机数,最好避免反复创建和销毁std::random_device对象,因为这会带来不必要的开销。

原因:

  • 每次创建std::random_device对象时,都会初始化一个新的文件句柄(在类Unix系统上,它通常是对/dev/urandom的封装),这会带来文件系统操作的开销。(不同操作系统的实现不同)
  • 每次从std::random_device读取随机数时,都会触发系统调用(如read系统调用),这可能会影响性能。
  • 在Windows系统上,std::random_device通常是对微软加密API的封装,每次创建和销毁std::random_device对象时,都会初始化和销毁加密库的接口,这也会带来额外的开销。

因此,如果需要频繁生成随机数,这种写法可能会导致性能问题。当然,如果你的应用程序对性能要求不高,这种写法也是可以接受的。

常见的写法:

在许多示例、网站和文章中,通常会看到以下写法:

std::random_device rd; std::mt19937 e{rd()}; // 或者 std::default_random_engine e{rd()}; std::uniform_int_distribution<int> dist{1, 5};

这种写法的优点是:

  • std::random_device只被创建一次,避免了反复初始化和销毁的开销。
  • std::mt19937是一个伪随机数生成器,它的初始化只需要一个种子(由std::random_device提供),之后的所有随机数生成都在用户进程中完成,不会涉及系统调用。

std::mt19937vsstd::random_device

std::mt19937

  • 伪随机数生成器,基于Mersenne Twister算法。
  • 它是自包含的,完全在用户进程中运行,不会调用操作系统或其他外部资源。
  • 代码非常稳定,跨平台性能一致,在任何平台上编译和运行它,都会得到相似的性能和结果。

std::random_device

  • 是一个真随机数生成器,但其实现是不透明的,我们无法确切知道它的底层实现是什么,它会做什么,或者它的效率如何,不同系统实现不一定相同。
  • 每次从std::random_device读取随机数时,可能会触发系统调用,因此其性能(每字节的周期数)可能远低于std::mt19937。
  • 它通常用于生成种子,而不是直接生成大量随机数。
  • 如果你为某些嵌入式设备或手机进行交叉编译,它的行为可能更加不可预测。

总结

  • 对于简单的随机数需求,可以使用std::rand。
  • 对于高质量的随机数,推荐使用std::mt19937引擎和分布,这是一个高效且可靠的伪随机数生成器,适合在用户进程中生成大量随机数。
  • 对于高安全性场景,可以使用std::random_device生成真随机数。
  • std::random_device的行为因平台而异,通常用于生成种子,而不是直接生成大量随机数。
  • 如果需要频繁生成随机数,建议避免反复创建和销毁std::random_device对象,而是将其作为种子生成器,只初始化一次。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭