3

I am learning C++, trying to generate a few random numbers within a specified range.

I'm trying to implement this answer here on SO, and revise it for it to be able to generate 64-bit integer.

Which I basically succeeded in, basic program could look like:

#include <iostream>
#include <random>

unsigned long long int random_integer(unsigned long long int rand_min, unsigned long long int rand_max)
{

    // initialize = seed the random device
    std::random_device random_device;

    // use Mersenne Twister as random-number generator engine
    std::mt19937_64 random_number_generator(random_device());

    // number distribution, guaranteed unbiased
    std::uniform_int_distribution<unsigned long long int> number_distribution(rand_min, rand_max);

    return number_distribution(random_number_generator);

}

int main()
{
    for (int i = 1; i <= 10; i++)
    {
        std::cout << random_integer(10000000, 100000000) << std::endl;
    }
    return 0;
}

Questions:

  • should I define the random device:

    std::random_device random_device
    

    out of the function?

  • I am also unsure where to define the Mersenne Twister:

    std::mt19937_64 random_number_generator(random_device());
    

    for the function to work as expected.

Vlastimil Burián
  • 3,024
  • 2
  • 31
  • 52
  • Add `static thread_local` to both `random_device` and `random_number_generator`. You don't want to reinitialize those on every function call. Otherwise this looks okay to me. – Henri Menke Nov 12 '17 at 07:12
  • What I proposed is also what the GCC standard library is doing for [`std::randint` (C++17)](http://en.cppreference.com/w/cpp/experimental/randint): https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/experimental/random#L42-L61 – Henri Menke Nov 12 '17 at 07:15
  • You also should use `std::uint64_t` instead of `unsigned long long`. For the latter one there is no guarantee that it is actually 64-bit on your platform (even though it might be usually the case). The only guarantee you get is that it is equal to or larger than `unsigned long`. – Henri Menke Nov 12 '17 at 07:25
  • @HenriMenke May I ask why did you changed normal `(` `)` for `{` `}` in `rng{std::random_device{}()}` in the answer + I also don't understand why there is `random_device{}()` braces. Thank you in advance. – Vlastimil Burián Nov 12 '17 at 08:36
  • 2
    This is to prevent something known as [most vexing parse](https://en.wikipedia.org/wiki/Most_vexing_parse) by using the [C++11 uniform initialization syntax](https://en.wikipedia.org/wiki/C%2B%2B11#Uniform_initialization). If you use the old-style parentheses you have to add extra ones at certain points to convince the compiler that this is not ambiguous with a function declaration. – Henri Menke Nov 12 '17 at 09:05

1 Answers1

3

No, you should not define the random device outside of the function. It's best to reduce objects to the minimum scope required to satisfy their usefulness. This helps avoid tougher to track / understand state.

You should define the Mersenne Twister inside the function as well.

Additionally, if you want to get a random 64 bit (unsigned) integer, use std::uint64_t from <cstdint>.

Using the static and thread_local keywords, we can set up the function to only initialize the Twister / use the std::random_device once, rather than recreate them every function call, which is nice!

Here's something nice you can do that I find convenient when hacking about:

#include <random>
#include <cstdint>

std::uint64_t random_integer(std::uint64_t rand_min, std::uint64_t rand_max) {
  static thread_local std::mt19937_64 rng{std::random_device{}()};
  std::uniform_int_distribution<std::uint64_t> dist(rand_min, rand_max);
  return dist(rng);
}
Henri Menke
  • 10,705
  • 1
  • 24
  • 42
druckermanly
  • 2,694
  • 15
  • 27