Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: How to set seed using <random>

Tags:

random

c++11

I am exercising the random library, new to C++11. I wrote the following minimal program:

#include <iostream>
#include <random>
using namespace std;
int main() {
    default_random_engine eng;
    uniform_real_distribution<double> urd(0, 1);
    cout << "Uniform [0, 1): " << urd(eng);
}

When I run this repeatedly it gives the same output each time:

>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538
>a
Uniform [0, 1): 0.131538

I would like to have the program set the seed differently each time it is called, so that a different random number is generated each time. I am aware that random provides a facility called seed_seq, but I find the explanation of it (at cplusplus.com) totally obscure:

http://www.cplusplus.com/reference/random/seed_seq/

I'd appreciate advice on how to have a program generate a new seed each time it is called: The simpler the better.

My platform(s):

  • Windows 7 : TDM-GCC compiler
like image 649
Argent Avatar asked Dec 28 '15 09:12

Argent


People also ask

How do you seed random numbers?

For example, “take a number x, add 900 +x, then subtract 52.” In order for the process to start, you have to specify a starting number, x (the seed). Let's take the starting number 77: Add 900 + 77 = 977. Subtract 52 = 925.

What is set seed in random?

Definition and Usage. The seed() method is used to initialize the random number generator. The random number generator needs a number to start with (a seed value), to be able to generate a random number. By default the random number generator uses the current system time.

How do I set a random seed in C++?

The srand() function in C++ can perform pseudo-random number calculation. This function requires a seed value which forms the basis of computation of random numbers. With the help of the seed value, srand() sets the stage for the generation of pseudo-random numbers by the rand() function. And Voila!

How do you set a seed value?

set seed (value) where value specifies the initial value of the random number seed. In the above line,123 is set as the random number value. The main point of using the seed is to be able to reproduce a particular sequence of 'random' numbers. and sed(n) reproduces random numbers results by seed.


2 Answers

The point of having a seed_seq is to increase the entropy of the generated sequence. If you have a random_device on your system, initializing with multiple numbers from that random device may arguably do that. On a system that has a pseudo-random number generator I don't think there is an increase in randomness, i.e. generated sequence entropy.

Building on that your approach:

If your system does provide a random device then you can use it like this:

  std::random_device r;
  // std::seed_seq ssq{r()};
  // and then passing it to the engine does the same
  default_random_engine eng{r()};
  uniform_real_distribution<double> urd(0, 1);
  cout << "Uniform [0, 1): " << urd(eng);

If your system does not have a random device then you can use time(0) as a seed to the random_engine

  default_random_engine eng{static_cast<long unsigned int>(time(0))};
  uniform_real_distribution<double> urd(0, 1);
  cout << "Uniform [0, 1): " << urd(eng);

If you have multiple sources of randomness you can actually do this (e.g. 2)

std::seed_seq seed{ r1(), r2() };
  default_random_engine eng{seed};
  uniform_real_distribution<double> urd(0, 1);
  cout << "Uniform [0, 1): " << urd(eng);

where r1 , r2 are different random devices , e.g. a thermal noise or quantum source .

Ofcourse you could mix and match

std::seed_seq seed{ r1(), static_cast<long unsigned int>(time(0)) };
  default_random_engine eng{seed};
  uniform_real_distribution<double> urd(0, 1);
  cout << "Uniform [0, 1): " << urd(eng);

Finally, I like to initialize with an one liner:

  auto rand = std::bind(std::uniform_real_distribution<double>{0,1},
              std::default_random_engine{std::random_device()()});
  std::cout << "Uniform [0,1): " << rand();

If you worry about the time(0) having second precision you can overcome this by playing with the high_resolution_clock either by requesting the time since epoch as designated firstly by bames23 below:

static_cast<long unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count()) 

or maybe just play with CPU randomness

long unsigned int getseed(int const K)
{

    typedef std::chrono::high_resolution_clock hiclock;

    auto gett= [](std::chrono::time_point<hiclock> t0)
    {
        auto tn = hiclock::now();
        return static_cast<long unsigned int>(std::chrono::duration_cast<std::chrono::microseconds>(tn-t0).count());
    };

    long unsigned int diffs[10];
    diffs[0] = gett(hiclock::now());
    for(int i=1; i!=10; i++)
    {
        auto last = hiclock::now();
        for(int k=K; k!=0; k--)
        {
            diffs[i]= gett(last);
        }
    }

    return *std::max_element(&diffs[1],&diffs[9]);
}
like image 63
g24l Avatar answered Oct 10 '22 02:10

g24l


#include <iostream>
#include <random>

using namespace std;

int main() {
    std::random_device r;                                       // 1
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; // 2
    std::mt19937 eng(seed);                                     // 3

    uniform_real_distribution<double> urd(0, 1);

    cout << "Uniform [0, 1): " << urd(eng);
}

In order to get unpredictable results from a pseudo-random number generator we need a source of unpredictable seed data. On 1 we create a std::random_device for this purpose. On 2 we use a std::seed_seq to combine several values produced by random_device into a form suitable for seeding a pseudo-random number generator. The more unpredictable data that is fed into the seed_seq, the less predictable the results of the seeded engine will be. On 3 we create a random number engine using the seed_seq to seed the engine's initial state.

A seed_seq can be used to initialize multiple random number engines; seed_seq will produce the same seed data each time it is used.

Note: Not all implemenations provide a source of non-deterministic data. Check your implementation's documentation for std::random_device.


If your platform does not provide a non-deterministic random_device then some other sources can be used for seeding. The article Simple Portable C++ Seed Entropy suggests a number of alternative sources:

  • A high resolution clock such as std::chrono::high_resolution_clock (time() typically has a resolution of one second which generally too low)
  • Memory configuration which on modern OSs varies due to address space layout randomization (ASLR)
  • CPU counters or random number generators. C++ does not provide standardized access to these so I won't use them.
  • thread id
  • A simple counter (which only matters if you seed more than once)

For example:

#include <chrono>
#include <iostream>
#include <random>
#include <thread>
#include <utility>

using namespace std;

// we only use the address of this function
static void seed_function() {}

int main() {
    // Variables used in seeding
    static long long seed_counter = 0;
    int var;
    void *x = std::malloc(sizeof(int));
    free(x);

    std::seed_seq seed{
        // Time
        static_cast<long long>(std::chrono::high_resolution_clock::now()
                                   .time_since_epoch()
                                   .count()),
        // ASLR
        static_cast<long long>(reinterpret_cast<intptr_t>(&seed_counter)),
        static_cast<long long>(reinterpret_cast<intptr_t>(&var)),
        static_cast<long long>(reinterpret_cast<intptr_t>(x)),
        static_cast<long long>(reinterpret_cast<intptr_t>(&seed_function)),
        static_cast<long long>(reinterpret_cast<intptr_t>(&_Exit)),
        // Thread id
        static_cast<long long>(
            std::hash<std::thread::id>()(std::this_thread::get_id())),
        // counter
        ++seed_counter};

    std::mt19937 eng(seed);

    uniform_real_distribution<double> urd(0, 1);

    cout << "Uniform [0, 1): " << urd(eng);
}
like image 34
bames53 Avatar answered Oct 10 '22 01:10

bames53