繁体   English   中英

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

[英]How to use <random> to replace rand()?

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

是否有一种相当简单的方法来替换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()的不同实现进行基准测试std::vector<int> v(size); std::generate(v.begin(), v.end(), std::rand); 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之间,包括1和2 31 -2。 这意味着结果将总是安全地转换成std::int_fast32_t (或到一个int如果int是至少32位长)。

randutils的Melissa O'Neill如何使用randutils

介绍性博客文章

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";

假设您想要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::*函数可以与<cstdlib>std::*对应函数完全一样使用。 我添加了一个不带参数的srand重载,并使用从std::random_device获得的“真实”随机数为引擎std::random_device 随机性的“真实”当然是实现定义的。

引擎和分发保存为thread_local static实例,因此它们跨多个调用携带状态,但仍允许不同的线程观察可预测的序列。 (这也是一种性能提升,因为您不需要重新构建引擎或使用锁定并可能摧毁其他人的现金。)

我已经使用了std::default_random_engine因为你做了但我不太喜欢它。 Mersenne Twister引擎( std::mt19937std::mt19937_64 )产生了更好的“随机性”,并且令人惊讶地, 也观察到更快 我不认为任何兼容程序必须依赖于使用任何特定类型的伪随机引擎实现std::rand (即使它确实如此,实现可以自由地将std::default_random_engine定义为他们喜欢的任何东西,所以你必须使用类似std::minstd_rand东西来确定。)

滥用引擎直接返回值的事实

<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;
}

然而,应该注意的是,所有引擎都返回一些无符号整数类型的值,这意味着它们可能会溢出有符号整数(这将导致未定义的行为 )。

如果您可以在任何地方使用无符号值来检索新值,则上面是替换std::srand + std::rand使用的简单方法。

注意 :使用上面描述的内容可能会导致某些值比其他值更有可能被返回,因为engineresult_type没有最大值可能是最高值的偶数倍。存储在目标类型中。
如果你过去并不担心这个问题 - 当使用像rand()%low+high这样的东西时 - 你现在不应该担心它。

注意 :您需要确保std:: engine-type ::result_type至少与所需的值范围一样大( std::mt19937::result_typeuint_fast32_t )。


如果你只需要为引擎播种一次

没有必要首先默认构造一个std::default_random_engine (它只是实现选择的某个引擎的typedef ),然后为它分配一个种子; 这可以通过使用随机引擎的适当构造函数一次完成。

std::random-engine-type engine (seed);

但是,如果你需要为引擎重新播种 ,那么使用std:: random-engine ::seed就可以了。


如果一切都失败了; 创建一个辅助函数

即使你发布的代码看起来有点复杂,你也只想写一次。

如果你发现自己很容易你所写的内容复制+粘贴到代码中的几个地方,那么建议你一如既往地进行复制+粘贴 ; 介绍一个辅助函数。

Intentionally left blank, see other posts for example implementations.

你可以创建一个这样的简单函数:

#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);

如前所述这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM