Mr Cherno
Mr Cherno

Reputation: 315

Sequential Random Numbers in C++?

I'm looking for a way to generate two random numbers quickly in C++. The code is for a die and needs to generate a random number when rolled. My code currently looks like this:

Die::Die() {
    srand(time(NULL));
}

void Die::roll() {
    value = rand() % 6 + 1;
}

If I create two objects with this code, srand(), being a static (non-instance based) function, will generate a new seed for all objects. I've also tried doing this:

void Die::roll() {
    srand(time(NULL));
    value = rand() % 6 + 1;
}

Rather then having it in the constructor, but if I call them quickly like follows:

die0->roll();
die1->roll();

Their values are usually equal. Is there any way to actually make this random, every time? Thanks for all your help. :)

Upvotes: 1

Views: 804

Answers (4)

Shafik Yaghmour
Shafik Yaghmour

Reputation: 158469

The least error prone method will be to use uniform_int_distribution from the random header. This also avoids modulo bias, this is basic example:

#include <iostream>
#include <random>

 class Die
 {
   public:
     template <class Generator>
     void roll( Generator &g )
     {
         std::uniform_int_distribution<int> dist(1,6);
         std::cout << dist(g) ;
     }

   private:

 } ;

int main()
{
    std::random_device rd;

    std::mt19937 e1(rd());

    Die d1, d2 ;

    for (int n = 0; n < 10; ++n) {
            d1.roll( e1 ) ;
            std::cout << " , " ;
            d2.roll( e1 ) ;
            std::cout << std::endl ;
    }
}

Alternatively, you can also use static members, as James suggests:

class Die
{
   public:
     Die()  {} ;

     void roll()
     {
         std::uniform_int_distribution<int> dist(1,6);
         std::cout << dist(e1) ;
     }

   private:
      static std::random_device rd;    
      static std::mt19937 e1 ;
} ;

Upvotes: 1

James Kanze
James Kanze

Reputation: 153919

As others have said, you don't want to reseed the generator more than once. If Die is the sole user of rand (or even if it isn't), you can use a variable at name space scope to seed it:

int seeded = (srand( time( NULL ) ), rand());

Whether this is a good idea or not depends; it makes it impossible to duplicate a run of your code for debugging purposes, for example. If this is an issue, you'll want to 1) log the seed you use, and 2) add an option to the command line to specify the seed; the latter means calling srand from main.

(Note too that I've added a call to rand() in the initialization. In at least one wide spread implementation, the first call to rand() returns the seed, which means that it can be very predictable.)

Upvotes: 0

Kindread
Kindread

Reputation: 966

The problem is, if the srands occur close enough to each other in time, then you are seeding the rng with the same value, and so the first values you get after each seed will be the same. To avoid this, you can ensure that the seeding only occurs once. For example, you can remove the srand from your Die class, and seed sometime before your first use of Die.

Alternatively, if you don't want the consumer of Die to have the burden of calling srand, you could keep the srand in Die's constructor, but guard against calling it multiple times with a static bool.

Die::Die()
{
  static bool seeded = false;
  if (!seeded)
  {
    srand(time(NULL));
    seeded = true;
  }
}

Note that this srand can still affect other classes that srand() and rand(), but chances are this wouldn't matter. But if you need a random number within multiple classes that you are defining, you could look at creating an RNG singleton to generate numbers for you.

Upvotes: 1

mark
mark

Reputation: 5469

Assuming the program is running quickly enough, the time (in seconds) will generally be the same, which means your seed will be the same, which means your random values with be the same. As the commenters have said, do the random seed elsewhere (like the start of your program or thread).

Upvotes: 4

Related Questions