C++ 如何洗牌 std::vector?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6926433/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:56:21  来源:igfitidea点击:

How to shuffle a std::vector?

c++shufflestdvector

提问by laurent

I am looking for a generic, reusable way to shuffle a std::vectorin C++. This is how I currently do it, but I think it's not very efficient because it needs an intermediate array and it needs to know the item type (DeckCard in this example):

我正在寻找一种通用的、可重用的方法来std::vector在 C++ 中对 a 进行洗牌。这就是我目前的做法,但我认为它不是很有效,因为它需要一个中间数组,并且需要知道项目类型(在本例中为 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);
}

回答by user703016

From C++11 onwards, you should prefer:

从 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

Live example on Coliru

Make sure to reuse the same instance of rngthroughout multiple calls to std::shuffleif you intend to generate different permutations every time!

如果您打算每次生成不同的排列,请确保rng在多次调用中重用相同的实例std::shuffle

Moreover, if you want your program to create different sequences of shuffles each time it is run, you can seed the constructor of the random engine with the output of std::random_device:

此外,如果您希望程序在每次运行时创建不同的 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);


For C++98 you may use:

对于 C++98,您可以使用:

#include <algorithm>

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

回答by Mehmet Fide

http://www.cplusplus.com/reference/algorithm/shuffle/

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

回答by Mehmet Fide

In addition to what @Cicada said, you should probably seed first,

除了@Cicada 所说的,你应该先播种,

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

Per @FredLarson's comment:

根据@FredLarson 的评论:

the source of randomness for this version of random_shuffle() is implementation defined, so it may not use rand() at all. Then srand() would have no effect.

此版本的 random_shuffle() 的随机性来源是实现定义的,因此它可能根本不使用 rand()。那么 srand() 将不起作用。

So YMMV.

所以YMMV。

回答by madx

If you are using boostyou could use this class (debug_modeis set to false, if you want that the randomizing could be predictable beetween execution you have to set it to true):

如果您正在使用boost,您可以使用此类(debug_mode设置为false,如果您希望随机化可以在执行之间进行预测,则必须将其设置为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_));
    }
};

Than you can test it with this 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;
}

回答by Apollys supports Monica

It can be even simpler, seeding can be avoided entirely:

甚至可以更简单,可以完全避免播种:

#include <algorithm>
#include <random>

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

This will produce a new shuffle each time the program is run. I also like this approach due to the simplicity of the code.

这将在每次程序运行时产生一个新的 shuffle。由于代码简单,我也喜欢这种方法。

This works because all we need for std::shuffleis a UniformRandomBitGenerator, whose requirements std::random_devicemeets.

这是有效的,因为我们只需要std::shuffle一个UniformRandomBitGenerator,其要求std::random_device满足。

Note: if shuffling repeatedly, it may be better to store the random_devicein a local variable:

注意:如果重复洗牌,最好将 存储random_device在局部变量中:

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

回答by Ocezo

Depending of the standard you have to follow (C++11/C++14/C++17) this "cppreference" page provides pretty good examples: https://en.cppreference.com/w/cpp/algorithm/random_shuffle.

根据您必须遵循的标准(C++11/C++14/C++17),这个“cppreference”页面提供了很好的例子:https: //en.cppreference.com/w/cpp/algorithm/ random_shuffle