0

The total number of poker hands (i.e. five cards for each hand) from a 52 deck is computed by using the choose method nPk (i.e. nPk = n!/(n-k)!*k!) which is 2,598,960 possible hands. What I would like to do is to generate all possible cases(i.e. 2,598,960 hands). The only way I think of is to randomly generate a hand and add it to a vector. Every time I add a hand, I check it if it already exists if so, keep generate random hand. This will of course work but I'm looking for faster and elegant approach for this. The order of cards in a hand is not important. This is a minimal working example to save your time.

#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;
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
CroCo
  • 5,531
  • 9
  • 56
  • 88
  • obviously you can't get better than O(2,598,960) although loop-unrolling may reduce the number of loops. The only way to help in this case is vectorize the generation with SIMD and run in multiple threads – phuclv May 30 '21 at 03:58
  • 1
    The random approach will take approximately forever - literally. Consider: after `2,598,959` (all but one) possibilities are generated, you only have one in 2,5 million chance to randomly generate the last remaining hand. That'd take a lot of attempts. – Igor Tandetnik May 30 '21 at 04:27
  • There are 52C6 poker hands, not 52P6. Two hands in different order are equivalent? – Yakk - Adam Nevraumont May 30 '21 at 04:36
  • @Yakk-AdamNevraumont you meant 5 not 6 right? – Patrick Roberts May 30 '21 at 04:44
  • @Yakk-AdamNevraumont if there is an explicit definition, no one cares about which letter you assign it. – CroCo May 30 '21 at 04:51
  • @croco It depends on if you are interested in communicating? We call permute P and choose C for a reason. It isn't a really tricky one either. Also, order of operations is a thing; do you mean n!/((n-k)!k!) or what you wrote? – Yakk - Adam Nevraumont May 30 '21 at 11:48
  • @patr Yes, fumble fingers on keyboard – Yakk - Adam Nevraumont May 30 '21 at 11:52

2 Answers2

5

A better method would be to give all cards a numeric index and then write a recursive function to iterate over all existing combinations instead of guessing and checking. Let's imagine you have a deck with 5 cards and only 3 in the hand, this will be your result.

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

The next card always starts one higher than the previous one and you just increase the last row until you get to the highest card, but then you increase the card before it.

Do notice that in the example above, the first card only goes until highest_card-2 and the second card goes to highest_card-1. Thereby the second card starts from lowest_card+1 and the third hand starts from lowest_card+2

UPDATE

This is the code

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 << "\n\nSize: " << std::size(all_options);
    std::cout << "\nTime: " << duration;
    std::cout << std::endl;

    return 0;
}

On my PC, it takes around 6.7s in debug mode and 0.2s in release mode. You can half it by reserving 2.598.960. You'll have to hard code it in though as the combination calculation will cause a numeric overflow.

JMRC
  • 1,473
  • 1
  • 17
  • 36
0

You can place the 52 cards in an array or vector randomly. Then with the following nested loop you generate all the combinations:

for (int i0 = 0; i0 != 48; i0++)
    for (int i1 = i0 + 1; i1 != 49; i1++)
        for (int i2 = i1 + 1; i2 != 50; i2++)
            for (int i3 = i2 + 1; i3 != 51; i3++)
                for (int i4 = i3 + 1; i4 != 52; i4++)
                    store(i0, i1, i2, i3, i4);

In the store function you store in another arrangement the 5 cards corresponding to the indices i0, i1, i2, i3 and i4.