How to Randomize a Vector

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());

How to randomize a vector

Yes.

sample(V)

From ?sample:

For ‘sample’ the default for ‘size’ is the number of items
inferred from the first argument, so that ‘sample(x)’ generates a
random permutation of the elements of ‘x’ (or ‘1:x’).

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

How to shuffle from a vector in R?

This is a very rough solution, but it should answer your question.

# initialization of parameters
V = c(8, 15, 16, 7, 3)
m = 3
n_iter = 10
mat <- matrix(0, nrow = n_iter, ncol = length(V))

# creation of matrix
set.seed(42)
for (j in 1:n_iter)
mat[j,] <- replace(V, sample(5, 2), 0)

Output

 #       [,1] [,2] [,3] [,4] [,5]
# [1,] 8 15 0 0 3
# [2,] 8 0 16 0 3
# [3,] 8 15 0 7 0
# [4,] 0 15 16 7 0
# [5,] 8 15 0 7 0
# [6,] 0 15 16 0 3
# [7,] 8 15 16 0 0
# [8,] 8 0 16 0 3
# [9,] 8 15 0 0 3
# [10,] 8 0 16 7 0

Shuffle Vector in R, But Identical Elements Should Have Minimum Distance

So basically we need to conditionally sample one element from the x vector that have not been selected in the min.dist-1 runs. Using purrr's reduce we can achieve this:

min.dist <- 2
reduce(integer(length(x)-1), ~c(.x, sample(x[!x %in% tail(.x, min.dist)], 1)), .init=sample(x,1))
[1] "A" "E" "D" "B" "A" "D" "E" "C" "D" "A" "C" "E" "B" "A" "E"

Bundled in a function

shuffle <- function(x, min.dist=2){
stopifnot(min.dist < length(unique(x)))
reduce(integer(length(x)-1), ~c(.x, sample(x[!x %in% tail(.x, min.dist)], 1)), .init=sample(x,1))
}
> shuffle(x, 3)
[1] "A" "C" "B" "D" "E" "A" "B" "C" "E" "D" "A" "B" "C" "E" "A"
> shuffle(x, 3)
[1] "A" "B" "D" "E" "C" "A" "B" "D" "E" "C" "A" "D" "E" "C" "A"
> shuffle(x, 4)
[1] "C" "E" "D" "A" "B" "C" "E" "D" "A" "B" "C" "E" "D" "A" "B"
> shuffle(x, 4)
[1] "A" "B" "D" "E" "C" "A" "B" "D" "E" "C" "A" "B" "D" "E" "C"
> shuffle(x, 2)
[1] "E" "A" "D" "E" "B" "D" "A" "E" "C" "D" "A" "E" "C" "A" "B"
> shuffle(x, 2)
[1] "B" "A" "D" "C" "B" "A" "E" "B" "A" "E" "B" "C" "D" "A" "E"

after @27ϕ9 comment:

shuffle <- function(x, min.dist=2){
stopifnot(min.dist < length(unique(x)))
reduce(integer(length(x)-1), ~ c(.x, sample(x[!x %in% tail(.x, min.dist) &( x %in% names(t <- table(x[x%in%.x]) > table(.x))[t] | !x %in% .x)], 1)), .init=sample(x,1))
}
> table(shuffle(rep(LETTERS[1:5], 3),2))

A B C D E
3 3 3 3 3
> table(shuffle(rep(LETTERS[1:5], 3),2))
Error in sample.int(length(x), size, replace, prob) :
invalid first argument

UPDATE

After some trial and error, looking at the fact that not always you're gonna have enough elements to space out the min.dist I came up with a solution this code is the most explained from the ones above :

shuffle <- function(x, min.dist=2){
stopifnot(min.dist < length(unique(x)))
reduce(integer(length(x)-1), function(.x, ...){
# whether the value is in the tail of the aggregated vector
in.tail <- x %in% tail(.x, min.dist)
# whether a value still hasn't reached the max frequency
freq.got <- x %in% names(t<-table(x[x%in%.x]) > table(.x))[t]
# whether a value isn't in the aggregated vector
yet <- !x %in% .x
# the if is there basically to account for the cases when we don't have enough vars to space out the vectors
c(.x, if(any((!in.tail & freq.got) | yet )) sample(x[(!in.tail & freq.got) | yet ], 1) else x[which(freq.got)[1]] )
}, .init=sample(x,1))
}

now running the table(shuffle(rep(LETTERS[1:5], 3),2)) will always return 3 for all vars and we can say with some certainty that in the vector the variables are spaced with a minimum distance of 2. the only way to guarantee that no elements are duplicated is by using min.dist=length(unique(x))-1 otherwise there will be instances where at maximum r < min.dist elements are not min.dist distanced from their last occurrences, and if such elements exist they're going to be in the length(x) + 1 - 1:min.dist subset of the resulting vector.

Just to be completely certain using a loop to check whether tail of the output vector has unique values: (remove the print statement I used it just for demonstration purposes)

shuffler <- function(x, min.dist=2){
while(!length(unique(print(tail(l<-shuffle(x, min.dist=min.dist), min.dist+1))))==min.dist+1){}
l
}

table(print(shuffler(rep(LETTERS[1:5], 3),2)))
[1] "A" "B" "C" "E" "B" "C" "D" "A" "C" "D" "A" "E" "B" "D" "E"

A B C D E
3 3 3 3 3

table(print(shuffler(rep(LETTERS[1:5], 3),2)))
[1] "D" "C" "C"
[1] "C" "C" "E"
[1] "C" "A" "C"
[1] "D" "B" "D"
[1] "B" "E" "D"
[1] "C" "A" "E" "D" "A" "B" "C" "E" "A" "B" "D" "C" "B" "E" "D"

A B C D E
3 3 3 3 3

Update:

shuffler <- function(x, min.dist=2){
while(any(unlist(lapply(unique(tl<-tail(l<-shuffle(x, min.dist=min.dist), 2*min.dist)), function(x) diff(which(tl==x))<=min.dist)))){}
l
}

this new version does a rigorous test on whether the elements in the tail of the vector are min.distanced, the previous version works for min.dist=2, however this new version does better testing.

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;
}

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 used std::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 calls std::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()};


Related Topics



Leave a reply



Submit