RANDU is a pseudorandom number generator from the early 1960s. It is a linear congruential generator, specifically a Lehmer random number generator.

# Formula

RANDU has a very short formula. Starting from the seed value, you can calculate the next value using this formula.

Vj+1 = Vj × 65539 mod 231

## LCG parameters

As RANDU is a linear congruential generator (LCG), it can be defined by its LCG parameters.

RANDU = LCG(mod=231, mult=216 + 3, inc=0)

# Recommendations

It is highly discouraged to use RANDU in any new projects. Its flaws have been known for a very long time and there are many alternatives that are superiour in every way.

Below is a (short) list of valid reasons to use RANDU.

1. Historical interest
2. Educational; specifically, how to recognise a bad RNG
3. Replicating old studies
4. Re-creating old software that needs to have identical behaviour

# Python implementation

Below is a simple implementation.

In :
```def RANDU(n):
n *= 65539
n %= 0x80000000
return n```
In :
```RANDU(1)
RANDU(RANDU(1))
RANDU(RANDU(RANDU(1)))```
Out :
`65539`
Out :
`393225`
Out :
`1769499`

Or using the LCG parameters from above.

In :
`RANDU = LCG(2**31, 2**16 + 3, 0)`
In :
```RANDU(1)
RANDU(RANDU(1))
RANDU(RANDU(RANDU(1)))```
Out :
`65539`
Out :
`393225`
Out :
`1769499`

# Analysis

In this section, I will try to visually inspect the output of RANDU in order to see how obvious its flaws are. We will inspect 1-D, 2-D, and 3-D outputs in that order.

Due to the known flaws of RANDU, 3-D outputs should completely give away how broken it is. But let’s go through all the steps and see if we can identify any signs earlier.

## 1-D analysis

In :
```vals = []

RANDU.seed_urandom()
for _ in range(2_000_000):
x = 0
x += RANDU.next_float()
x += RANDU.next_float()
x += RANDU.next_float()
vals.append(x - 1.5)

_ = plt.hist(vals, bins=200)```
Out:
` `
In :
```RES = 1024

RANDU.seed_urandom()
vals = [RANDU.next_float() for _ in range(RES * RES)]
vals = np.array(vals).reshape((RES, RES))

_ = plt.imshow(vals, cmap=cm)```
Out:
` `
In :
```RES = 1024

vals = [0 for _ in range(RES * RES)]

RANDU.seed_urandom()
for _ in range(10_000_000):
index = int(RANDU.next_float() * (RES * RES))
vals[index] += 1

vals = np.array(vals).reshape((RES, RES))

_ = plt.imshow(vals, cmap=cm)```
Out:
` `
In :
```RES = 512

vals = [0 for _ in range(RES * RES)]

RANDU.seed_urandom()
for _ in range(200):
for index in range(RES * RES):
vals[index] += RANDU.next_float()

vals = np.array(vals).reshape((RES, RES))

_ = plt.imshow(vals, cmap=cm)
_ = plt.colorbar()```
Out: