C++11: How to set seed using

前端 未结 2 509
醉话见心
醉话见心 2020-12-01 11:11

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

#include 
#include 
using namespace st         


        
相关标签:
2条回答
  • 2020-12-01 11:26
    #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);
    }
    
    0 讨论(0)
  • 2020-12-01 11:33

    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]);
    }
    
    0 讨论(0)
提交回复
热议问题