Recommended way to initialize srand?

2018-12-31 17:19发布

问题:

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 numbers, srand is usually initialized to some distinctive value, like those related with the execution time. For example, the value returned by the function time (declared in header ctime) is different each second, which is distinctive enough for most randoming needs.

Unixtime isn\'t distinctive enough for my application. What\'s a better way to initialize this? Bonus points if it\'s portable, but the code will primarily be running on Linux hosts.

I was thinking of doing some pid/unixtime math to get an int, or possibly reading data from /dev/urandom.

Thanks!

EDIT

Yes, I am actually starting my application multiple times a second and I\'ve run into collisions.

回答1:

The best answer is to use the Boost random number stuff. Or if you have access to C++11 use the <random> header.

But if we are talking about rand() and srand()
The best way is just to use time():

int main()
{
    srand(time(NULL));

    ...
}

Be sure to do this at the beginning of your program, and not every time you call rand()!

Every time you start up, time() will return a unique value (unless you start the application multiple times a second). In 32 bit systems, it will only repeat every 60 years or so.

I know you don\'t think time is unique enough but I find that hard to believe. But I have been known to be wrong.

If you are starting a lot of copies of your application simultaneously you could use a timer with a finer resolution. But then you run the risk of a shorter time period before the value repeats.

OK, so if you really think you are starting multiple applications a second.
Then use a finer grain on the timer.

 int main()
 {
     struct timeval time; 
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     // Assuming you did not need quite that accuracy
     // Also do not assume the system clock has that accuracy.
     srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

     // The trouble here is that the seed will repeat every
     // 24 days or so.

     // If you use 100 (rather than 1000) the seed repeats every 248 days.

     // Do not make the MISTAKE of using just the tv_usec
     // This will mean your seed repeats every second.
 }


回答2:

This is what I\'ve used for small command line programs that can be run frequently (multiple times a second):

unsigned long seed = mix(clock(), time(NULL), getpid());

Where mix is:

// http://www.concentric.net/~Ttwang/tech/inthash.htm
unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
    a=a-b;  a=a-c;  a=a^(c >> 13);
    b=b-c;  b=b-a;  b=b^(a << 8);
    c=c-a;  c=c-b;  c=c^(b >> 13);
    a=a-b;  a=a-c;  a=a^(c >> 12);
    b=b-c;  b=b-a;  b=b^(a << 16);
    c=c-a;  c=c-b;  c=c^(b >> 5);
    a=a-b;  a=a-c;  a=a^(c >> 3);
    b=b-c;  b=b-a;  b=b^(a << 10);
    c=c-a;  c=c-b;  c=c^(b >> 15);
    return c;
}


回答3:

if you need a better random number generator, don\'t use the libc rand. Instead just use something like /dev/random or /dev/urandom directly (read in an int directly from it or something like that).

The only real benefit of the libc rand is that given a seed, it is predictable which helps with debugging.



回答4:

On windows:

srand(GetTickCount());

provides a better seed than time() since its in milliseconds.



回答5:

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.



回答6:

Best way is to use another pseudorandom number generator. Mersenne twister (and Wichmann-Hill) is my recommendation.

http://en.wikipedia.org/wiki/Mersenne_twister



回答7:

i suggest you see unix_random.c file in mozilla code. ( guess it is mozilla/security/freebl/ ...) it should be in freebl library.

there it uses system call info ( like pwd, netstat ....) to generate noise for the random number;it is written to support most of the platforms (which can gain me bonus point :D ).



回答8:

The real question you must ask yourself is what randomness quality you need.

libc random is a LCG

The quality of randomness will be low whatever input you provide srand with.

If you simply need to make sure that different instances will have different initializations, you can mix process id (getpid), thread id and a timer. Mix the results with xor. Entropy should be sufficient for most applications.

Example :

struct timeb tp;
ftime(&tp);   
srand(static_cast<unsigned int>(getpid()) ^ 
static_cast<unsigned int>(pthread_self()) ^ 
static_cast<unsigned int >(tp.millitm));

For better random quality, use /dev/urandom. You can make the above code portable in using boost::thread and boost::date_time.



回答9:

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


回答10:

#include <stdio.h>
#include <sys/time.h>
main()
{
     struct timeval tv;
     gettimeofday(&tv,NULL);
     printf(\"%d\\n\",  tv.tv_usec);
     return 0;
}

tv.tv_usec is in microseconds. This should be acceptable seed.



回答11:

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.



回答12:

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.



回答13:

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.



回答14:

Include the header at the top of your program, and write:

srand(time(NULL));

In your program before you declare your random number. Here is an example of a program that prints a random number between one and ten:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
   //Initialize srand
   srand(time(NULL));

   //Create random number
   int n = rand() % 10 + 1;

   //Print the number
   cout << n << endl; //End the line

   //The main function is an int, so it must return a value
   return 0;
}


标签: c++ random srand