9

I'm using the random_shuffle on a vector like this:

#include <algorithm>
vector <Card> deck;
//some code to add cards to the deck here
random_shuffle ( deck.begin(), deck.end() );

When run, the content of the deck is mixed up, but this mixed-up order is kept when I restart the program.

Did I miss something? How can I make it truly random?

Coding Mash
  • 3,338
  • 5
  • 24
  • 45
Chin
  • 19,717
  • 37
  • 107
  • 164

3 Answers3

15

You need to seed the psuedo-random number generator first using srand.

#include <algorithm>
#include <cstdlib>

...

std::srand(std::time(0));

vector <Card> deck;
//some code to add cards to the deck here
random_shuffle ( deck.begin(), deck.end() );

Note from link above:

Generally speaking, the pseudo-random number generator should only be seeded once, before any calls to rand(), and the start of the program. It should not be repeatedly seeded, or reseeded every time you wish to generate a new batch of pseudo-random numbers.

Joe
  • 56,979
  • 9
  • 128
  • 135
  • @Chin: note that you should seed the generator once when the program starts and never again. – Mooing Duck Nov 19 '12 at 18:40
  • is there any difference between time(0) and time(NULL)? – Chin Nov 19 '12 at 18:40
  • Good point I assumed the code he posted was part of `int main()`. I included the excerpt from the link.. – Joe Nov 19 '12 at 18:43
  • @Chin, `NULL` should be used with pointers, and has been superseded by `nullptr`. – chris Nov 19 '12 at 18:43
  • 1
    @Chin There are three ways of writing a null pointer in C++: Using the old `NULL` (inherited from C), using `0` (the old C++ way) and using the new C++11 keyword `nullptr`. – Some programmer dude Nov 19 '12 at 18:43
  • I think `cstdlib` is not needed, but `ctime` instead, otherwise I get a "identifier `time` is undefined" – Chin Nov 19 '12 at 18:47
  • `srand()` requires `cstdlib`. Something else might be pulling it in for you, but you should included it anyway. – Fred Larson Nov 19 '12 at 18:53
  • 8
    `random_shuffle()` isn't actually specified to use `rand()` and so `srand()` may not have any impact. If you want to be sure, you should use one of the C++11 forms, `random_shuffle(b, e, RNG)` or `shuffle(b, e, uRNG)` – bames53 Nov 19 '12 at 20:24
  • That is correct, it just so happens that `rand` is often used. – Joe Nov 19 '12 at 20:32
  • You have to `#include ` for `std::time()`. – maxschlepzig Feb 05 '14 at 23:05
10

With current C++ (i.e. C++11) you can use the shuffle algorithm which can take a pseudo random number generator (PRNG) object (which you can seed) as third parameter:

#include <iostream>
#include <random>
#include <algorithm>
#include <vector>
#include <string>
#include <ctime>
using namespace std;

int main(int argc, char **argv)
{
  vector<string> v;
  for (int i = 1; i<argc; ++i)
    v.push_back(argv[i]);
  mt19937 g(static_cast<uint32_t>(time(0)));
  shuffle(v.begin(), v.end(), g);
  for (auto &x : v)
    cout << x << ' ';
  cout << '\n';
}

(for GCC 4.8.2 you need to compile it via g++ -std=c++11 -Wall -g shuffle.cc -o shuffle)

In the above example, the PRNG is seeded with the current system time.

For pre-C++11 compilers you only have the random_shuffle algorithm in the STL - but even with that you can optionally specify a number generator object/function to it. Note that you can't just pluck in a PRNG object like mtl19937 into it (because it does not provide a operator()(U upper_bound) member).

Thus, you can supply your own adapter like this:

#include <iostream>
#include <random>
#include <algorithm>
#include <vector>
#include <string>
#include <ctime>
using namespace std;

struct Gen {
  mt19937 g;
  Gen()
   : g(static_cast<uint32_t>(time(0)))
  {
  }
  size_t operator()(size_t n)
  {
    std::uniform_int_distribution<size_t> d(0, n ? n-1 : 0);
    return d(g);
  }
};

int main(int argc, char **argv)
{
  vector<string> v;
  for (int i = 1; i<argc; ++i)
    v.push_back(argv[i]);
  random_shuffle(v.begin(), v.end(), Gen());
  for (vector<string>::const_iterator i = v.begin(); i != v.end(); ++i)
    cout << *i << ' ';
  cout << '\n';
}
maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
3

Place the line:

srand (time (0));

in your code before you do anything else, such as at the start of main().

Without that, the default seed of 1 will always be used, leading to identical sequences from rand() and anything that uses it.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953