std :: vector를 섞는 방법?


97

std::vectorC ++에서 셔플하는 일반적인 재사용 가능한 방법을 찾고 있습니다. 이것이 내가 현재하는 방법이지만 중간 배열이 필요하고 항목 유형을 알아야하기 때문에 매우 효율적이지 않다고 생각합니다 (이 예에서는 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);
}

아니. fisher-yates를 찾아보십시오 ....
Mitch Wheat

3
사용하지 마십시오. rand()더 나은 RNG API를 사용할 수 있습니다 (Boost.Random 또는 0x <random>).
Cat Plus Plus

답변:


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.
Alexandre C.

19
+1-이것은 프로그램을 실행할 때마다 동일한 결과를 생성 할 수 있습니다. std::random_shuffle이것이 문제인 경우 추가 인수로 사용자 지정 난수 생성기 (외부 소스에서 시드 할 수 있음)를 추가 할 수 있습니다 .
Mankarse 2011-08-03

4
@ Gob00st : 프로그램의 모든 인스턴스에 대해 동일한 결과를 생성합니다 random_shuffle. 이 동작은 정상이며 의도 된 것입니다.
user703016

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

4
@ ParkYoung-Bae 감사 합니다 . 방금 . SO 답변에 정보가 포함되어 있지 않으면 Google 검색 결과의 상단에 있기 때문에 정말 불편합니다.
Tomáš Zato-Monica 복원

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 에서 복사 한 나쁜 예 입니다. 셔플을 다시 호출하는 방법은 무엇입니까?
miracle173

@ miracle173 예제 개선
Mehmet Fide

2
왜 그냥 사용하는 대신 시드에 대해 시스템 시계를 이상하게 사용 std::random_device합니까?
Chuck Walbourn

6

@Cicada가 말한 것 외에도 먼저 씨를 뿌려야합니다.

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

@FredLarson의 의견에 따라 :

이 버전의 random_shuffle ()에 대한 임의성의 소스는 구현이 정의되어 있으므로 rand ()를 전혀 사용하지 않을 수 있습니다. 그러면 srand ()는 효과가 없습니다.

그래서 YMMV.


11
실제로이 버전의 임의성 소스 random_shuffle()는 구현이 정의되어 있으므로 전혀 사용하지 않을 수 있습니다 rand(). 그러면 srand()효과가 없습니다. 나는 전에 그것을 만났다.
Fred Larson

@Fred : 감사합니다 Fred. 그것을 몰랐습니다. 나는 항상 srand를 사용하는 데 익숙해졌습니다.

6
이 답변은 틀 렸기 때문에 삭제해야 할 것입니다. 더 나쁜 것은 정확하고 일부 구현에서는 정확하지만 전부는 아니므로이 조언을 매우 위험하게 만듭니다.
Thomas Bonini

2
@Fred가 위에서 설명했듯이 random_shuffle난수를 생성 하는 데 사용하는 것은 구현 정의입니다. 이것은 당신의 구현에서 rand()(그리고 따라서 srand ()가 작동 함) 사용한다는 것을 의미하지만, 내에서는 완전히 다른 것을 사용할 수 있습니다. 즉, 프로그램을 실행할 때마다 srand로 구현해도 동일한 결과를 얻을 수 있습니다.
Thomas Bonini

2
@Code : 논의했듯이 모든 구현에서 작동하지 않습니다. 자신의 번호 생성을 제공 할 수 있다는 사실은 답변에 언급되지 않았으며 어떤 경우에도이 토론과 관련이 없습니다. 나는 우리가 빙빙 돌고있는 것처럼 느낀다 : S
Thomas Bonini

2

당신이 사용하는 경우 부스트 (당신은이 클래스를 사용할 수 있습니다 debug_mode로 설정을 false하면 랜덤 당신이 그것을로 설정해야합니다 예측 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입니까?
Chuck Walbourn

1

더 간단 할 수 있으며 시드를 완전히 피할 수 있습니다.

#include <algorithm>
#include <random>

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

프로그램이 실행될 때마다 새로운 셔플이 생성됩니다. 나는 또한 코드의 단순성 때문에이 접근 방식을 좋아합니다.

이는 우리가 필요로하는 모든 것이 요구 사항을 충족 std::shuffle하는 이므로 작동합니다 .UniformRandomBitGeneratorstd::random_device

참고 : 반복적으로 셔플 random_device하는 경우 로컬 변수에 를 저장하는 것이 좋습니다 .

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

2
이것은 8 년 전에 받아 들여진 답변에 아직 포함되지 않았던 무엇을 추가합니까?
ChrisMM

1
당신이해야 할 일은 답을 읽고 알아 내기위한 것뿐입니다 ... 위에서 명확하게 설명되지 않은 것은 더 이상 말할 것도 없습니다.
Apollys 모니카 지원

1
허용 대답은 이미 셔플을 사용 및 사용에 말한다 random_device...
ChrisMM

1
이전에 받아 들여진 답변이 더 심층적 일 수 있습니다. 그러나 이것은별로 신경 쓰지 않고 그러한 간단한 질문을 검색 할 때 기대할 수있는 단선 포인트 앤 샷 대답입니다. 한
Ichthyo

2
이것은 잘못된 것 입니다. random_device는 PRNG를 시드하기 위해 한 번만 호출되도록 설계되었으며 , 반복해서 호출되지 않도록 설계되었습니다 (기본 엔트로피를 빠르게 소모하여 최적 이하의 생성 체계로 전환 할 수 있음)
LF

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.