How to shuffle a std::vector?

C++ShuffleStdvector

C++ Problem Overview


I am looking for a generic, reusable way to shuffle a std::vector in C++. This is how I currently do it, but I think it's not very efficient because it needs an intermediate array and it needs to know the item type (DeckCard in this example):

srand(time(NULL));

cards_.clear();

while (temp.size() > 0) {
	int idx = rand() % temp.size();
	DeckCard* card = temp[idx];
	cards_.push_back(card);
	temp.erase(temp.begin() + idx);
}

C++ Solutions


Solution 1 - C++

From C++11 onwards, you should prefer:

#include <algorithm>
#include <random>

auto rng = std::default_random_engine {};
std::shuffle(std::begin(cards_), std::end(cards_), rng);

Live example on Coliru

Make sure to reuse the same instance of rng throughout multiple calls to std::shuffle if you intend to generate different permutations every time!

Moreover, if you want your program to create different sequences of shuffles each time it is run, you can seed the constructor of the random engine with the output of std::random_device:

auto rd = std::random_device {}; 
auto rng = std::default_random_engine { rd() };
std::shuffle(std::begin(cards_), std::end(cards_), rng);

For C++98 you may use:

#include <algorithm>

std::random_shuffle(cards_.begin(), cards_.end());

Solution 2 - C++

http://www.cplusplus.com/reference/algorithm/shuffle/

// shuffle algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::shuffle
#include <vector>       // std::vector
#include <random>       // std::default_random_engine
#include <chrono>       // std::chrono::system_clock

int main () 
{
    // obtain a time-based seed:
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine e(seed);

    while(true)
    {
      std::vector<int> foo{1,2,3,4,5};

      std::shuffle(foo.begin(), foo.end(), e);
    
      std::cout << "shuffled elements:";
      for (int& x: foo) std::cout << ' ' << x;
      std::cout << '\n';
    }

    return 0;
}

Solution 3 - C++

In addition to what @Cicada said, you should probably seed first,

srand(unsigned(time(NULL)));
std::random_shuffle(cards_.begin(), cards_.end());

Per @FredLarson's comment:

> the source of randomness for this version of random_shuffle() is > implementation defined, so it may not use rand() at all. Then srand() > would have no effect.

So YMMV.

Solution 4 - C++

It can be even simpler, seeding can be avoided entirely:

#include <algorithm>
#include <random>

// Given some container `container`...
std::shuffle(container.begin(), container.end(), std::random_device());

This will produce a new shuffle each time the program is run. I also like this approach due to the simplicity of the code.

This works because all we need for std::shuffle is a UniformRandomBitGenerator, whose requirements std::random_device meets.

Note: if shuffling repeatedly, it may be better to store the random_device in a local variable:

std::random_device rd;
std::shuffle(container.begin(), container.end(), rd);

Solution 5 - C++

If you are using boost you could use this class (debug_mode is set to false, if you want that the randomizing could be predictable beetween execution you have to set it to true):

#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <algorithm> // std::random_shuffle

using namespace std;
using namespace boost;

class Randomizer {
private:
	static const bool debug_mode = false;
	random::mt19937 rng_;

	// The private constructor so that the user can not directly instantiate
	Randomizer() {
		if(debug_mode==true){
			this->rng_ = random::mt19937();
		}else{
			this->rng_ = random::mt19937(current_time_nanoseconds());
		}
	};

	int current_time_nanoseconds(){
		struct timespec tm;
		clock_gettime(CLOCK_REALTIME, &tm);
		return tm.tv_nsec;
	}

	// C++ 03
	// ========
	// Dont forget to declare these two. You want to make sure they
	// are unacceptable otherwise you may accidentally get copies of
	// your singleton appearing.
	Randomizer(Randomizer const&);     // Don't Implement
	void operator=(Randomizer const&); // Don't implement

public:
	static Randomizer& get_instance(){
		// The only instance of the class is created at the first call get_instance ()
		// and will be destroyed only when the program exits
		static Randomizer instance;
		return instance;
	}

	template<typename RandomAccessIterator>
	void random_shuffle(RandomAccessIterator first, RandomAccessIterator last){
		boost::variate_generator<boost::mt19937&, boost::uniform_int<> > random_number_shuffler(rng_, boost::uniform_int<>());
		std::random_shuffle(first, last, random_number_shuffler);
	}

	int rand(unsigned int floor, unsigned int ceil){
		random::uniform_int_distribution<> rand_ = random::uniform_int_distribution<> (floor,ceil);
		return (rand_(rng_));
	}
};

Than you can test it with this code:

#include "Randomizer.h"
#include <iostream>
using namespace std;

int main (int argc, char* argv[]) {
	vector<int> v;
	v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);
	v.push_back(6);v.push_back(7);v.push_back(8);v.push_back(9);v.push_back(10);

	Randomizer::get_instance().random_shuffle(v.begin(), v.end());
	for(unsigned int i=0; i<v.size(); i++){
		cout << v[i] << ", ";
	}
	return 0;
}

Solution 6 - C++

Depending of the standard you have to follow (C++11/C++14/C++17) this "cppreference" page provides pretty good examples: https://en.cppreference.com/w/cpp/algorithm/random_shuffle.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionlaurentView Question on Stackoverflow
Solution 1 - C++user703016View Answer on Stackoverflow
Solution 2 - C++Mehmet FideView Answer on Stackoverflow
Solution 3 - C++user195488View Answer on Stackoverflow
Solution 4 - C++Apollys supports MonicaView Answer on Stackoverflow
Solution 5 - C++madxView Answer on Stackoverflow
Solution 6 - C++OcezoView Answer on Stackoverflow