正如标题所示,我正在尝试使用新的C++11 <random>
库生成随机数。我已经尝试了以下代码:
std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);
我写的代码有个问题,每次编译运行后生成的数字都是一样的。那么我的问题是,随机库中还有哪些函数可以产生真正随机的数值。
特别地,我需要获取一个在区间[1, 10]
内的随机数。
rand()
的演讲。在演讲中,他包含了一张幻灯片,基本上解决了你的问题。我将下面的代码从那张幻灯片上复制了出来。#include <random>
#include <iostream>
int main() {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(1.0, 10.0);
for (int i=0; i<16; ++i)
std::cout << dist(mt) << "\n";
}
random_device
一次来为名为mt
的随机数生成器提供种子。 random_device()
比mt19937
慢,但它不需要种子,因为它从操作系统请求随机数据(例如,可以从各种位置获取,如RdRand)。
uniform_real_distribution
返回的是一个在区间[a, b)
内的数值,而你想要的是[a, b]
。为了实现这个目标,我们的uniform_real_distribution
应该像这样:std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));
default_random_engine
。根据C++ Primer,它是实现中被认为最有用的引擎。 - aaronmandefault_random_engine
存在。 - Bill Lynchstd::default_container
。希望没有人认为自己是程序员而不知道区别。许多脚本语言都有默认的映射类型结构,这可能是以各种方式实现的,用户可能不知道。 - aaronmannextafter
函数调用是过度的。一个随机的 double
值恰好落在端点上的概率非常小,因此包括或排除它没有实际区别。 - Mark Ransomstd::vector
比喻在这里不适用,因为由于 CPU 缓存作用,std::vector
实际上是一个很好的默认选择。即使你理解所有容器并且可以根据算法复杂度做出明智的决策,它甚至比中间插入性能更好的 std::list
更优秀。 - void.pointer我的 "random" 库提供了一个方便的封装,可以简化 C++11 随机数类的使用。你可以通过一个简单的 "get" 方法完成几乎所有操作。
例如:
获取指定范围内的随机数
auto val = Random::get(-10, 10); // Integer
auto val = Random::get(10.f, -10.f); // Float point
随机布尔值
auto val = Random::get<bool>( ) // 50% to generate true
auto val = Random::get<bool>( 0.7 ) // 70% to generate true
从 std::initializer_list 中获取随机值
auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
从迭代器范围或整个容器中获取随机迭代器
auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
auto it = Random::get( vec ); // return random iterator
还有更多的东西!请查看 GitHub 页面:
void random_device_uniform()
-- 用于加密生成但速度较慢(来自我的示例)mt19937
的示例 -- 更快,能够创建种子,但未加密
srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or
只需使用<random>
而不是<time>和<cstdlib>
#includings - 所以要小心只从一本书中学习 :)。
这意味着自C ++11起不应该使用它,因为:
程序通常需要一个随机数源。在新标准之前,C和C ++都依赖于一个简单的C库函数rand。该函数生成伪随机整数,这些整数均匀分布在0到至少32767个系统相关最大值的范围内。rand函数有几个问题:许多程序(如果不是大多数)需要与rand生成不同范围内的随机数。一些应用程序需要随机浮点数。一些程序需要反映非均匀分布的数字。当程序员尝试转换由rand生成的数字的范围、类型或分布时,他们经常引入非随机性。(引自Lippmans C++ primer fifth edition 2012)
我终于在Bjarne Stroustrups的一些新书中找到了最好的解释。
其实很简单,因为随机数生成器由两部分组成: (1)生成随机或伪随机值序列的引擎。 (2)将这些值映射到范围内的数学分布的分布。
尽管微软STL的人有不同意见,但Bjarne Stroustrups写道:
在C++中,标准库提供了随机数引擎和分布(§24.7)。默认情况下使用default_random_engine,该引擎被选择为具有广泛适用性和低成本。
void die_roll()
示例来自Bjarne Stroustrups - 用using
生成引擎和分布是个好主意(更多信息请参见这里)。
为了能够实际利用标准库提供的随机数生成器<random>
,这里提供一些可执行的代码,其中包含不同的示例,缩小到最少必要的内容,希望能为大家节省时间和金钱:
#include <random> //random engine, random distribution
#include <iostream> //cout
#include <functional> //to use bind
using namespace std;
void space() //for visibility reasons if you execute the stuff
{
cout << "\n" << endl;
for (int i = 0; i < 20; ++i)
cout << "###";
cout << "\n" << endl;
}
void uniform_default()
{
// uniformly distributed from 0 to 6 inclusive
uniform_int_distribution<size_t> u (0, 6);
default_random_engine e; // generates unsigned random integers
for (size_t i = 0; i < 10; ++i)
// u uses e as a source of numbers
// each call returns a uniformly distributed value in the specified range
cout << u(e) << " ";
}
void random_device_uniform()
{
space();
cout << "random device & uniform_int_distribution" << endl;
random_device engn;
uniform_int_distribution<size_t> dist(1, 6);
for (int i=0; i<10; ++i)
cout << dist(engn) << ' ';
}
void die_roll()
{
space();
cout << "default_random_engine and Uniform_int_distribution" << endl;
using my_engine = default_random_engine;
using my_distribution = uniform_int_distribution<size_t>;
my_engine rd {};
my_distribution one_to_six {1, 6};
auto die = bind(one_to_six,rd); // the default engine for (int i = 0; i<10; ++i)
for (int i = 0; i <10; ++i)
cout << die() << ' ';
}
void uniform_default_int()
{
space();
cout << "uniform default int" << endl;
default_random_engine engn;
uniform_int_distribution<size_t> dist(1, 6);
for (int i = 0; i<10; ++i)
cout << dist(engn) << ' ';
}
void mersenne_twister_engine_seed()
{
space();
cout << "mersenne twister engine with seed 1234" << endl;
//mt19937 dist (1234); //for 32 bit systems
mt19937_64 dist (1234); //for 64 bit systems
for (int i = 0; i<10; ++i)
cout << dist() << ' ';
}
void random_seed_mt19937_2()
{
space();
cout << "mersenne twister split up in two with seed 1234" << endl;
mt19937 dist(1234);
mt19937 engn(dist);
for (int i = 0; i < 10; ++i)
cout << dist() << ' ';
cout << endl;
for (int j = 0; j < 10; ++j)
cout << engn() << ' ';
}
int main()
{
uniform_default();
random_device_uniform();
die_roll();
random_device_uniform();
mersenne_twister_engine_seed();
random_seed_mt19937_2();
return 0;
}
我认为这总结了所有内容,就像我所说的,我花费了很多时间来阅读和筛选出这些例子 - 如果你有更多关于数字生成的信息,欢迎私信或在评论区留言告诉我,如果有必要我会添加或编辑这篇文章。Bool
https://en.wikipedia.org/wiki/Pseudorandom_number_generator
基本上,计算机中的随机数需要一个种子(这个数字可以是当前系统时间)。std::default_random_engine generator;
通过
std::default_random_engine generator(<some seed number>);
这是我刚写的一些相关内容:
#include <random>
#include <chrono>
#include <thread>
using namespace std;
//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
public:
random_device rd;
mt19937 mt;
uniform_real_distribution<double> dist;
backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}
double rand() {
return dist(mt);
}
};
thread_local backoff_time_t backoff_time;
int main(int argc, char** argv) {
double x1 = backoff_time.rand();
double x2 = backoff_time.rand();
double x3 = backoff_time.rand();
double x4 = backoff_time.rand();
return 0;
}
~
#define uniform() (rand()/(RAND_MAX + 1.0))
这段代码可以给你一个在0到1-epsilon范围内的p值(除非RAND_MAX比double精度还大,但是遇到这种情况再考虑)。
int x = (int) (uniform() * N);
现在可以得到一个0到N-1的随机整数。
如果你需要其他分布,就必须对p进行转换。或者有时候调用uniform()多次会更容易实现。
如果你想要可重复的行为,就使用一个常量作为种子,否则就使用time()函数的返回值作为种子。
如果你关心质量或运行时间性能,可以重写uniform()函数。但是如果不关心,就不要修改代码。始终将uniform()函数保持在0到1-epsilon之间。现在你可以包装C++随机数库来创建更好的uniform()函数,但这是一种中级选项。如果你关心RNG的特性,那么值得花费一些时间了解底层方法的工作原理,然后提供一个自己的函数。这样你就完全控制了代码,并且可以保证在相同的种子下,序列始终完全相同,无论平台或链接到哪个版本的C++。
std::mt19937
作为引擎。这个分布是一个双端封闭区间。 - chrisstd::uniform_int_distribution
,它在两端都是闭区间。 - chris