如何使用<random>替换rand()?

时间:2015-10-03 21:34:10

标签: c++ c++11 random

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()

5 个答案:

答案 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::vector<int> v(size); std::generate(v.begin(), v.end(), std::rand);

的内容创建随机输入来对std :: sort()的不同实现进行基准测试

您不需要加密安全的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怎么样?

来自introductory blog post

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风格的randsrand函数的行为,包括它们的怪癖,但随机性很好,这是我能得到的最接近的函数。

#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_enginestd::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_typeuint_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