11

A deck of cards is 52. A hand is 5 cards from the 52 (cannot have a duplicate).

What is the least amount of bits to represent a 5 card hand and how?
A hand is NOT order dependent (KQ = QK). 64329 = 96432

Yes, can use 52 bits. That can represent a hand of any number of cards.

Given a hand is exactly 5 cards is there a way to represent it with less than 52 bits.

A single card can be represented with 6 bits = 64. So could just use 6 bits * 5 cards = 30 bits. But that would be order dependent. I could just sort and this should work. If that would not work please let me know.

Is there a way to get the key to 32 bits or under and not have to sort the 5 card tuple.

This is for poker simulations and sorting would be a lot of overhead compared to just generating the hand. If I have a dictionary with the relative value of each hand it is two simple lookups and a comparison to compare the value of two hands. If I have to sort the hands first that is large compared to two lookups and a comparison. In a simulation will compare millions. I will not get sorted hands from the simulation. The sort is not simple like 52 51 50 49 48 before 52 51 50 49 47. You can have straight flush quads ....

There are 2598960 possible 5 card hands. That is the number of rows. The key is the 5 cards. I would like to get a key that is 32 bits or under where the the cards do not need to be sorted first.

Cannot just order the list as many hands tie. Suit are spade, club, diamond, and heart. 7c 8c 2d 3d 4s = 7s 8s 2c 3c 4h. There is a large number of ties.

The next step is 64 bits and will take the hit of the sort rather than double the size of the key.

I tested and SortedSet<int> quickSort = new SortedSet<int>() { i, j, k, m, n }; doubles the time of the operation but I still may do it.

It gets more complex. I need to be able to represent a boat as twos over fives (22255). So sorting them breaks that. I know you are going to say but that is fast. Yes it is fast and trivial but I need as fast as possible.

C# for the accepted answer:

private int[] DeckXOR = new int[] {0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,
                                    0x00000080,0x00000100,0x00000200,0x00000400,0x00000800,0x00001000,0x00002000,
                                    0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000,0x00100000,
                                    0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x07fe0000,
                                    0x07c1f000,0x0639cc00,0x01b5aa00,0x056b5600,0x04ed6900,0x039ad500,0x0717c280,
                                    0x049b9240,0x00dd0cc0,0x06c823c0,0x07a3ef20,0x002a72e0,0x01191f10,0x02c55870,
                                    0x007bbe88,0x05f1b668,0x07a23418,0x0569d998,0x032ade38,0x03cde534,0x060c076a,
                                    0x04878b06,0x069b3c05,0x054089a3};
public void PokerProB()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    HashSet<int> cardsXOR = new HashSet<int>();
    int cardXOR;
    int counter = 0;
    for (int i = 51; i >= 4; i--)
    {
        for (int j = i - 1; j >= 3; j--)
        {
            for (int k = j - 1; k >= 2; k--)
            {
                for (int m = k - 1; m >= 1; m--)
                {
                    for (int n = m - 1; n >= 0; n--)
                    {
                        counter++;
                        cardXOR = DeckXOR[i] ^ DeckXOR[j] ^ DeckXOR[k] ^ DeckXOR[m] ^ DeckXOR[n];
                        if (!cardsXOR.Add(cardXOR))
                            Debug.WriteLine("problem");
                    }
                }
            }
        }
    }
    sw.Stop();
    Debug.WriteLine("Count {0} millisec {1} ", counter.ToString("N0"), sw.ElapsedMilliseconds.ToString("N0"));
    Debug.WriteLine("");
}
paparazzo
  • 431
  • 3
  • 13
  • 4
    Use a hand-coded sorting algorithm which works for sorting lists of length 5. This is probably faster than the library function you're currently using. – Yuval Filmus Apr 05 '17 at 20:47
  • 1
    I don't understand why you say "The sort is not simple". The sort is simple -- convert each card to a number from 1 to 52, so that the hand is represented by a list (of length 5) of cards. Sort that list. That is just the problem of sorting a list of 5 integers, which can be done very fast, as Yuval mentions. I suggest that you measure before assuming that it is too slow, but my guess is that sorting such a list will be very fast and might even be faster than a random-access memory read that doesn't hit in the cache. – D.W. Apr 05 '17 at 21:49
  • @dw Yes the sort is simple but what I am doing (millions of times) is simple. I tested and a sort doubles the time. – paparazzo Apr 06 '17 at 00:37
  • 1
    @Paparazzi No, Yuval is telling you to write your own sorting routine that is specifically tuned to sorting five numbers between 1 and 52. You tried using a library routine, which is slow because it is much more general than this and because the recursive nature of quicksort makes it very inefficient on short lists. – David Richerby Apr 06 '17 at 08:48
  • In practice, most items that are not <=16 bits might as well be 32 bits. So since you need at least 23 bits, any encoding which uses <=32 bits is probably viable. The trivial 6 bits per card * 5 card encoding works well enough. There's one caveat: a 23 bit array index is much better than a 32 bits array index. – MSalters Apr 06 '17 at 09:00
  • @DavidRicherby Not even close. I am not using a library. It is not sorting on the number directly. I insert the numbers pre sorted. – paparazzo Apr 06 '17 at 11:02
  • @MSalters I am asking for 32 bits or less that does not require the 5 numbers be sorted. – paparazzo Apr 06 '17 at 11:04
  • @Paparazzi: Just in case you hadn't realized it, the existing answer is far more complex than sorting 5 6-bit values using a dedicated sorting network. Sure, you may have millions of hands, but such a simple sort can be done tens of millions of times per second. Randomly generating the hand will also be slower than sorting it. – MSalters Apr 06 '17 at 11:14
  • @MSalters As stated in my question. A sort doubles the time for the method. Can you just accept the stated question? I am looking for a key of 5 numbers 0-51 where the numbers can be in any order and the key is still less than or equal 32 bits. I am not really sorting the numbers - I am feeding the the number pre sorted. – paparazzo Apr 06 '17 at 11:21
  • What do you mean by key? Do you want two hands which are the same up to order to map to the same key? – Yuval Filmus Apr 06 '17 at 15:05
  • @YuvalFilmus Please forget how I am using this key. I have 5 values 1-52. Value cannot repeat in the 5. Order does not matter. 22 15 50 3 7 must equal 3 7 22 15 50 . What is the smallest number to uniquely identify the 5 where they are not first sorted. For example I could use the first 52 primes and multiply but that is bigger than 2 power 52. – paparazzo Apr 06 '17 at 15:19

3 Answers3

13

If we have a set of size $n$, you can represent an element of the set using $\lceil \lg n \rceil$ bits. You say that there are 2598960 possible 5-card hands. That means that a 5-card hand can be represented using just $\lceil \lg 2598960 \rceil = 22$ bits. 22 bits is significantly shorter than 30 bits.

How does the representation work? There are various options, with different tradeoffs. I list two below.

Hard-coded dictionary

In this case, the number of possible 5-card hands is small enough that you could just have a hard-coded dictionary that lists all 2598960 hands, and you represent a hand by its index in the dictionary (represented in binary).

In other words, the dictionary can be a sorted list of hands. Each hand is the 5-tuple of the cards in the hand, in sorted order. You can look up a hand in the dictionary using binary search and find its corresponding index; and given an index, you can find the correspond hand. Or, you could store the dictionary as a hashmap that maps from the hand to its index. The index is an integer between 0 and 2598959, so it can be represented using 23 bits.

This approach will work and be very simple to program, but it is wasteful in space (size of the program executable).

Ranking/unranking

Alternatively, if you care, there are better methods. See, e.g., any of the following references:

The general topic is known as "ranking (and unranking) of combinations". These are a little more complex to implement and understand, but avoid the need to include a hard-coded dictionary in the program.

D.W.
  • 159,275
  • 20
  • 227
  • 470
  • I will update the question. Yes there are 2598960 hands. The dictionary will have that many rows. My problem is generation of the key. From 5 cards I need to generate a the key to perform the dictionary lookup. – paparazzo Apr 05 '17 at 20:36
  • @Paparazzi, if you use the dictionary approach, the hand is the key. In other words, the key is the 5-tuple of the cards in the hand (in sorted order). The dictionary can be stored as a hashtable using that as the key. If you don't like the memory costs of a dictionary, use the alternative approach: ranking/unranking. – D.W. Apr 05 '17 at 20:38
  • Yes I know I can get the key of 30 bits if I sort. I am wondering if there is a way to get the key 32 bits or under without sorting the 5 card tuple. I will look into rank and ranking. – paparazzo Apr 05 '17 at 20:41
  • I don't follow the ranking / unranking but thanks. I will try and figure it out. Also have the possibilities of ties. There are a lot of ties. – paparazzo Apr 05 '17 at 21:32
  • 1
    Also relevant: http://cs.stackexchange.com/questions/67664/prng-for-generating-numbers-with-n-set-bits-exactly – Pseudonym Apr 06 '17 at 02:27
10

Let $C$ be a $[52,25,11]$ code. The parity check matrix of $C$ is a $27 \times 52$ bit matrix such that the minimal number of columns whose XOR vanishes is $11$. Denote the $52$ columns by $A_1,\ldots,A_{52}$. We can identify each $A_i$ as a binary number of length $27$ bits. The promise is that the XOR of any $1$ to $10$ of these numbers is never $0$. Using this, you can encode your hand $a,b,c,d,e$ as $A_a \oplus A_b \oplus A_c \oplus A_d \oplus A_e$, where $\oplus$ is XOR. Indeed, clearly this doesn't depend on the order, and if two hands $H_1,H_2$ collide, then XORing the two hash values gives $10-2|H_1 \cap H_2|\leq 10$ numbers whose XOR is zero.

Bob Jenkins describes such a code in his site, and from that we can extract the array

0x00000001,0x00000002,0x00000004,0x00000008,0x00000010,0x00000020,0x00000040,
0x00000080,0x00000100,0x00000200,0x00000400,0x00000800,0x00001000,0x00002000,
0x00004000,0x00008000,0x00010000,0x00020000,0x00040000,0x00080000,0x00100000,
0x00200000,0x00400000,0x00800000,0x01000000,0x02000000,0x04000000,0x07fe0000,
0x07c1f000,0x0639cc00,0x01b5aa00,0x056b5600,0x04ed6900,0x039ad500,0x0717c280,
0x049b9240,0x00dd0cc0,0x06c823c0,0x07a3ef20,0x002a72e0,0x01191f10,0x02c55870,
0x007bbe88,0x05f1b668,0x07a23418,0x0569d998,0x032ade38,0x03cde534,0x060c076a,
0x04878b06,0x069b3c05,0x054089a3

Since the first 27 vectors are just the 27 numbers of Hamming weight 1, in order to check that this construction is correct it suffices to consider all $2^{52-27}-1 = 2^{25}-1$ possible non-trivial combinations of the last 25 numbers, checking that their XORs always have Hamming weight at least 10. For example, the very first number 0x07fe0000 has Hamming weight exactly 10.

Yuval Filmus
  • 276,994
  • 27
  • 311
  • 503
3

You can sort the five items and simultaneously check for duplicates without any comparisons on some processors: Assume a processor has a fast instruction which determines the position of the highest bit set, and a fast instruction calculating a number with only the n-th bit set.

Let bit (n) be the number with exactly the n-th bit set. Let highest_bit (x) be the number of the highest bit that is set in the number x, with an unspecified value if x = 0. Let x ^ y be the exclusive-or of x and y.

Given are five numbers a, b, c, d and e, each from 0 to 51, representing the five cards in the hand.

Let x = bit (a) ^ bit (b) ^ bit (c) ^ bit (d) ^ bit (e).

Let A = highest_bit (x), change x to x ^ bit (A).

Let B = highest_bit (x), change x to x ^ bit (B).

Let C = highest_bit (x), change x to x ^ bit (C).

Let D = highest_bit (x), change x to x ^ bit (D).

Let E = highest_bit (x).

If x = 0 then there were duplicates in the numbes a, b, c, d, and e. Otherwise, use A * bit (24) + B * bit (18) + C * bit (12) + D * bit (6) + E as the encoding of the hand, where A, B, C, D and E are defined as above. This encodes a hand as a 30-bit string, while doing the sorting in a very efficient way.

D.W.
  • 159,275
  • 20
  • 227
  • 470
gnasher729
  • 29,996
  • 34
  • 54