C++中生成随机数的最佳方法是什么?

21

如何生成随机数的最佳方式?


2
为什么 rand 不好使用? - David Schwartz
2
因为你需要除以、加上等操作才能使数字处于两个数之间!必须有更好的函数。 - Hamed
4
@DavidSchwartz 我来到这里是因为 rand 函数每次生成的结果都相同... - Tomáš Zato
1
@TomášZato,请展示出现问题的代码,并询问如何修复它。 - David Schwartz
@DavidSchwartz "为什么不建议使用rand函数?" - 请参考rand()被认为是有害的 - Jesper Juhl
5个回答

32

你应该使用<random>

#include <random>

typedef std::mt19937 rng_type;
std::uniform_int_distribution<rng_type::result_type> udist(0, 7);

rng_type rng;

int main()
{
  // seed rng first:
  rng_type::result_type const seedval = get_seed(); // get this from somewhere
  rng.seed(seedval);

  rng_type::result_type random_number = udist(rng);

  return random_number;
}

在 C++11 之前,你可以在 TR1 (<tr1/random>, std::tr1::mt19937 等等) 或 Boost.random 中找到它们,两者的接口基本相同(尽管存在细微差异)。


我认为分布的返回类型和引擎的类型之间不需要有关联。通常我喜欢将分布和引擎绑定在一起,除非我有特殊的原因需要将它们分开。此外,引擎可以在构造函数中接受种子,所以你可以将所有内容放在一行中:auto rand = bind(uniform_int_distribution<>(0,7),mt19937(get_seed())); - bames53
@bames53:你说得对,分布和引擎的整数类型可能是不同的。就我个人而言,我不太喜欢过多的bind,但如果你想要更紧凑的形式,那当然可以。 - Kerrek SB
1
但是我如何获得足够随机的种子? - Tomáš Zato
1
@TomášZato:你可以使用std::random_device。结果的质量取决于你的实现,但这是一个开始。 - Kerrek SB

15

当且仅当:

  • 您不需要"完美的一致性"或

  • 您没有C++11支持,甚至没有TR1(因此您别无选择)

那么您可以考虑使用以下C风格解决方案,为了这个社区的声誉(请参见rand() Considered Harmful),代码已经被划掉了:

这是一个简单的C风格函数,它生成从minmax之间的随机数,包括两端。这些数字似乎非常接近均匀分布。

int irand(int min, int max) {
    return ((double)rand() / ((double)RAND_MAX + 1.0)) * (max - min + 1) + min;
}

请不要忘记在使用之前调用srand函数:
int occurrences[8] = {0};

srand(time(0));
for (int i = 0; i < 100000; ++i)
    ++occurrences[irand(1,7)];

for (int i = 1; i <= 7; ++i)
    printf("%d ", occurrences[i]);

输出:14253 14481 14210 14029 14289 14503 14235

另外看看:
在范围内生成随机数?
均匀地在整个范围内生成随机数
并找时间观看前11分钟的视频

否则:

Kerrek SB已经指出的那样,使用<random>


1
你的 irand 实现并不能产生均匀分布的随机数:正如 rand() considered harmful 中所解释的那样,某些值比其他值更有可能出现。你的示例在视频的 7:30 处有描述。 - syam
1
@syam:我已经看到这篇演讲的链接好几次了,但一直没有注意它。最终我观看了它,并相应地编辑了我的答案 :) - LihO
1
公正地说,演讲者认为您的实现只是“微妙”地不均匀,而不是其他“滑稽”的不均匀解决方案。就我个人而言,我在 rand() 函数上犯过更严重的错误... ;) - syam
1
@syam:但如果<random>可用,为什么不选择它呢?它更清晰和更好的解决方案。它也可以像我的irand一样封装成一个独立的帮助函数。 - LihO
1
别误解我的意思,我并不是在倡导使用你的实现替代<random>,只是指出还有更糟糕的错误可以犯。当然如果有<random>可用,应优先考虑它,但是如果你必须使用rand(),那么你的解决方案可能是我们能够得到的最好的(之一)。 - syam
rand() 对我的单元测试非常有用。但在 Unix 系统上,你至少可以从 /dev/random/dev/urandom 加载字节。此外,OpenSSL 库提供了 RAND(尝试 man RANDman RAND_bytes)。 - Alexis Wilke

6

Boost.Random是一个出色的库,用于生成伪随机数(如果平台支持,则可以生成真正的随机数)。


5
我查看了他们的文档,只是为了确保他们没有声称产生真正随机的数字,他们没有这样做。那么你为什么要在你的回答中提到这个呢? - Kevin Crowell
1
@KevinCrowell 非确定性均匀随机数生成器 - spencercw
2
它包括random_device(与现在标准的<random>头文件一样)以提供对非确定性均匀随机数生成器的访问,该生成器在具有此类设备的平台上可用。(例如,操作系统通过观察网络流量等活动收集随机数据,并通过/dev/random提供对该数据的访问) - bames53

3
如果你使用的是C++11之前的标准C++库,rand和srand是你的随机数生成器。有一些方法可以通过使用双精度浮点数来获得比使用整数算术取模更高的精度,例如,如果速度不是问题,你可以将结果舍入为整数。
至于自定义库,如果你真的想要良好的随机分布和速度,请搜索Mersenne Twister。在boost中也有选项。
在C++11中,你可以使用库。 http://en.cppreference.com/w/cpp/numeric/random

2

我的“随机”库提供了一个方便的C++11随机类的封装。你可以通过一个简单的“get”方法完成几乎所有的事情。

例如:

  1. 在范围内生成随机数

auto val = Random::get(-10, 10); // 整数
auto val = Random::get(10.f, -10.f); // 浮点数

  1. 生成随机布尔值

auto val = Random::get<bool>( ) // 生成true的概率为0.5%

auto val = Random::get<bool>( 0.7 ) // 生成true的概率为0.7%

  1. 从std::initilizer_list中生成随机值
auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  1. 从迭代器范围或所有容器中获取随机迭代器

auto it = Random::get( vec.begin(), vec.end() ); // it = 随机迭代器

auto it = Random::get( vec ); // 返回随机迭代器

甚至还有更多!请查看 GitHub 页面:

https://github.com/effolkronium/random


你的库非常完整!我喜欢可以加载/保存当前状态的事实,因为这样你就不需要每次重新播种,这使得它更安全,我想。 - Alexis Wilke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接