C ++ 11引入了标头<random>
,其中包含随机数引擎和随机分布的声明。那是很好的时间来取代rand()
的那些用途,这在各种方面经常会出现问题。但是,如何替换
srand(n);
// ...
int r = rand();
基于声明,似乎可以构建统一分布:
std::default_random_engine engine;
engine.seed(n);
std::uniform_int_distribution<> distribution;
auto rand = [&](){ return distribution(engine); }
这种方法似乎相当复杂,而且与使用srand()
和rand()
不同,我肯定不记得。我知道N4531,但即便如此,似乎仍然非常介入。
是否有一种相当简单的方法可以替换srand()
和rand()
?
答案 0 :(得分:16)
是否有一种相当简单的方法来替换srand()和rand()?
完全披露:我不喜欢rand()
。这很糟糕,很容易被滥用。
C ++ 11随机库填补了一个长期缺乏的空白。高质量随机库的问题在于它们经常难以使用。 C ++ 11 <random>
库代表了这方面的巨大进步。几行代码,我有一个非常好的生成器,行为非常好,很容易从许多不同的发行版中生成随机变量。
鉴于上述情况,我对你的回答有点异端。如果rand()
足以满足您的需求,请使用它。与rand()
一样糟糕(并且它很糟糕),删除它将代表C语言的巨大突破。只需确保rand()
的错误确实足以满足您的需求。
C ++ 14并没有弃用rand()
;它只推荐使用rand()
的C ++库中的函数。虽然C ++ 17可能会弃用rand()
,但它不会删除它。这意味着在rand()
消失之前你还有几年的时间。在C ++委员会最终从C ++标准库中删除rand()
时,您将退休或转换为其他语言的可能性很高。
我使用
的内容创建随机输入来对std :: sort()的不同实现进行基准测试std::vector<int> v(size); std::generate(v.begin(), v.end(), std::rand);
您不需要加密安全的PRNG。你甚至不需要Mersenne Twister。在这种特殊情况下,rand()
可能足以满足您的需求。
的更新强>
在C ++ 11随机库中有rand()
和srand()
的简单替换:std::minstd_rand
。
#include <random>
#include <iostream>
int main ()
{
std:: minstd_rand simple_rand;
// Use simple_rand.seed() instead of srand():
simple_rand.seed(42);
// Use simple_rand() instead of rand():
for (int ii = 0; ii < 10; ++ii)
{
std::cout << simple_rand() << '\n';
}
}
函数std::minstd_rand::operator()()
返回std::uint_fast32_t
。但是,该算法将结果限制在1到2 31 -2之间,包括端值。这意味着如果std::int_fast32_t
长度至少为32位,结果将始终安全地转换为int
(或int
。
答案 1 :(得分:6)
randutils
的Melissa O'Neill pcg-random.org怎么样?
randutils::mt19937_rng rng;
std::cout << "Greetings from Office #" << rng.uniform(1,17)
<< " (where we think PI = " << rng.uniform(3.1,3.2) << ")\n\n"
<< "Our office morale is " << rng.uniform('A','D') << " grade\n";
答案 2 :(得分:6)
假设你想要C风格的rand
和srand
函数的行为,包括它们的怪癖,但随机性很好,这是我能得到的最接近的函数。
#include <random>
#include <cstdlib> // RAND_MAX (might be removed soon?)
#include <climits> // INT_MAX (use as replacement?)
namespace replacement
{
constexpr int rand_max {
#ifdef RAND_MAX
RAND_MAX
#else
INT_MAX
#endif
};
namespace detail
{
inline std::default_random_engine&
get_engine() noexcept
{
// Seeding with 1 is silly, but required behavior
static thread_local auto rndeng = std::default_random_engine(1);
return rndeng;
}
inline std::uniform_int_distribution<int>&
get_distribution() noexcept
{
static thread_local auto rnddst = std::uniform_int_distribution<int> {0, rand_max};
return rnddst;
}
} // namespace detail
inline int
rand() noexcept
{
return detail::get_distribution()(detail::get_engine());
}
inline void
srand(const unsigned seed) noexcept
{
detail::get_engine().seed(seed);
detail::get_distribution().reset();
}
inline void
srand()
{
std::random_device rnddev {};
srand(rnddev());
}
} // namespace replacement
replacement::*
函数的用法与std::*
的{{1}}对应函数完全相同。我添加了一个不带参数的<cstdlib>
重载,并使用从srand
获得的“真实”随机数为引擎添加种子。 “随机性”的“真实”当然是实现定义的。
引擎和分发保存为std::random_device
thread_local
个实例,因此它们在多个调用中携带状态,但仍允许不同的线程观察可预测的序列。 (这也是一个性能上的提升,因为你不需要重新构建引擎或使用锁定,并可能摧毁其他人的现金。)
我已经使用了static
因为你做了但我不太喜欢它。 Mersenne Twister引擎(std::default_random_engine
和std::mt19937
)产生了更好的“随机性”,令人惊讶的是have also been observed to be faster。我不认为任何兼容程序必须依赖std::mt19937_64
使用任何特定类型的伪随机引擎实现。 (即使它确实如此,实现可以自由地将std::rand
定义为他们喜欢的任何内容,因此您必须使用类似std::default_random_engine
的内容来确定。)
答案 3 :(得分:4)
<random>
中定义的所有引擎都有一个operator()()
,可用于检索下一个生成的值,以及推进引擎的内部状态。
std::mt19937 rand (seed); // or an engine of your choosing
for (int i = 0; i < 10; ++i) {
unsigned int x = rand ();
std::cout << x << std::endl;
}
但应注意,所有引擎都返回一些 unsigned 整数类型的值,这意味着它们可能会溢出签名的积分(然后会导致< EM>未定义行为)。
如果你可以在任何地方使用无符号值来检索新值,则上述内容是替换std::srand
+ std::rand
使用情况的简便方法。
注意:使用上面描述的内容可能会导致某些值比其他值更有可能被返回,因为
result_type
engine
{1}} 没有最大值,该最大值是可以存储在目标类型中的最高值的偶数倍。
如果你过去并不担心这个问题 - 当使用类似rand()%low+high
的东西时 - 你现在不应该担心它。
注意:您需要确保
std::engine-type::result_type
至少与所需的值范围一样大(std::mt19937::result_type
为uint_fast32_t
)。
首先 default-construct std::default_random_engine
(对于实现选择的某个引擎,它只是 typedef )),然后为其分配种子;这可以通过使用 random-engine 的适当构造函数一次完成。
std::random-engine-type engine (seed);
如果您需要重新播种引擎,则可以使用std::random-engine::seed
。
即使你发布的代码看起来有点复杂,你也只想写一次。
如果您发现自己想要复制+粘贴您已写入代码中的多个位置的内容,建议您一如既往地执行复制+粘贴;介绍一个辅助函数。
Intentionally left blank, see other posts for example implementations.
答案 4 :(得分:0)
你可以创建一个这样的简单函数:
#include <random>
#include <iostream>
int modernRand(int n) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, n);
return dis(gen);
}
后来像这样使用它:
int myRandValue = modernRand(n);
如上所述here