Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::random_shuffle produces same result each time [duplicate]

Tags:

c++

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

like image 823
panthro Avatar asked Jan 08 '13 18:01

panthro


Video Answer


1 Answers

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.

like image 68
bames53 Avatar answered Oct 01 '22 20:10

bames53