कैसे एक std :: वेक्टर फेरबदल करने के लिए?


97

मैं std::vectorसी + + में फेरबदल करने के लिए एक सामान्य, पुन: प्रयोज्य तरीके की तलाश कर रहा हूं । यह वर्तमान में मैं इसे कैसे करता हूं, लेकिन मुझे लगता है कि यह बहुत कुशल नहीं है क्योंकि इसे एक मध्यवर्ती सरणी की आवश्यकता है और इसे आइटम प्रकार (इस उदाहरण में डेककार्ड) को जानना होगा:

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

नहीं। फिशर-येट्स देखो ....
मिच गेहूं

3
उपयोग न करने का प्रयास करें rand(), बेहतर RNG API उपलब्ध हैं (Boost.Random या 0x <random>)।
कैट प्लस प्लस

जवाबों:


201

C ++ 11 के बाद से, आपको पसंद करना चाहिए:

#include <algorithm>
#include <random>

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

Live example on Coliru

यदि आप हर बार अलग-अलग क्रमपरिवर्तन उत्पन्न करने का इरादा रखते हैं, तो rngकई कॉल में एक ही उदाहरण का पुन: उपयोग करना सुनिश्चित करें std::shuffle!

इसके अलावा, यदि आप चाहते हैं कि आपका कार्यक्रम हर बार चलने वाले फेरबदल के अलग-अलग अनुक्रम बनाए, तो आप आउटपुट के साथ यादृच्छिक इंजन के निर्माता को बीज दे सकते हैं 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 के लिए आप उपयोग कर सकते हैं:

#include <algorithm>

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

8
आप तीसरे तर्क के रूप में एक कस्टम यादृच्छिक संख्या जनरेटर को भी प्लग कर सकते हैं std::random_shuffle
एलेक्जेंडर सी।

19
+1 - ध्यान दें कि यह प्रोग्राम के प्रत्येक रन के समान परिणाम उत्पन्न कर सकता है। आप एक अतिरिक्त यादृच्छिक तर्क जनरेटर जोड़ सकते हैं (जो एक बाहरी स्रोत से सीड किया जा सकता है) एक अतिरिक्त तर्क के रूप में std::random_shuffleअगर यह एक समस्या है।
मकरसे

4
@ Gob00st: यह प्रोग्राम के हर उदाहरण के लिए एक ही परिणाम उत्पन्न करेगा, न कि हर कॉल random_shuffle। यह व्यवहार सामान्य और इरादा है।
user703016

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

4
@ ParkYoung-Bae धन्यवाद, मुझे अभी पता चला है । यह वास्तव में असुविधाजनक है जब SO उत्तरों में जानकारी शामिल नहीं होती है क्योंकि उनके Google खोज परिणामों के शीर्ष पर होते हैं।
टॉम ज़ातो -

10

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 से कॉपी किया गया एक खराब उदाहरण । आप फेरबदल की एक और कॉल कैसे करते हैं?
चमत्कार 173

@ चमत्कार 173 उदाहरण में सुधार हुआ
मेहमत

2
सिर्फ इस्तेमाल करने के बजाय बीज के लिए सिस्टम घड़ी का अजीब उपयोग क्यों std::random_device?
चक वालबोरन

6

@Cicada ने जो कहा उसके अलावा, आपको संभवतः पहले बीज बोना चाहिए,

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

प्रति @ FredLarson की टिप्पणी:

random_shuffle () के इस संस्करण के लिए यादृच्छिकता का स्रोत कार्यान्वयन परिभाषित है, इसलिए यह रैंड () का उपयोग बिल्कुल नहीं कर सकता है। फिर सरंड () का कोई असर नहीं होगा।

तो YMMV।


10
दरअसल, इस संस्करण के लिए यादृच्छिकता का स्रोत random_shuffle()कार्यान्वयन परिभाषित है, इसलिए यह बिल्कुल भी उपयोग नहीं कर सकता है rand()। फिर srand()कोई असर नहीं होगा। मैं इससे पहले भाग चुका हूं।
फ्रेड लार्सन

@ फ़्रेड: धन्यवाद फ्रेड। वह नहीं जानता था। मुझे हर समय सरंड का उपयोग करने की आदत है।

6
आपको शायद इस उत्तर को हटा देना चाहिए क्योंकि यह गलत है और - इससे भी बदतर - यह सही प्रतीत होता है और वास्तव में कुछ कार्यान्वयनों में सही है, लेकिन सभी नहीं, इस सलाह को बहुत खतरनाक बनाते हैं।
थॉमस बोनी 16

2
जैसा कि @Fred ने बताया कि random_shuffleरैंडम नंबर जेनरेट करने के लिए क्या उपयोग होता है, क्या इसे परिभाषित किया गया है। इसका मतलब यह है कि आपके कार्यान्वयन पर यह rand()(और इसलिए srand () काम करता है) लेकिन मेरा यह पूरी तरह से कुछ अलग उपयोग कर सकता है, जिसका अर्थ है कि मेरे कार्यान्वयन पर भी जब भी मैं हर बार कार्यक्रम चलाता हूं तो मुझे वही परिणाम मिलेंगे।
थॉमस बोनी

2
@ कोड: जैसे हमने चर्चा की कि यह सभी कार्यान्वयनों में काम नहीं करता है। तथ्य यह है कि आप अपनी खुद की नंबर पीढ़ी की आपूर्ति कर सकते हैं आपके उत्तर में उल्लेख नहीं किया गया है और किसी भी मामले में इस चर्चा से संबंधित नहीं है। मुझे लगता है कि हम हलकों में जा रहे हैं: एस
थॉमस बोनिनी

2

आप उपयोग कर रहे हैं को बढ़ावा देने आप (इस वर्ग इस्तेमाल कर सकते हैं debug_modeके लिए निर्धारित है falseअगर आप चाहते हैं कि randomizing उम्मीद के मुताबिक beetween निष्पादन आप इसे करने के लिए सेट करने के लिए है हो सकता है, 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_));
    }
};

आप इस कोड के साथ इसका परीक्षण कर सकते हैं:

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

आप जनरेटर की जगह बीज का उपयोग क्यों कर रहे हैं std::random_device?
चक वाल्बर्न

1

यह और भी सरल हो सकता है, बोने से पूरी तरह बचा जा सकता है:

#include <algorithm>
#include <random>

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

यह हर बार जब कार्यक्रम चलाया जाता है तो एक नया फेरबदल होगा। कोड की सादगी के कारण मुझे भी यह तरीका पसंद है।

यह काम करता है क्योंकि हम सभी की जरूरत std::shuffleहै एक है UniformRandomBitGenerator, जिनकी आवश्यकताओं को std::random_deviceपूरा करता है।

नोट: यदि बार-बार फेरबदल किया जाता है, तो random_deviceस्थानीय चर में स्टोर करना बेहतर हो सकता है :

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

2
यह क्या जोड़ता है जो पहले से ही 8 साल पहले से स्वीकृत जवाब का हिस्सा नहीं था?
ChrisMM

1
आपको बस इतना पता करने के लिए उत्तर पढ़ना है ... यह कहा जाना बहुत अधिक नहीं है कि पहले से ही ऊपर स्पष्ट रूप से नहीं बताया गया है।
अपोलिस

1
स्वीकृत उत्तर पहले से ही फेरबदल का उपयोग करता है, और उपयोग करने के लिए कहता है random_device...
ChrisMM

1
पुराना स्वीकृत उत्तर अधिक गहराई से हो सकता है। हालांकि, यह बिल्कुल एकल-बिंदु बिंदु-और-शॉट का उत्तर है, मैं उम्मीद करूंगा जब बिना किसी विशेषण के इस तरह के एक सरल प्रश्न के लिए googling हो। +1
इचथ्यो

2
यह गलत हैrandom_deviceयह केवल एक बार PRNGs बोने के लिए डिज़ाइन किया गया है, जिसे बार-बार नहीं बुलाया जाना चाहिए (जो अंतर्निहित एन्ट्रापी को जल्दी से समाप्त कर सकता है और इसे उप-इष्टतम पीढ़ी योजना पर स्विच कर सकता है)
LF

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.