首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用c++11随机头生成随机数

使用c++11随机头生成随机数
EN

Code Review用户
提问于 2020-12-24 23:47:03
回答 2查看 1.5K关注 0票数 7

我对编程很陌生。我有一个关于使用c++11随机头来生成随机数的问题。我试着学习,但没有成功。最近,我尝试了以下方法,并且成功了。关于如何改进它的任何建议。有一个好的来源来了解更多这方面的知识吗?

下面是代码:

代码语言:javascript
运行
复制
#include <iostream>
#include <random>
#include <ctime>
int main()
{
   std::default_random_engine random_engine(time(0));
   for (int i = 1; i <= 100; i++)
   {
    std::uniform_int_distribution<int> num(1, 100);
    std::cout << i << "==> " << num(random_engine) << std::endl;
   }
    return 0;
}
EN

回答 2

Code Review用户

发布于 2020-12-25 01:37:13

首先,请注意,std::default_random_enginestd::uniform_int_distribution<int>在同一意义上都是不可移植的,就像rand()不可移植一样:它们不会在不同的平台上产生相同的数字序列,因为它们的行为是由实现定义的。对于uniform_int_distribution来说,实际上没有可移植的替代方案,所以它是镇上最好的游戏。但是对于PRNG本身,我不认为任何专家会推荐default_random_engine。只需选择一个已知的引擎,如std::mt19937。(这是不太好,但这也是目前镇上唯一的游戏。)

对于种子:请注意,您只向PRNG的构造函数提供了32位种子(以及一个非常可预测的种子)。您应该从random_device中播种,类似于这样(从这里偷来的):

代码语言:javascript
运行
复制
std::random_device rd;
int data[624];
std::generate_n(data, std::size(data), std::ref(rd));
std::seed_seq sseq(data, std::end(data));
std::mt19937 g(sseq);

(当然,在实践中没有人这样做。)

代码语言:javascript
运行
复制
for (int i = 1; i <= 100; i++)

首选

代码语言:javascript
运行
复制
for (int i = 0; i < 100; ++i)

即使这意味着您必须在循环中引用i+1。在C和C++以及每种现代语言中,使用从0开始的半开放范围是很好的实践。

代码语言:javascript
运行
复制
std::uniform_int_distribution<int> num(1, 100);

就我个人而言,我会写成

代码语言:javascript
运行
复制
auto dist = std::uniform_int_distribution<int>(1, 100);

以获得一个漂亮的清晰=分离可见一目了然。另外,请注意我的常规变量名:rd用于随机设备,g用于PRNG本身,dist用于分发。使用比dist更具有描述性的名称可能是有意义的.但是毫无疑问,num并不是描述性更强,而num则不那么真实--发行版不是单个数字!

最后,您不需要最终的return 0 (默认情况下,main返回0),也不需要std::endl,因为'\n' (或"\n")也一样。

但是基本上你做的是正确的:在循环之外创建的PRNG,从循环内部调用类似a函数的发行版。

如果需要,也可以将发行版的变量定义移出循环之外。在实践中,这可能并不重要,因为uniform_int_distribution不太可能在迭代之间保留任何状态。但是,如果它是一个std::normal_distribution,那么每次通过循环销毁和重新创建分发将基本上要花费您速度的50%。

代码语言:javascript
运行
复制
auto dist = std::uniform_int_distribution<int>(1, 100);
for (int i = 0; i < 100; ++i) {
    std::cout << (i+1) << "==> " << dist(g) << '\n';
}
票数 7
EN

Code Review用户

发布于 2020-12-25 01:56:04

不幸的是,我不知道有什么源代码能够很好地介绍标准<random>库。我几年前就写过这篇文章,但那时它还是全新的,我主要是在介绍它;它并不是一个真正全面的教程。

不过,我可以给您一些关于代码的建议。

代码语言:javascript
运行
复制
std::default_random_engine random_engine(time(0));

使用std::time()为随机数生成器添加种子通常是不明智的,原因有很多。

首先,不能保证返回类型实际上可以用作种子,因为它可能不能转换为无符号整数类型。(实际上,我从未见过这样的平台,因为返回类型是所有POSIX平台上32位签名的int )。

其次,返回类型通常(可能总是)只有1秒左右。这意味着,如果程序在一秒钟内运行两次,它将产生完全相同的“随机”序列。

第三,这是一个可预测的价值。如果我想控制你的程序,我所要做的就是把我的时钟设置为一个时间来生成我想要的序列。

在实践中,随机引擎种子的通常方法是使用std::random_device。现在它通常是不确定的(所以,完全不可预测),但它是那些不能保证在任何地方都能正常工作的东西之一(尽管它几乎肯定在大多数人需要的地方都能正常工作)。

所以你要做的就是:

代码语言:javascript
运行
复制
std::random_device rd;
std::default_random_engine random_engine(rd());

// or, in one line:
std::default_random_engine random_engine(std::random_device{}());

下一步:

代码语言:javascript
运行
复制
for (int i = 1; i <= 100; i++)
{
    std::uniform_int_distribution<int> num(1, 100);
    std::cout << i << "==> " << num(random_engine) << std::endl;
}

将分发移出循环可能更有效:

代码语言:javascript
运行
复制
std::uniform_int_distribution<int> dist(1, 100);

for (int i = 1; i <= 100; i++)
{
    std::cout << i << "==> " << dist(random_engine) << '\n';
}

最后,给出一些简单的建议:

  1. 我发现唯一需要使用的引擎是mersenne_twister_enginelinear_congruential_enginemt19937 (即mersenne_twister_engine)应该是您的默认选择。我在模拟中使用了LCG,其中每个实体都有自己的随机数生成器,因为Mersenne是伟大的…但也是巨大的和相对缓慢的。LCG很小,而且速度很快,但不会产生很大的随机性…但有时候这没什么。
  2. 我发现唯一需要经常使用的发行版是uniform_int_distributionnormal_distribution
    • 均匀分布适用于以下情况:您希望在两个值之间设置一个数字,而两者之间的每个值都是相同的--例如,滚动一个模具或从一个组中选择一个值(比如从帽子中提取一个名称)。
    • 正态分布是指你想要“自然”随机性的情况--例如,在一场游戏中击中后造成的伤害(攻击造成的“名义”伤害可能是100,然后你使用正态分布的随机性来“接近”,所以99或101更有可能超过95或105)。我使用了其他发行版,比如bernoulli_distribution,来模拟一些事情,比如说,62%的成功机会被一遍又一遍地完成。但即便如此,我通常也只是使用正态分布。

  3. 如果您需要更改发行版的范围,您可以直接给它分配一个新值,或者使用param()函数。
票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/253883

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档