新的C++11标准专门为随机数生成器设立了一个章节。但是,如何执行最简单、最常见的任务,而不依赖于标准C库,以前通常会这样编码:
srand((unsigned int)time(0)); int i = rand();
是否有合理的默认值可用于开箱即用的随机数引擎、分布和种子?
你应该能够做到类似以下方式:
std::default_random_engine e((unsigned int)time(0));
int i = e();
default_random_engine
的质量是取决于实现的。你也可以使用 std::min_rand0
或 std::min_rand
。
可能更好的随机引擎种子方式是使用实现中可用的真正随机数,而不是使用 time
。
例如:
std::random_device rd;
std::default_random_engine e( rd() );
e
里面。在原始问题的代码中,状态被隐藏为rand
内部的静态数据。当处理多线程程序时,这是一个重要的区别。rand
必须有一些保护使其线程安全。default_random_engine
则没有。如果您想从多个线程调用它,则需要自己提供同步机制外部。这意味着如果不需要同步,default_random_engine
可能会更快。 - Howard Hinnanttime
前加上std::
<耸肩>。符合C++11标准的托管平台需要使用std::default_random_engine
,在libc++中它等价于minstd_rand
,是一个线性同余引擎。 - Howard Hinnantrandom_device
建议。或者您可以继续使用time
。还有一种被称为soup-up seeder on steroids
的东西叫做seed_seq
。我已经实现了这个东西,但仍然不确定何时使用它。:-)而且为了增加乐趣,seed_seq
本身也可以被种子化! - Howard Hinnant我将统一和简化已经提供的一些示例,总结如下:
// Good random seed, good engine
auto rnd1 = std::mt19937(std::random_device{}());
// Good random seed, default engine
auto rnd2 = std::default_random_engine(std::random_device{}());
// like rnd1, but force distribution to int32_t range
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));
// like rnd3, but force distribution across negative numbers as well
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));
我进行了一些测试,以查看默认设置如何:
#include <random>
#include <functional>
#include <limits>
#include <iostream>
template<class Func>
void print_min_mean_max(Func f) {
typedef decltype(f()) ret_t;
ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min();
uint64_t total = 0, count = 10000000;
for (uint64_t i = 0; i < count; ++i) {
auto res = f();
min = std::min(min,res);
max = std::max(max,res);
total += res;
}
std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl;
}
int main() {
auto rnd1 = std::mt19937(std::random_device{}());
auto rnd2 = std::default_random_engine(std::random_device{}());
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));
print_min_mean_max(rnd1);
print_min_mean_max(rnd2);
print_min_mean_max(rnd3);
print_min_mean_max(rnd4);
}
min: 234 mean: 2147328297 max: 4294966759
min: 349 mean: 1073305503 max: 2147483423
min: 601 mean: 1073779123 max: 2147483022
min: -2147481965 mean: 178496 max: 2147482978
从上面我们可以看出,mt19937和default_random_engine有不同的默认范围,因此建议使用uniform_int_distribution。
此外,默认的uniform_int_distribution是[0,max_int](非负数),即使使用带符号的整数类型也是如此。如果需要完整的范围,必须明确提供范围。
最后,在这种情况下,记住这一点很重要。
std::mt19937
有一个 64 位版本:std::mt19937_64
,每次调用返回 64 位的随机数。auto rnd5 = std::mt19937_64(std::random_device{}()); // min: 4879020137534 mean: 1655417118684 max: 18446741225191893648
- Seanstd::mt19937_64 random = std::mt19937_64(std::random_device{}());``auto randomCardinality = std::bind(std::uniform_int_distribution<int>(1, 4), random);
auto randomValue = std::bind(std::uniform_real_distribution<double>(-1.0, 1.0), random);
- Xharlie#include <random>
#include <functional>
#include <iostream>
...
std::uniform_int_distribution<unsigned int> unif;
std::random_device rd;
std::mt19937 engine(rd());
std::function<unsigned int()> rnd = std::bind(unif, engine);
std::cout << rnd() << '\n';
这里是随机生成指定范围内的双精度浮点数:
// For ints
// replace _real_ with _int_,
// <double> with <int> and use integer constants
#include <random>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <iterator>
int main()
{
std::default_random_engine rng(std::random_device{}());
std::uniform_real_distribution<double> dist(-100, 100); //(min, max)
//get one
const double random_num = dist(rng);
//or..
//print 10 of them, for fun.
std::generate_n(
std::ostream_iterator<double>(std::cout, "\n"),
10,
[&]{ return dist(rng);} );
return 0;
}
随机数生成是一个困难的问题。没有真正随机的方法来做到这一点。如果你只是生成随机数来种子化游戏环境,那么你的方法应该是可以的。rand() 有几个缺点。
如果你需要随机性来生成加密密钥,那么你就很难了。在这种情况下,最好的方法是去操作系统中寻找机制。在 POSIX 上,这是 random()(或者如果你愿意的话,从 /dev/random 中读取)。在 Windows 上,你可以使用 CryptoAPI:
rand
是标准 C 的一部分,而random
是 POSIX 的一部分,arc4random
则提供在 BSD 上。 - sbooth