【问题标题】:add random element of a set to a list and remove it from original set将集合的随机元素添加到列表中并将其从原始集合中删除
【发布时间】:2019-11-10 17:50:05
【问题描述】:

我有一组字符串,并想将该组的随机元素添加到列表中(例如,将扑克牌分发给不同的玩家)

我尝试了以下方法:

std::set<std::string> remaining_cards;
std::vector<std::set<std::string>> player_cards;

int random_number;
for (int i = 0; i < number_of_players; ++i) 
{
    random_number = 2;  // for simplicity let's assume the random number is always 2
    auto it = remaining_cards.cbegin();
    std::advance(it, random_number);
    player_cards.emplace_back(remaining_cards.cbegin(), it);  // get one element
    remaining_cards.erase(it);  // remove distributed card from deck
}

为什么即使我用erase从牌组的最后一行中删除了分发到最后一行的那张牌,但所有玩家都得到了同一张牌?

【问题讨论】:

  • remaining_cards.erase(it) 你只从系列中移除一张牌。在player_cards.emplace_back(remaining_cards.cbegin(), it); 中,您通过调用构造函数采用两个迭代器来创建set - 它们从remaining_cards 集合中定义范围。所以你的玩家肯定有重复的牌,这真的是你想做的吗?
  • iow 使用 emplace_back 可以将多张牌添加到玩家的手牌中,但使用擦除只会从牌组中移除一张牌。
  • 我明白了,所以emplace_back 需要两个迭代器。如果我只想添加一个元素怎么办?

标签: c++ list set emplace


【解决方案1】:

我不确定您为什么使用std::set... 可能是因为它会自动对卡片进行分类。我会使用 std::vector 并手动排序 (std::sort)。

我必须在您尝试在代码中执行的操作中填写一些空白,因为您没有发布一个有效的完整示例。

我建议使用封装并移动绘制的卡片而不是在删除之前复制。例如

#include <random>
#include <string>
#include <set>
#include <vector>
#include <numeric>

class Random {
private:
    std::default_random_engine generator{};
public:
    int operator()(int maxValue) { return generator() % maxValue; }
};

class Card {
private:
    std::string name{};
public:
    Card() : name{} {}
    Card& operator= (int i) {
        name = std::to_string(i);
        return *this;
    }
    friend bool operator<(Card const& lhs, Card const& rhs) {
        return lhs.name < rhs.name; // or some other sorting.
    }
};

class Player {
private: 
    std::set<Card> cards{};
public:
    void AddCard(Card&& card) noexcept {
        cards.emplace(std::move(card));
    }
};

int main() {
    //fill the deck
    std::vector<Card> deck(42); // instead of remaining cards... why would this be a set?
    std::iota(std::begin(deck), std::end(deck), 1); // fill 1 to 42

    Random random{};

    std::vector<Player> players(4);
    while (deck.size() > 0) { // distribute the whole deck.
        for (auto& player : players) {
            if (deck.empty()) break; // if cards in desck is not equaly dividable between players
            auto randIdx = random(deck.size());
            player.AddCard(std::move(deck[randIdx]));  // move one card
            deck.erase(std::next(std::begin(deck), randIdx));  // and remove it from deck
        }
    }
}

【讨论】:

  • 我发现根据我提供的信息移动它是有意义的。但是,在有助于估计给定卡片获胜概率的蒙特卡洛模拟中,我将不得不再次重新创建牌组(因为我想多次模拟该过程)。我想知道是否有一种更快的方法可以将卡片从一副牌随机分配到一组矢量多次,而无需重新创建原始牌组。也许指针可以通过指向随机卡片来提供帮助?但不清楚在这种情况下如何避免重复。
  • @Nickpick 移动实际上比复制更有效。但你问的是一个对 cme​​ts 来说太大的新问题。我在 Codingame 上做了一些有竞争力的编程,我确实倾向于使用指向游戏中的牌的指针。如果您正在做蒙特卡罗,请注意std::set 之类的内容。因为他们确实通过排序来存储元素,这很慢。你需要性能,所以使用最简单的容器。
猜你喜欢
  • 2019-07-07
  • 2012-10-14
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2015-08-13
相关资源
最近更新 更多