web-dev-qa-db-fra.com

Comment mélanger un vecteur std ::?

Je cherche un moyen générique et réutilisable de mélanger un std::vector en C++. Voici comment je le fais actuellement, mais je pense que ce n'est pas très efficace car il a besoin d'un tableau intermédiaire et il a besoin de connaître le type d'élément (DeckCard dans cet exemple):

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);
}
82
laurent

A partir de C++ 11, vous devriez préférer:

#include <algorithm>
#include <random>

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

Live example on Colir

Assurez-vous de réutiliser la même instance de rng lors de plusieurs appels à std::shuffle si vous avez l’intention de générer des permutations différentes à chaque fois!

De plus, si vous voulez que votre programme crée différentes séquences de shuffles à chaque exécution, vous pouvez associer le constructeur du moteur aléatoire à la sortie de std::random_device .


Pour C++ 98, vous pouvez utiliser:

#include <algorithm>

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

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;
}
9
Mehmet Fide

En plus de ce que @Cicada a dit, vous devriez probablement commencer en premier,

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

Commentaire de Per @ FredLarson:

la source de caractère aléatoire pour cette version de random_shuffle () est définie par l'implémentation; elle ne peut donc pas utiliser Rand () du tout. Alors srand () n'aurait aucun effet.

Alors YMMV.

4
user195488

Si vous utilisez boost , vous pouvez utiliser cette classe (debug_mode est défini sur false, si vous souhaitez que la randomisation soit prévisible entre les exécutions, vous devez la définir sur 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_));
    }
};

Alors vous pouvez le tester avec ce 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;
}
1
madx