Handling very large numbers in Python
Question:
I’ve been considering fast poker hand evaluation in Python. It occurred to me that one way to speed the process up would be to represent all the card faces and suits as prime numbers and multiply them together to represent the hands. To whit:
class PokerCard:
faces = '23456789TJQKA'
suits = 'cdhs'
facePrimes = [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 53, 59, 61]
suitPrimes = [2, 3, 5, 7]
AND
def HashVal(self):
return PokerCard.facePrimes[self.cardFace] * PokerCard.suitPrimes[self.cardSuit]
This would give each hand a numeric value that, through modulo could tell me how many kings are in the hand or how many hearts. For example, any hand with five or more clubs in it would divide evenly by 2^5; any hand with four kings would divide evenly by 59^4, etc.
The problem is that a sevencard hand like AcAdAhAsKdKhKs has a hash value of approximately 62.7 quadrillion, which would take considerably more than 32 bits to represent internally. Is there a way to store such large numbers in Python that will allow me to perform arithmetic operations on it?
Answers:
Python supports arbitrarily large integers naturally:
In [1]: 59**3*61**4*2*3*5*7*3*5*7
Out[1]: 62702371781194950
In [2]: _ % 61**4
Out[2]: 0
Python supports a “bignum” integer type which can work with arbitrarily large numbers. In Python 2.5+, this type is called long
and is separate from the int
type, but the interpreter will automatically use whichever is more appropriate. In Python 3.0+, the int
type has been dropped completely.
That’s just an implementation detail, though — as long as you have version 2.5 or better, just perform standard math operations and any number which exceeds the boundaries of 32bit math will be automatically (and transparently) converted to a bignum.
You can find all the gory details in PEP 0237.
You could do this for the fun of it, but other than that it’s not a good idea. It would not speed up anything I can think of.

Getting the cards in a hand will be an integer factoring operation which is much more expensive than just accessing an array.

Adding cards would be multiplication, and removing cards division, both of large multiword numbers, which are more expensive operations than adding or removing elements from lists.

The actual numeric value of a hand will tell you nothing. You will need to factor the primes and follow the Poker rules to compare two hands. h1 < h2 for such hands means nothing.
Python supports arbitrarily large integers naturally:
Example:
>>> 10**1000
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
You could even get, for example, a huge integer value, fib(4000000).
But still it does not (for now) supports an arbitrarily large float!!
If you need one big, large, float then check up on the decimal Module. There are examples of use on this site: OverflowError: (34, 'Result too large')
Another reference: 9.4. decimal — Decimal fixed point and floating point arithmetic
You can even using the gmpy module if you need a speedup (which is likely to be of your interest): Handling big numbers in code
Another reference: gmpy (Google Code. Readonly)
The Python interpreter will handle it for you. You just have to do your operations (+, , *, /), and it will work as normal.
The int
value is unlimited.
Be careful when doing division. By default, the quotient is turned into float
, but float
does not support such large numbers. If you get an error message saying float
does not support such large numbers, then it means the quotient is too large to be stored in float
you’ll have to use floor division (//
).
It ignores any decimal that comes after the decimal point, this way, the result will be int
, so you can have a large number result.
>>>10//3
3
>>>10//4
2
Why would you ever want to do this? If you insist on storing the hand as a single encoded value instead of a dict or list use a bitstring instead of the product of primes. Multiplication and prime number factorization is slow.
Encode each card as a power of 2 (1, 2, 4, 8, 16, etc.). You can add a card with hand = hand. You can check for a card with if hand & card > 0.