Std :: vector nasıl karıştırılır?


99

Bir std::vectorC ++ 'da karıştırmak için genel, yeniden kullanılabilir bir yol arıyorum . Şu anda bunu böyle yapıyorum, ancak bunun çok verimli olmadığını düşünüyorum çünkü bir ara diziye ihtiyaç duyuyor ve öğe türünü bilmesi gerekiyor (bu örnekte DeckCard):

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

Hayır. fisher-yates'e bak ....
Mitch Wheat

4
rand()Kullanmamaya çalışın , daha iyi RNG API'leri mevcuttur (Boost.Random veya 0x <random>).
Cat Plus Plus

Yanıtlar:


208

C ++ 11'den itibaren aşağıdakileri tercih etmelisiniz:

#include <algorithm>
#include <random>

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

Live example on Coliru

Her seferinde farklı permütasyonlar oluşturmayı düşünüyorsanız, rngbirden çok çağrı boyunca aynı örneğini yeniden kullandığınızdan emin olun std::shuffle!

Dahası, programınızın her çalıştırıldığında farklı karıştırma dizileri oluşturmasını istiyorsanız, rastgele motorun yapıcısını şu çıktıyla tohumlayabilirsiniz std::random_device:

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

C ++ 98 için şunları kullanabilirsiniz:

#include <algorithm>

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

8
Üçüncü argüman olarak özel bir rastgele sayı üreteci de takabilirsiniz std::random_shuffle.
Alexandre C.

20
+1 - Bunun programın her çalıştırılmasında aynı sonucu verebileceğini unutmayın. Bunun std::random_shufflebir sorun olup olmadığına dair ek bir argüman olarak özel bir rastgele sayı üreteci (harici bir kaynaktan başlatılabilen) ekleyebilirsiniz .
Mankarse

4
@ Gob00st: her çağrı için değil, programın her örneği için aynı sonucu üretecektir random_shuffle. Bu davranış normaldir ve amaçlanmıştır.
user703016

3
@ TomášZato#include <algorithm>
user703016

4
@ ParkYoung-Bae Teşekkürler, yeni öğrendim . SO cevaplarının, google arama sonuçlarının en üstünde yer aldığı için bilgi içermemesi gerçekten sakıncalıdır.
Tomáš Zato - Monica'yı eski durumuna getir

11

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

cplusplus.com/reference/algorithm/shuffle adresinden kopyalanmış kötü bir örnek . Nasıl başka bir karışıklık çağrısı yaparsınız?
miracle173

@ miracle173 örneği iyileştirildi
Mehmet Fide

2
Sistem saatini neden sadece kullanmak yerine bir tohum için garip kullanıyorsunuz std::random_device?
Chuck Walbourn

6

@Cicada'nın söylediklerine ek olarak, muhtemelen önce tohumlamalısınız,

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

@ FredLarson'ın yorumu:

random_shuffle () 'ın bu sürümü için rastgelelik kaynağı uygulama tanımlıdır, bu nedenle rand () kullanmayabilir. Bu durumda srand () 'nin hiçbir etkisi olmaz.

Yani YMMV.


11
Aslında, random_shuffle()uygulamasının bu sürümü için rastgeleliğin kaynağı uygulama tanımlıdır, bu nedenle hiç kullanılmayabilir rand(). O srand()zaman hiçbir etkisi olmazdı. Bununla daha önce karşılaştım.
Fred Larson

@Fred: Teşekkürler Fred. Bunu bilmiyordum. Ben her zaman srand kullanmaya alışkınım.

6
Muhtemelen bu yanıtı silmelisiniz çünkü yanlış ve - daha da kötüsü - bazı uygulamalarda doğru görünüyor ve gerçekten doğru, ancak hepsinde değil, bu tavsiyeyi çok tehlikeli hale getiriyor.
Thomas Bonini

2
@ Fred'in yukarıda açıkladığı gibi random_shufflerasgele sayı üretmek için kullanılan, uygulama tanımlıdır. Bu, uygulamanızda kullandığı rand()(ve dolayısıyla srand () çalıştığı) anlamına gelir, ancak benimkinde tamamen farklı bir şey kullanabilir, yani srand ile bile benim uygulamamda programı her çalıştırdığımda aynı sonuçları alacağım anlamına gelir.
Thomas Bonini

2
@Kod: tartıştığımız gibi tüm uygulamalarda işe yaramıyor. Kendi numara üretiminizi sağlayabileceğiniz gerçeği cevabınızda belirtilmemiştir ve her halükarda bu tartışma ile ilgisi yoktur. Dairelerin içine
giriyormuşuz

2

Eğer kullanıyorsanız destek (eğer bu sınıfı kullanabilirsiniz debug_modeayarlandığında falseEğer rasgele yapma bunu ayarlı zorunda öngörülebilir beetween yürütme olabileceğini istiyorsanız 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_));
    }
};

Bu kodla test edebileceğinizden:

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

Jeneratörü tohumlamak yerine neden zamanı kullanıyorsunuz std::random_device?
Chuck Walbourn

1

Daha da basit olabilir, tohumlamadan tamamen kaçınılabilir:

#include <algorithm>
#include <random>

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

Bu, program her çalıştırıldığında yeni bir karıştırma üretecektir. Ben de kodun basitliğinden dolayı bu yaklaşımı seviyorum.

Biz gereken tüm Bunun nedeni çalışır std::shufflebir olduğunu UniformRandomBitGeneratorkimin gereksinimleri, std::random_devicekarşılamaktadır.

Not: tekrar tekrar karıştırılıyorsa, random_deviceyerel bir değişkende saklamak daha iyi olabilir :

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

2
Bu, 8 yıl önce kabul edilen cevabın bir parçası olmayan ne ekler?
ChrisMM

1
Tek yapmanız gereken, öğrenmek için cevabı okumak ... Yukarıda çok net bir şekilde açıklanmayan söylenecek çok şey yok.
Apollys,

1
Kabul edilen cevap zaten karışık kullanıyor ve şunu diyor random_device...
ChrisMM

1
Eski kabul edilen cevap daha derinlemesine olabilir. Ancak, bu kadar basit bir soru için Google'da çok fazla uzatmadan beklediğim tek satırlık nokta ve atış cevabı bu. +1
Ichthyo

2
Bu yanlış . PRNG'leri tohumlamak için random_deviceyalnızca bir kez çağrılacak, tekrar tekrar çağrılmayacak şekilde tasarlandı (bu, temeldeki entropiyi hızla tüketebilir ve optimalin altında bir üretim şemasına geçmesine neden olabilir)
LF

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.