如何随机播放std :: vector?


97

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

Answers:


200

从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年

4
@ Gob00st:它将为程序的每个实例(而不是对的每个调用)生成相同的结果random_shuffle。此行为是正常的和预期的。
user703016 2011年

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

4
@ ParkYoung-Bae谢谢,我才发现。当SO答案不包含包含信息时,这真的很不方便,因为它们位于Google搜索结果的顶部。
托马什Zato -恢复莫妮卡

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示例得到了改进
穆罕默德·菲德

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。


10
实际上,此版本的随机性来源random_shuffle()是由实现定义的,因此可能根本不使用rand()。那就srand()没有效果了。我以前遇到过。
Fred Larson

@Fred:谢谢弗雷德。不知道。我一直都习惯使用srand。

6
您可能应该删除此答案,因为它是错误的,甚至更糟的是,它看起来是正确的,并且在某些实现中确实是正确的,但不是全部,因此使此建议非常危险。
Thomas Bonini 2011年

2
正如上面@Fred所解释的,random_shuffle用于生成随机数的是实现定义。这意味着它在您的实现上使用rand()(因此srand()起作用),但在我的实现上可以使用完全不同的东西,这意味着即使在每次运行srand的情况下,在我的实现上,我也会得到相同的结果。
Thomas Bonini 2011年

2
@Code:就像我们讨论的那样,它并不是在所有实现中都起作用。您的答案中没有提到您可以提供自己的数字生成的事实,并且无论如何都与本讨论无关。我觉得我们要
绕圈转

2

如果您使用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_));
    }
};

比您可以使用以下代码进行测试:

#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是因为我们所需的只是一个UniformRandomBitGenerator,它的要求std::random_device可以满足。

注意:如果反复进行改组,最好将它们存储random_device在局部变量中:

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

2
这是8年前尚未被接受的答案的一部分吗?
ChrisMM '19

1
您所要做的就是阅读答案以找出答案...上面没有很清楚地解释了很多话。
Apollys

1
接受的答案已经使用随机播放,并说要使用random_device...
ChrisMM

1
先前接受的答案可能会更深入。但是,这正是我在毫不费力地搜索这样一个简单问题时所期望的单行即击答案。+1
Ichthyo,

2
这是错的random_device只能被调用一次以播种PRNG,而不能被一遍又一遍地调用(这可能会很快耗尽潜在的熵并导致其转换为次优生成方案)
LF

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.