如何打乱 std::vector 的顺序?

发布于 2024-11-27 19:59:10 字数 348 浏览 5 评论 0原文

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

I am looking for a generic, reusable way to shuffle a std::vector in 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):

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

北方的巷 2024-12-04 19:59:10

从 C++20 开始,我们有

#include <algorithm>
#include <random>

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

C++11 到 C++17,它稍微更详细:

#include <algorithm>
#include <random>

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

Coliru 上的实时示例

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

此外,如果您希望程序每次运行时创建不同的洗牌序列,您可以使用 std::random_device

auto rd = std::random_device {}; 
auto rng = std::default_random_engine { rd() };

如果您坚持使用 C++98,您可以使用:

#include <algorithm>

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

在新代码中避免使用此方法, 尽管: std::random_shuffle() 在 C++20 中被删除。

Since C++20, we have

#include <algorithm>
#include <random>

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

For C++11 to C++17, it's slightly more verbose:

#include <algorithm>
#include <random>

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

Live example on Coliru

Make sure to reuse the same instance of rng throughout multiple calls to std::shuffle if you intend to generate different permutations every time!

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:

auto rd = std::random_device {}; 
auto rng = std::default_random_engine { rd() };

If you're stuck with C++98, you may use:

#include <algorithm>

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

Avoid this for new code, though: std::random_shuffle() was removed in C++20.

执笏见 2024-12-04 19:59: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;
}

请请注意,如果继续对已经洗牌的向量进行洗牌,最终会得到不同的分布。因此,foo 在循环内定义为具有已知的初始状态。

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

Please note the fact that if you keep shuffling a vector already shuffled, you end up with a different distribution. Therefore foo is defined inside the loop to have a known initial state.

败给现实 2024-12-04 19:59:10

除了 @Cicada 所说的之外,您可能应该先播种,

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

根据 @FredLarson 的评论:

此版本的 random_shuffle() 的随机性来源是
定义了实现,因此它可能根本不使用 rand()。然后srand()
不会有任何影响。

所以YMMV。

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

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

Per @FredLarson's comment:

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.

So YMMV.

野の 2024-12-04 19:59:10

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

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

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.

This works because all we need for std::shuffle is a UniformRandomBitGenerator, whose requirements std::random_device meets.

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

std::random_device rd;
std::shuffle(container.begin(), container.end(), rd);
驱逐舰岛风号 2024-12-04 19:59:10

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

If you are using boost you could use this class (debug_mode is set to false, if you want that the randomizing could be predictable beetween execution you have to set it to 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文