The Intractability of Certain Combinatorial Problems in Poker
By: Zynga Poker Engineering
Tags: Software Engineering, Mathematics, Zynga, Video Game Development, Poker

A Better Approach

The takeaway from all these examples is that people are not reliable when it comes to manually inspecting for randomness. And we already know that given a large enough problem space no computer exists that could generate let alone verify all elements of randomness. So as a software engineer, what can you do?

Math. Rather than try to observe randomness in the outcome, we can prove that the method we use to generate numbers is random.

One method that has been proven to produce random sequences of numbers given proper parameters is the pseudorandom Linear Congruential Generator. This is, for example, how Java’s java.util.Random class works. To generate a number, you take a predefined constant “a”, multiply it by the previously generated number, add a predefined constant “c”, and take that value modulo some predefined constant “m”.

Pseudorandom Linear Congruential Generator

Because a number is recursively defined, we need to have an initial number sub i. This value is known as the “seed”. It’s a number that you can generally either specify yourself (mostly useful for debugging) or have the algorithm implicitly provide. When implicitly provided by the algorithm, it is usually either time based (such as milliseconds since the computer started) or derived from environmental noise (such as measurements from hardware like cameras or thermometers). Some of the more extreme, more secure seeds are something unpredictable, such as the precise combination of radioactive isotopes monitored for a short period of time that have undergone radioactive decay. Such a generator can be found here.

The choices for the three constants are obviously very important as well. For example, if “a“ and “c” are both 0, you will always get 0. If “a” is 0 and c is 1, you’ll always get 1 mod “m”. I won’t go into how exact values for “a”, “c”, and “m” are chosen (you can find the values chosen for popular implementations here), but I will briefly talk about the importance of “m”. Because we are modding by “m”, the number we generate will never be higher than “m”. So, if “m” is 2⁶⁴, our random number will never be higher than 2⁶⁴. However, taking Java’s Random as an example, it actually throws out the lowest order 16 bits as their combinations are not actually evenly distributed. This leaves us with 48 usable bits, meaning that the maximum value we can generate once the low order 16 bits are thrown out is 2⁴⁸. This upper limit is known as the “Period” of the generator.

Because we’re generating whole numbers between 0 and 2⁴⁸, after at most 2⁴⁸ iterations we will be guaranteed to repeat a number that we’ve already generated. When that happens, all following numbers in their exact order are known with certainty by anyone who kept track of the numbers already generated, even without knowing “a”, “c”, “m”, or the seed. This takes us back to the problem we discussed earlier of a sequence being eventually repeated.

This is why after some number of iterations less than the period, best practice is to re-seed the generator. That is, restart the random number generator using a new seed. This is a way of ensuring that any pattern that exists in the Linear Congruential Generator will not persist through a re-seeding. Though it does not eliminate the possibility of a pattern emerging, it does make it less likely and in any case limits such a pattern’s length to the number of iterations between re-seeds (where otherwise there would be no limit to its length).