It's about time someone noded this.
Have you ever wondered what kind of PRNG your calculator uses? Your Borland compiler? Your blackjack game? No? Well, chances are they use the same kind of PRNG:
Linear congruential pseudorandom number generator
The linear congruential (also known as linear congruent) PRNG is probably one of the most commonly used in programs, and one of a family of pseudorandom techniques known as Monte Carlo Techniques. It isn't used in any cryptographic software because it isn't cryptographically secure. The reason it's used is because it's easy to set up, and can look random, but it is actually predictable and easy to have a crack at. It generates numbers in the range (0...Z-1).
The reason it's known as the 'linear congruential' method is because at the heart of it lies this formula (part of which is of the form ax + c, hence the name):
Xn+1 = (A*Xn + C) mod Z
Here's how you make your 'random' numbers. First, you need to pick a seed (X0), a multiplier (A), a modulus (Z) and a constant (C) to increment A*Xn. The modulus basically says what the range of the outputted list of numbers is; the output will be a list of numbers from 0 to Z-1. We can pick any value we like for X0, A, Z and C, as long as four conditions are satisfied:
- The increment C must be relatively prime to our modulus, Z.
- A-1 must be a multiple of every prime p that divides Z.
- A-1 must be a multiple of (a number) if Z is a multiple of (a number).
- X0, A, C and Z must all be greater than .
Then, keeping C, A and Z the same, we iterate
, to get values for X1
Now I've explained what the LCPRNG is and how it works, let's use an example. I want to have a series of pseudorandom numbers from 1 to 16 inclusive. I've decided to use a LCPRNG. My modulus Z is going to be 16. According to the rules above, C must be relatively prime to Z and C > 0. A good choice for C would then be 3. Now we need to pick the multiplier A and the seed X0. A-1 must be a multiple of every prime that divides into Z. Since only one prime number divides into Z, which is 2, A must be of the form 2n+1. In that case, A can be 5. The seed is independent and can be just about any integer (because the modulo operation makes it less than Z). In this case, let's use 9, just for convenience.
So, to sum up: X0=9, A=5, Z=16 and C=3. Now let's get cracking.
X1 = (5*9 + 3) mod 16 = 0
X2 = (5*0 + 3) mod 16 = 3
X3 = (5*3 + 3) mod 16 = 2
X4 = (5*2 + 3) mod 16 = 13
X5 = (5*13 + 3) mod 16 = 4
X6 = (5*4 + 3) mod 16 = 7
X7 = (5*7 + 3) mod 16 = 6
X8 = (5*6 + 3) mod 16 = 1
X9 = (5*1 + 3) mod 16 = 8
X10 = (5*8 + 3) mod 16 = 11
X11 = (5*11 + 3) mod 16 = 10
X12 = (5*10 + 3) mod 16 = 5
X13 = (5*5 + 3) mod 16 = 12
X14 = (5*12 + 3) mod 16 = 15
X15 = (5*15 + 3) mod 16 = 14
X16 = (5*9 + 3) mod 16 = 9
Do you see how it works? The previously generated number is used as a 'seed' for the next number.
So, taking, X1 to X16, we get the numbers from 0 to 15 in this order: 0, 3, 2, 13, 4, 7, 6, 1, 8, 11, 10, 5, 12, 15, 14, 9. But I wanted numbers from 1 to 16, so we add them all by one to get:
1, 4, 3, 14, 5, 8, 7, 2, 9, 12, 11, 6, 13, 16, 15, 8
And that's my sequence of numbers. However, there are hints that the sequence isn't random. Look at (X2, X3), (X6, X7), (X10, X11) and (X14, X15).
If you don't follow the four rules I defined above, what can happen is that you end up with a sequence of numbers which does not contain all of the integers from 0 to Z. You may instead get a repeating sequence with a period smaller than Z, or you may get some negative numbers in your output.
It's easy to see why many trivial programs use the LCPRNG to generate random numbers. It's very easy to implement, easy to generate numbers you want (especially as it's a trivial matter to convert the numbers to binary) and easy to follow. If you use a very high modulus (we're talking something like 232 or 264) and divide the output by it, you can get an impressive stream of fake random decimals. Here's a list of decimals from an LCPRNG I just cooked up myself.
The contents of this writeup are in the public domain.