Recommended way to initialize srand?

前端 未结 15 1653
夕颜
夕颜 2020-11-22 08:07

I need a \'good\' way to initialize the pseudo-random number generator in C++. I\'ve found an article that states:

In order to generate random-like

相关标签:
15条回答
  • 2020-11-22 08:32

    Assuming that the randomness of srand() + rand() is enough for your purposes, the trick is in selecting the best seed for srand. time(NULL) is a good starting point, but you'll run into problems if you start more than one instance of the program within the same second. Adding the pid (process id) is an improvement as different instances will get different pids. I would multiply the pid by a factor to spread them more.

    But let's say you are using this for some embedded device and you have several in the same network. If they are all powered at once and you are launching the several instances of your program automatically at boot time, they may still get the same time and pid and all the devices will generate the same sequence of "random" numbers. In that case, you may want to add some unique identifier of each device (like the CPU serial number).

    The proposed initialization would then be:

    srand(time(NULL) + 1000 * getpid() + (uint) getCpuSerialNumber()); 
    

    In a Linux machine (at least in the Raspberry Pi where I tested this), you can implement the following function to get the CPU Serial Number:

    // Gets the CPU Serial Number as a 64 bit unsigned int. Returns 0 if not found.
    uint64_t getCpuSerialNumber() {
    
        FILE *f = fopen("/proc/cpuinfo", "r");
        if (!f) {
            return 0;
        }
    
        char line[256];
        uint64_t serial = 0;
        while (fgets(line, 256, f)) {
            if (strncmp(line, "Serial", 6) == 0) {
                serial = strtoull(strchr(line, ':') + 2, NULL, 16);
            }
        }
        fclose(f);
    
        return serial;
    }
    
    0 讨论(0)
  • 2020-11-22 08:35

    The c++11 version of the top voted post by Jonathan Wright:

    #include <ctime>
    #include <random>
    #include <thread>
    
    ...
    
    const auto time_seed = static_cast<size_t>(std::time(0));
    const auto clock_seed = static_cast<size_t>(std::clock());
    const size_t pid_seed =
          std::hash<std::thread::id>()(std::this_thread::get_id());
    
    std::seed_seq seed_value { time_seed, clock_seed, pid_seed };
    
    ...
    // E.g seeding an engine with the above seed.
    std::mt19937 gen;
    gen.seed(seed_value);
    
    0 讨论(0)
  • 2020-11-22 08:35

    As long as your program is only running on Linux (and your program is an ELF executable), you are guaranteed that the kernel provides your process with a unique random seed in the ELF aux vector. The kernel gives you 16 random bytes, different for each process, which you can get with getauxval(AT_RANDOM). To use these for srand, use just an int of them, as such:

    #include <sys/auxv.h>
    
    void initrand(void)
    {
        unsigned int *seed;
    
        seed = (unsigned int *)getauxval(AT_RANDOM);
        srand(*seed);
    }
    

    It may be possible that this also translates to other ELF-based systems. I'm not sure what aux values are implemented on systems other than Linux.

    0 讨论(0)
  • 2020-11-22 08:35

    Suppose you have a function with a signature like:

    int foo(char *p);
    

    An excellent source of entropy for a random seed is a hash of the following:

    • Full result of clock_gettime (seconds and nanoseconds) without throwing away the low bits - they're the most valuable.
    • The value of p, cast to uintptr_t.
    • The address of p, cast to uintptr_t.

    At least the third, and possibly also the second, derive entropy from the system's ASLR, if available (the initial stack address, and thus current stack address, is somewhat random).

    I would also avoid using rand/srand entirely, both for the sake of not touching global state, and so you can have more control over the PRNG that's used. But the above procedure is a good (and fairly portable) way to get some decent entropy without a lot of work, regardless of what PRNG you use.

    0 讨论(0)
  • 2020-11-22 08:37

    For those using Visual Studio here's yet another way:

    #include "stdafx.h"
    #include <time.h>
    #include <windows.h> 
    
    const __int64 DELTA_EPOCH_IN_MICROSECS= 11644473600000000;
    
    struct timezone2 
    {
      __int32  tz_minuteswest; /* minutes W of Greenwich */
      bool  tz_dsttime;     /* type of dst correction */
    };
    
    struct timeval2 {
    __int32    tv_sec;         /* seconds */
    __int32    tv_usec;        /* microseconds */
    };
    
    int gettimeofday(struct timeval2 *tv/*in*/, struct timezone2 *tz/*in*/)
    {
      FILETIME ft;
      __int64 tmpres = 0;
      TIME_ZONE_INFORMATION tz_winapi;
      int rez = 0;
    
      ZeroMemory(&ft, sizeof(ft));
      ZeroMemory(&tz_winapi, sizeof(tz_winapi));
    
      GetSystemTimeAsFileTime(&ft);
    
      tmpres = ft.dwHighDateTime;
      tmpres <<= 32;
      tmpres |= ft.dwLowDateTime;
    
      /*converting file time to unix epoch*/
      tmpres /= 10;  /*convert into microseconds*/
      tmpres -= DELTA_EPOCH_IN_MICROSECS; 
      tv->tv_sec = (__int32)(tmpres * 0.000001);
      tv->tv_usec = (tmpres % 1000000);
    
    
      //_tzset(),don't work properly, so we use GetTimeZoneInformation
      rez = GetTimeZoneInformation(&tz_winapi);
      tz->tz_dsttime = (rez == 2) ? true : false;
      tz->tz_minuteswest = tz_winapi.Bias + ((rez == 2) ? tz_winapi.DaylightBias : 0);
    
      return 0;
    }
    
    
    int main(int argc, char** argv) {
    
      struct timeval2 tv;
      struct timezone2 tz;
    
      ZeroMemory(&tv, sizeof(tv));
      ZeroMemory(&tz, sizeof(tz));
    
      gettimeofday(&tv, &tz);
    
      unsigned long seed = tv.tv_sec ^ (tv.tv_usec << 12);
    
      srand(seed);
    
    }
    

    Maybe a bit overkill but works well for quick intervals. gettimeofday function found here.

    Edit: upon further investigation rand_s might be a good alternative for Visual Studio, it's not just a safe rand(), it's totally different and doesn't use the seed from srand. I had presumed it was almost identical to rand just "safer".

    To use rand_s just don't forget to #define _CRT_RAND_S before stdlib.h is included.

    0 讨论(0)
  • 2020-11-22 08:38

    C++11 random_device

    If you need reasonable quality then you should not be using rand() in the first place; you should use the <random> library. It provides lots of great functionality like a variety of engines for different quality/size/performance trade-offs, re-entrancy, and pre-defined distributions so you don't end up getting them wrong. It may even provide easy access to non-deterministic random data, (e.g., /dev/random), depending on your implementation.

    #include <random>
    #include <iostream>
    
    int main() {
        std::random_device r;
        std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
        std::mt19937 eng(seed);
    
        std::uniform_int_distribution<> dist{1,100};
    
        for (int i=0; i<50; ++i)
            std::cout << dist(eng) << '\n';
    }
    

    eng is a source of randomness, here a built-in implementation of mersenne twister. We seed it using random_device, which in any decent implementation will be a non-determanistic RNG, and seed_seq to combine more than 32-bits of random data. For example in libc++ random_device accesses /dev/urandom by default (though you can give it another file to access instead).

    Next we create a distribution such that, given a source of randomness, repeated calls to the distribution will produce a uniform distribution of ints from 1 to 100. Then we proceed to using the distribution repeatedly and printing the results.

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