How to use to replace rand()?

后端 未结 5 810
执笔经年
执笔经年 2020-12-28 13:45

C++11 introduced the header with declarations for random number engines and random distributions. That\'s great - time to replace those uses of <

相关标签:
5条回答
  • 2020-12-28 14:08

    Abusing the fact that engines return values directly

    All engines defined in <random> has an operator()() that can be used to retrieve the next generated value, as well as advancing the internal state of the engine.

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

    It shall however be noted that all engines return a value of some unsigned integral type, meaning that they can potentially overflow a signed integral (which will then lead to undefined-behavior).

    If you are fine with using unsigned values everywhere you retrieve a new value, the above is an easy way to replace usage of std::srand + std::rand.

    Note: Using what has been described above might lead to some values having a higher chance of being returned than others, due to the fact that the result_type of the engine not having a max value that is an even multiple of the highest value that can be stored in the destination type.
    If you have not worried about this in the past — when using something like rand()%low+high — you should not worry about it now.

    Note: You will need to make sure that the std::engine-type::result_type is at least as large as your desired range of values (std::mt19937::result_type is uint_fast32_t).


    If you only need to seed the engine once

    There is no need to first default-construct a std::default_random_engine (which is just a typedef for some engine chosen by the implementation), and later assigning a seed to it; this could be done all at once by using the appropriate constructor of the random-engine.

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

    If you however need to re-seed the engine, using std::random-engine::seed is the way to do it.


    If all else fails; create a helper-function

    Even if the code you have posted looks slightly complicated, you are only meant to write it once.

    If you find yourself in a situation where you are tempted to just copy+paste what you have written to several places in your code it is recommended, as always when doing copy+pasting; introduce a helper-function.

    Intentionally left blank, see other posts for example implementations.
    0 讨论(0)
  • 2020-12-28 14:10

    How about randutils by Melissa O'Neill of pcg-random.org?

    From the 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";
    
    0 讨论(0)
  • 2020-12-28 14:11

    Assuming you want the behavior of the C-style rand and srand functions, including their quirkiness, but with good random, this is the closest I could get.

    #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
    

    The replacement::* functions can be used exactly like their std::* counterparts from <cstdlib>. I have added a srand overload that takes no arguments and seeds the engine with a “real” random number obtained from a std::random_device. How “real” that randomness will be is of course implementation defined.

    The engine and the distribution are held as thread_local static instances so they carry state across multiple calls but still allow different threads to observe predictable sequences. (It's also a performance gain because you don't need to re-construct the engine or use locks and potentially trash other people's cashes.)

    I've used std::default_random_engine because you did but I don't like it very much. The Mersenne Twister engines (std::mt19937 and std::mt19937_64) produce much better “randomness” and, surprisingly, have also been observed to be faster. I don't think that any compliant program must rely on std::rand being implemented using any specific kind of pseudo random engine. (And even if it did, implementations are free to define std::default_random_engine to whatever they like so you'd have to use something like std::minstd_rand to be sure.)

    0 讨论(0)
  • 2020-12-28 14:15

    Is there a reasonably simple way to replace srand() and rand()?

    Full disclosure: I don't like rand(). It's bad, and it's very easily abused.

    The C++11 random library fills in a void that has been lacking for a long, long time. The problem with high quality random libraries is that they're oftentimes hard to use. The C++11 <random> library represents a huge step forward in this regard. A few lines of code and I have a very nice generator that behaves very nicely and that easily generates random variates from many different distributions.


    Given the above, my answer to you is a bit heretical. If rand() is good enough for your needs, use it. As bad as rand() is (and it is bad), removing it would represent a huge break with the C language. Just make sure that the badness of rand() truly is good enough for your needs.

    C++14 didn't deprecate rand(); it only deprecated functions in the C++ library that use rand(). While C++17 might deprecate rand(), it won't delete it. That means you have several more years before rand() disappears. The odds are high that you will have retired or switched to a different language by the time the C++ committee finally does delete rand() from the C++ standard library.

    I'm creating random inputs to benchmark different implementations of std::sort() using something along the lines of std::vector<int> v(size); std::generate(v.begin(), v.end(), std::rand);

    You don't need a cryptographically secure PRNG for that. You don't even need Mersenne Twister. In this particular case, rand() probably is good enough for your needs.


    Update
    There is a nice simple replacement for rand() and srand() in the C++11 random library: 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';
        }
    }
    

    The function std::minstd_rand::operator()() returns a std::uint_fast32_t. However, the algorithm restricts the result to between 1 and 231-2, inclusive. This means the result will always convert safely to a std::int_fast32_t (or to an int if int is at least 32 bits long).

    0 讨论(0)
  • 2020-12-28 14:26

    You can create a simple function like this:

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

    And later use it like this:

    int myRandValue = modernRand(n);
    

    As mentioned here

    0 讨论(0)
提交回复
热议问题