The classic and infamous RANDU. This is a simplified version of a version courtesy of ptomato on github.
"...its very name RANDU is enough to bring dismay into the eyes and stomachs of many computer scientists!"
Donald E. Knuth, The Art of Computer Programming
import numpy as np
class RANDU:
"Pseudorandom number generator RANDU, a (flawed) linear congruential PRNG."
def __init__(self, seed=None):
self.seed(seed)
def seed(self, s):
self._state = hash(s) % 2147483648 # == 2^31
def _random(self):
self._state = (65539 * self._state) % 2147483648
return self._state / 2147483648
def random(self, size=None):
"Return one random number or an array of them, with `size` elements."
if size is None:
return self._random()
else:
r = self._random
return np.array([r() for i in range(size)])
RANDU().random(10)
%matplotlib notebook
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
plt.rcParams['figure.figsize'] = (10, 6)
plt.style.use('seaborn')
sns.set_context('talk', font_scale=1.4)
npts = 1_000
pts = RANDU().random(npts*3).reshape(npts, 3)
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], s=5);
plt.title("RANDU");
This is what similar output looks like for Numpy, which uses the Mersenne Twister PRNG, whose period is $2^{19937}-1 (\approx \cal{O}(10^{6001}))$ and which does not suffer from known serial correlations:
pts = np.random.uniform(size=(npts, 3))
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ax.scatter(pts[:, 0], pts[:, 1], pts[:, 2], s=5)
plt.title("Numpy Random");
Let's implement a simple linear congruential PRNG of the type
$$ X_{k+1} = a X_k \mod M $$
with particularly bad constants $a$, $M$ and seed $X_0$:
a = 3
M = 64
seed = 17
state = seed
numbers = []
for i in range(100):
state = (a * state) % M
numbers.append(state)
numbers = np.array(numbers)
print(numbers)
sorted(set(numbers))
len(_)
np.nonzero(numbers==1)
np.diff(_)
%matplotlib inline
randx = numbers[:-1]
randy = numbers[1:]
plt.scatter(randx, randy, facecolors='none', edgecolors='b', linewidth=2);