Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues with seeding a pseudo-random number generator more than once?

I've seen quite a few recommendations for not seeding pseudo-random number generators more than once per execution, but never accompanied by a thorough explanation. Of course, it is easy to see why the following (C/C++) example is not a good idea:

int get_rand() {
  srand(time(NULL));
  return rand();
}

since calling get_rand several times per second produces repeated results.

But wouldn't the following example still be an acceptable solution?

MyRand.h

#ifndef MY_RAND_H
#define MY_RAND_H

class MyRand
{
  public:
    MyRand();
    int get_rand() const;
  private:
    static unsigned int seed_base;
};

#endif

MyRand.cpp

#include <ctime>
#include <cstdlib>
#include "MyRand.h"

unsigned int MyRand::seed_base = static_cast<unsigned int>(time(NULL));

MyRand::MyRand()
{
  srand(seed_base++);
}

int MyRand::get_rand() const
{
  return rand();
}

main.cpp

#include <iostream>
#include "MyRand.h"

int main(int argc, char *argv[]) 
{
  for (int i = 0; i < 100; i++) 
  {
    MyRand r;
    std::cout << r.get_rand() << " ";
  }
}

i.e. even though MyRand:s constructor is called several times in rapid succession, each call to srand has a different parameter. Obviously, this is not thread-safe, but then again neither is rand.

like image 826
Flygsand Avatar asked Jun 10 '09 17:06

Flygsand


3 Answers

Each time you call a pseudo-random number generator function, the generator takes some internal state and produces a pseudo-random number and a new internal state. The algorithm for transforming the internal state is carefully chosen so the output appears random.

When you seed the random number generator, you're basically setting this internal state. If you reset the internal state to some predictable value, you'll lose the appearance of randomness.

For example, a popular, simple RNG is a linear congruential generator. Numbers are generated like this:

X[n+1] = (a X[n] + c) mod m

In this case, X[n+1] is both the result and the new internal state. If you seed the generator every time as you suggest above, you'll get a sequence that looks like this:

{(ab + c) mod m, (a(b+1) + c) mod m, (a(b+2) + c) mod m, ...}

where b is your seed_base. This doesn't look random at all.

like image 96
Jay Conrod Avatar answered Oct 13 '22 23:10

Jay Conrod


If your seed is predictable, which it is here since you're just incrementing it, the output from rand() will also be predictable.

It really depends on why you want to generate random numbers, and how "random" is an acceptable random for you. In your example, it may avoid duplicates in rapid succession, and that may be good enough for you. After all, what matters is that it runs.

On almost every platform there is a better way to generate random numbers than rand().

like image 22
i_am_jorf Avatar answered Oct 13 '22 23:10

i_am_jorf


Well it's extra processing that doesn't need to be done.

In that scenario I'd just call the constructor once with a time-based seed before the start of the loop. That will guarantee random results without the extra overhead of changing seeds for every iteration.

I wouldn't think your method is any more random than that.

like image 1
Steve Wortham Avatar answered Oct 14 '22 00:10

Steve Wortham