How to shuffle a std::vector?
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());
Is there a better way of shuffling a vector in C++?
std::shuffle
in <algorithm>
should be what you're looking for. Here's an example of it in use.
#include <iostream>
#include <algorithm>
#include <vector>
#include <random>
int main () {
std::vector<int> myvector {1,2,3,4,5};
std::random_device rd;
std::default_random_engine gen(rd);
std::shuffle (myvector.begin(), myvector.end(), gen);
for (int& x: myvector) std::cout << ' ' << x;
std::cout << std::endl;
return 0;
}
This example is adapted to use vectors from a version found at http://www.cplusplus.com/reference/algorithm/shuffle/. Checkout the link for more info on std::shuffle
Fastest trivial way of shuffling a vector
There is a tradeoff to be made: Shuffling a a std::vector<size_t>
of indices can be expected to be cheaper than shuffling a std::vector<Position>
at the cost of an indirection when accessing the Position
s via shuffled indices. Actually the example on cppreference for std::iota
is doing something along that line (it uses iterators):
#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>
#include <random>
#include <vector>
int main()
{
std::list<int> l(10);
std::iota(l.begin(), l.end(), -4);
std::vector<std::list<int>::iterator> v(l.size());
std::iota(v.begin(), v.end(), l.begin());
std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()});
std::cout << "Contents of the list: ";
for(auto n: l) std::cout << n << ' ';
std::cout << '\n';
std::cout << "Contents of the list, shuffled: ";
for(auto i: v) std::cout << *i << ' ';
std::cout << '\n';
}
Instead of shuffling the list directly, a vector of iterators (with a std::vector
indices woud work as well) is shuffled and std::shuffle
only needs to swap iterators (/indices) rather than the more costly actual elements (in the example the "costly to swap" elements are just int
s).
For a std::list
I don't expect a big difference between iterating in order or iterating via shuffled iterators. On the other hand, for a std::vector
I do expect a significant impact. Hence, I would shuffle indices, then rearrange the vector once, and profile to see which performs better.
PS: As noted in comments, std::shuffle
is already the optimal algorithm to shuffle a range of elements. However, note that it swaps each element twice on average (possible implementation from cppreference):
for (diff_t i = n-1; i > 0; --i) {
using std::swap;
swap(first[i], first[D(g, param_t(0, i))]);
On the other hand, shuffling the indices and then rearranging the vector only requires to copy/move each element once (when additional memory is available).
C++ - Shuffling a vector of objects
deck.reserve(deckSize);
That doesn't add any elements to your vector, it only reserves space for them. So the size of your vector is zero, and when you shuffle it, nothing happens. The rest of your constructor, which sets (non-existent) elements of the vector, as well as your printDeck
function, are both undefined behavior. Use resize
instead.
deck.resize(deckSize);
In fact, it would be better to just remove the deckSize
variable altogether, and just use deck.size()
.
How can I shuffle a vector with unique pointers in a random sequence with std::shuffle?
std::shuffle() expects random access iterator as std::vector has. You have to replace std::list with std::vector.
clarification regarding std::random_shuffle to shuffle the vector
The function std::random_shuffle has two overloads. In short, that means you can call the same function with a different number of parameters.
- The version
std::random_shuffle(vector.begin(), vector.end());
calls an internal random generator which is defined by the implementation (compiler, operative system, standard libraries, ...). However, often it is internally usedstd::rand
.
From the documentation:
The random number generator is implementation-defined, but the function std::rand is often used.
- The version
std::random_shuffle(cards_vector.begin(), cards_vector.end(), myrandom);
uses an explicit random generator (that you define; that is,myrandom
) which internally callsstd::rand
.
The function std::rand (from C) generate a pseudo-random number. It means that once you have set the seed it will generate (every time) the same number sequence. In other words, the sequence is deterministic depending on the initial value (called seed).
In order to set the seed (the initial value) for the std::rand
you need to call the function std::srand
which accepts the seed as argument.
The statement srand(time(NULL));
is an old common trick to initialize the generator with a "random" (not actually) seed. Indeed, the function time(NULL)
returns the current time which is supposed to be different every time is called.
Therefore, every time your program starts you set the seed with a different number and the generator std::rand
will produce a difference sequence every time.
Please Note That: std::random_shuffle
is an old C++ function and it has been deprecated. You should indeed use its replacement, that is, std::shuffle
.
However, the solution above always return the same shuffle, even when the random seed is called in main. I am not sure whether that is intended or not.
The rationale behind this is the same as above.
In that example you are using another (different from std::rand
) pseudo-random number generator, that is std::default_random_engine{}
. This generator is defaulted initialized with a default seed.
If you want to generate different results for different application run, you need to initialize the generator with a seed which is meant to be different every time your application starts.
A more correct code would be:
#include <algorithm>
#include <chrono>
#include <random>
// ...
auto rng = std::default_random_engine{std::chrono::system_clock::now().time_since_epoch().count()};
Shuffle 2D vector by column
One way is to shuffle each inner vector with the same "random" engine
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
int main()
{
std::vector<std::vector<int>> v { {1,4,7}, {2,5,8}, {3,6,9} };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << v[i][j] << " ";
}
std::cout << std::endl;
}
std::cout << std::endl;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
for (auto &inner : v) {
shuffle(inner.begin(),inner.end(), std::default_random_engine(seed));
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << v[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
Related Topics
Using Custom Std::Set Comparator
Array[N] VS Array[10] - Initializing Array With Variable VS Numeric Literal
Initialization Order of Class Data Members
Two Different Values At the Same Memory Address
Compilers and Argument Order of Evaluation in C++
How to Properly Delete Nodes of Linked List in C++
How to Have Functions Inside Functions in C++
Difference Between Static and Dynamic Arrays in C++
How to Convert Vector to Array
Is There a Simple Way to Convert C++ Enum to String
Adding External Library into Qt Creator Project
How Does Virtual Inheritance Solve the "Diamond" (Multiple Inheritance) Ambiguity
Modern Way to Set Compiler Flags in Cross-Platform Cmake Project
What Are the Errors, Misconceptions or Bad Pieces of Advice Given by Cplusplus.Com