16

Possible Duplicate:
How to make sure that std::random_shuffle always produces a different result?

I have an array and I wish to shuffle it, I use:

answerPositionArray[0] = 100;
answerPositionArray[1] = 400;
answerPositionArray[2] = 800;
std::random_shuffle(answerPositionArray, answerPositionArray + 2);

But every time I run my program the same shuffle comes out, 400, 800, 100. Is there a way to get the shuffle to be different every time? Eg. first time 100, 800, 400 then 800, 400, 100 etc.

Thanks

Community
  • 1
  • 1
panthro
  • 22,779
  • 66
  • 183
  • 324
  • 18
    You forgot to seed it, `std::srand(std::time(0))`, from what I see. – Rapptz Jan 08 '13 at 18:36
  • Where should I put that line in the code? – panthro Jan 08 '13 at 18:37
  • 2
    @user1013512 Before any calls to Random – lcs Jan 08 '13 at 18:37
  • 1
    @user1013512: Post a [Short, Self Contained, Correct (Compilable), Example](http://sscce.org/) and then maybe we can tell you why, otherwise you're asking us to use psychic powers to debug your code. No can do. – GManNickG Jan 08 '13 at 18:42
  • 1
    @user1013512: the code you posted doesn't have a the call to `srand` you now say it does. And it's not compilable. – Mat Jan 08 '13 at 18:47
  • @user1013512 Please don't update the question with a posted solution. It renders the question meaningless to future viewers. – Praetorian Jan 08 '13 at 18:55
  • This is a great example of why C++14 deprecated and C++17 removed `random_shuffle()` - so users are obliged to use the stdlib's other, far better (P)RNG facilities instead of relying on rubbishy old `rand()` and its lack of enforcement that they use it properly. – underscore_d Sep 25 '18 at 14:06

2 Answers2

44

std::random_shuffle(b,e) uses an implementation-defined source of randomness and so this cannot be portably controlled. Typically implementations use std::rand() and so using std::srand() to seed the rng often works.

// not portable, depends on implementation defined source of randomness in random_shuffle
std::srand(some_seed);
std::random_shuffle(answerPositionArray, answerPositionArray+size);

There is an overload of std::random_shuffle() which takes as a third parameter a random number generator. You can use this form to define the source of randomness so you can seed it.

struct RNG {
    int operator() (int n) {
        return std::rand() / (1.0 + RAND_MAX) * n;
    }
};

std::srand(seed);
std::random_shuffle(answerPositionArray, answerPositionArray+size, RNG());

C++11 introduces another algorithm std::shuffle which takes a UniformRandomNumberGenerator, allowing you to use the C++11 <random> generators:

std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);

std::shuffle(std::begin(answerPositionArray), std::end(answerPositionArray), eng);

Your comments indicate that the problem was that you were not shuffling the entire array, that you were only shuffling the first two elements and the last element was not being touched.

This is a good demonstration of how using magic numbers, as in your code:

std::random_shuffle(answerPositionArray, answerPositionArray + 2);
                                                               ^
                                                               |
                                                 magic number --

can be error prone. Instead you should try to write code that works independently of such values.

// trick for getting an array size
template<typename T, int N> int array_size(T (&)[N]) { return N; }

int answerPositionArray[] = {100, 400, 800};

std::random_shuffle(answerPositionArray,
                    answerPositionArray + array_size(answerPositionArray));

Or once you can use C++11 you can use std::begin and std::end on arrays:

std::random_shuffle(std::begin(answerPositionArray), std::end(answerPositionArray));

Or you can implement begin and end functions yourself in C++03 using the above array size trick:

template<typename T, int N> T *begin(T (&a)[N]) { return a; }
template<typename T, int N> T   *end(T (&a)[N]) { return a + N; }

These methods allow you to avoid having to use a magic number for the array size, and so when you write or modify code you'll be less likely to mistakenly use the wrong value.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • 4
    @user1013512: read the entire post. He includes solutions for both C++98/03 and C++11. – Jerry Coffin Jan 08 '13 at 18:48
  • @user1013512 then you want the second overload of `std::random_shuffle` I mention. Or if you can verify that all the implementations you will ever care about use `std::rand` then you can depend on that implementation detail and use `std::srand()`. – bames53 Jan 08 '13 at 18:50
26

C++ random numbers aren't truly random - they are generated from initial value called seed. If you don't set the seed, it will always be the same, so generated sequence won't change. std::random_shuffle depends on random number generation, so it will behave this way as well.

So how to set the seed? Use:

srand(time(0));

before any calls to functions using random numbers. It will set the seed to current time in seconds. Don't forget to add appropritate header files.

KCH
  • 2,794
  • 2
  • 23
  • 22