Bagaimana cara mengacak std :: vector?


97

Saya mencari cara umum yang dapat digunakan kembali untuk mengacak a std::vectordi C ++. Beginilah cara saya saat ini melakukannya, tetapi menurut saya itu tidak terlalu efisien karena memerlukan array perantara dan perlu mengetahui jenis itemnya (DeckCard dalam contoh ini):

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

nggak. lihat nelayan ....
Mitch Wheat

3
Cobalah untuk tidak menggunakan rand(), ada API RNG yang lebih baik yang tersedia (Boost.Random atau 0x <random>).
Cat Plus Plus

Jawaban:


202

Mulai C ++ 11 dan seterusnya, Anda harus memilih:

#include <algorithm>
#include <random>

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

Live example on Coliru

Pastikan untuk menggunakan kembali contoh yang sama di rngseluruh beberapa panggilan ke std::shufflejika Anda berniat untuk menghasilkan permutasi yang berbeda setiap saat!

Selain itu, jika Anda ingin program Anda membuat urutan pengacakan yang berbeda setiap kali dijalankan, Anda dapat memasukkan konstruktor mesin acak dengan keluaran std::random_device :

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

Untuk C ++ 98 Anda dapat menggunakan:

#include <algorithm>

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

8
Anda juga dapat memasang generator nomor acak khusus sebagai argumen ketiga std::random_shuffle.
Alexandre C.

19
+1 - Perhatikan bahwa ini mungkin menghasilkan hasil yang identik setiap menjalankan program. Anda dapat menambahkan generator nomor acak khusus (yang dapat di-seed dari sumber eksternal) sebagai argumen tambahan std::random_shufflejika ini menjadi masalah.
Mankarse

4
@ Gob00st: ini akan menghasilkan hasil yang sama untuk setiap program, tidak setiap panggilan ke random_shuffle. Perilaku ini normal dan disengaja.
pengguna703016

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

4
@ ParkYoung-Bae Terima kasih, saya baru tahu . Sungguh merepotkan bila jawaban SO tidak memuat info karena berada di urutan teratas hasil pencarian google.
Tomáš Zato - Kembalikan 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;
}

contoh buruk disalin dari cplusplus.com/reference/algorithm/shuffle . Bagaimana Anda membuat panggilan acak lainnya?
miracle173

@ miracle173 contoh ditingkatkan
Mehmet Fide

2
Mengapa penggunaan jam sistem yang aneh untuk benih daripada hanya menggunakan std::random_device?
Chuck Walbourn

6

Selain apa yang dikatakan @Cicada, Anda mungkin harus melakukan seed terlebih dahulu,

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

Per @ FredLarson komentar:

sumber keacakan untuk versi random_shuffle () ini ditentukan oleh implementasi, jadi mungkin tidak menggunakan rand () sama sekali. Maka srand () tidak akan berpengaruh.

Jadi YMMV.


11
Sebenarnya, sumber keacakan untuk versi ini random_shuffle()adalah implementasi yang ditentukan, jadi mungkin tidak digunakan rand()sama sekali. Maka tidak srand()akan berpengaruh. Saya pernah mengalami itu sebelumnya.
Fred Larson

@Fred: Terima kasih Fred. Tidak tahu itu. Saya sudah terbiasa menggunakan srand sepanjang waktu.

6
Anda mungkin harus menghapus jawaban ini karena salah dan - bahkan lebih buruk - tampak benar dan memang benar dalam beberapa penerapan, tetapi tidak semua, membuat saran ini sangat berbahaya.
Thomas Bonini

2
Seperti yang dijelaskan @Fred di atas, apa yang random_shuffledigunakan untuk menghasilkan bilangan acak adalah definisi implementasi. Ini berarti bahwa pada implementasi Anda ia menggunakan rand()(dan karenanya srand () berfungsi) tetapi pada saya dapat menggunakan sesuatu yang sama sekali berbeda, yang berarti bahwa pada implementasi saya bahkan dengan srand setiap kali saya menjalankan program saya akan mendapatkan hasil yang sama.
Thomas Bonini

2
@Code: seperti yang kita diskusikan, ini tidak berfungsi di semua implementasi. Fakta bahwa Anda dapat menyediakan pembuatan nomor Anda sendiri tidak disebutkan dalam jawaban Anda dan tidak terkait dengan diskusi ini dalam hal apa pun. Saya merasa seperti kita berputar
putar

2

Jika Anda menggunakan boost, Anda bisa menggunakan kelas ini ( debug_modedisetel ke false, jika Anda ingin pengacakan bisa diprediksi di antara eksekusi Anda harus menyetelnya ke 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_));
    }
};

Kemudian Anda dapat mengujinya dengan kode ini:

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

Mengapa Anda menggunakan waktu untuk menyemai generator daripada std::random_device?
Chuck Walbourn

1

Bahkan bisa lebih sederhana, penyemaian dapat dihindari sepenuhnya:

#include <algorithm>
#include <random>

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

Ini akan menghasilkan pengocokan baru setiap kali program dijalankan. Saya juga menyukai pendekatan ini karena kesederhanaan kodenya.

Ini berfungsi karena yang kita butuhkan std::shufflehanyalah a UniformRandomBitGenerator, yang persyaratannya std::random_devicememenuhi.

Catatan: jika mengocok berulang kali, mungkin lebih baik menyimpannya random_devicedi variabel lokal:

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

2
Apa yang ditambahkan ini yang belum menjadi bagian dari jawaban yang diterima dari 8 tahun yang lalu?
ChrisMM

1
Yang harus Anda lakukan hanyalah membaca jawabannya untuk mencari tahu ... Tidak banyak lagi yang bisa dikatakan yang belum dijelaskan dengan sangat jelas di atas.
Apollys mendukung Monica

1
Jawaban yang diterima sudah menggunakan shuffle, dan mengatakan untuk menggunakan random_device...
ChrisMM

1
Jawaban lama yang diterima mungkin lebih mendalam. Namun, ini justru merupakan jawaban titik-dan-tembakan satu baris yang saya harapkan saat mencari di Google untuk pertanyaan sederhana seperti itu tanpa banyak basa-basi. +1
Ichthyo

2
Ini salah . random_devicedirancang untuk dipanggil hanya sekali untuk menyemai PRNG, tidak untuk dipanggil berulang kali (yang dapat menghabiskan entropi yang mendasarinya dengan cepat dan menyebabkannya beralih ke skema pembangkitan yang kurang optimal)
LF

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.