如何为扑克游戏生成2,598,960手牌(有效方式)
使用选择方法 nPk(即 nPk = n!/(nk)!*k!)计算 52 副牌中的扑克手总数(即每手五张牌),即 2,598,960 手可能。我想做的是生成所有可能的情况(即 2,598,960 手)。我想到的唯一方法是随机生成一个手并将其添加到一个向量中。每次添加手牌时,我都会检查它是否已经存在,如果存在,则继续生成随机手牌。这当然会奏效,但我正在为此寻找更快、更优雅的方法。一手牌的顺序并不重要。这是一个最小的工作示例,可以节省您的时间。
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
using namespace std;
const int SUIT_MAX(4);
const int RANK_MAX(13);
const int HAND_SZ(5);
const std::string SUIT[SUIT_MAX] = {"S", "H", "D", "C"};
const std::string RANK[RANK_MAX] = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
class Card
{
public:
Card(const string &suit, const string &rank) { m_card = rank + suit; }
std::string print() const { return m_card; }
private:
string m_card;
};
class Hand
{
public:
Hand(const vector<Card>& cards)
{
m_hand.push_back(cards);
}
void printHand()
{
for (int i(0); i < m_hand.size(); ++i)
for (int j(0); j < m_hand[i].size(); ++j)
cout << m_hand[i][j].print() << " ";
cout << endl;
}
private:
vector<vector<Card>> m_hand;
};
int main()
{
srand((int) time(0));
Card c1(SUIT[0], RANK[3]);
vector<Card> handVec;
for(int i(0); i < HAND_SZ; ++i){
int suit = (rand() % (SUIT_MAX));
int rank = (rand() % (RANK_MAX));
Card c(SUIT[suit], RANK[rank]);
handVec.push_back(c);
}
Hand h1(handVec);
h1.printHand();
return 0;
}
回答
更好的方法是给所有卡片一个数字索引,然后编写一个递归函数来迭代所有现有的组合,而不是猜测和检查。假设您有一副有 5 张牌的牌,而手上只有 3 张,这将是您的结果。
1,2,3
1,2,4
1,2,5
1,3,4
1,3,5
1,4,5
2,3,4
2,3,5
2,4,5
3,4,5
下一张牌总是比前一张牌高,你只需增加最后一行直到你到达最高的牌,然后你增加它之前的牌。
请注意,在上面的示例中,第一张牌只到highest_card-2,第二张牌到highest_card-1。从而第二张牌开始,lowest_card+1第三手牌开始于lowest_card+2
更新
这是代码
std::vector<std::vector<int>> all_options;
int factorial(int x)
{
int total = 1;
for(int i = 1; i <= x; i++)
total *= i;
return total;
}
int combination(int total, int x)
{
return factorial(total) / (factorial(total-x) * factorial(x));
}
void _recursive_combination(int min, int max, int current_depth, int max_depth, std::vector<int> &previous_values)
{
for(int i = min; i <= max; ++i) {
previous_values.push_back(i);
if( current_depth == max_depth )
all_options.push_back(previous_values);
else
_recursive_combination(i + 1, max + 1, current_depth + 1, max_depth, previous_values);
previous_values.pop_back();
}
}
// depth starts at 1, not 0.
void initialize_combinations(int min, int max, int depth)
{
// Be carefull! 52! is too big.
// int total_combinations = combination(max - min + 1, depth) Doesn't work as 52! is too high.
// all_options.reserve(total_combinations);
std::vector<int> buffer;
buffer.reserve(depth);
_recursive_combination(min, max - depth + 1, 1, depth, buffer);
}
void print_combinations()
{
for(auto &row : all_options){
for(auto col : row)
std::cout << " " << col;
std::cout << "n";
}
}
int main()
{
int cards_per_hand = 5;
auto start = std::chrono::system_clock::now();
initialize_combinations(1, 52, cards_per_hand);
std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start);
// print_combinations(); // Be carefull, this will make you wait for an eternity.
std::cout << "nnSize: " << std::size(all_options);
std::cout << "nTime: " << duration;
std::cout << std::endl;
return 0;
}
在我的 PC 上,调试模式下大约需要 6.7 秒,发布模式下大约需要 0.2 秒。您可以通过预订来减半2.598.960。您必须对其进行硬编码,因为组合计算会导致数字溢出。