random implements a pseudo random number generator. Knowing the algorithm and the parameters we can predict the generated sequence. At the end of the text is a possible implementation of a linear pseudo random generator in Python, that shows the generator can be a simple linear function.
os.urandom uses system entropy sources to have better random generation. Entropy sources are something that we cannot predict, like asynchronous events. For instance the frequency that we hit the keyboard keys cannot be predicted.
Interrupts from other devices can also be unpredictable.
In the random module there is a class: SystemRandom which uses os.urandom() to generate random numbers.
Actually, it cannot be proven if a given sequence is Random or NOT. Andrey Kolmogorov work this out extensively around 1960s.
One can think that a sequence is random when the rules to obtain the sequence, in any given language, are larger than the sequence itself. Take for instance the following sequence, which seems random:
264338327950288419716939937510
However we can represent it also as:
pi digits 21 to 50
Since we found a way to represent the sequence smaller than the sequence itself, the sequence is not random. We could even think of a more compact language to represent it, say:
pi[21,50]
or yet another.
But the smaller rules, in the most compact language (or the smaller algorithm, if you will), to generate the sequence may never be found, even if it exists.
This finding depends only on human intelligence which is not absolute.
There might be a definitive way to prove if a sequence is random, but we will only know it when someone finds it. Or maybe there is no way to prove if randomness even exists.
An implementation of a LCG (Linear congruent generator) in Python can be:
from datetime import datetime
class LCG:
defaultSeed = 0
defaultMultiplier = 1664525
defaultIncrement = 1013904223
defaultModulus = 0x100000000
def __init__(self, seed, a, c, m):
self._x0 = seed #seed
self._a = a #multiplier
self._c = c #increment
self._m = m #modulus
@classmethod
def lcg(cls, seed = None):
if seed is None: seed = cls.defaultSeed
return LCG(int(seed), cls.defaultMultiplier,
cls.defaultIncrement, cls.defaultModulus)
#pre: bound > 0
#returns: pseudo random integer in [0, bound[
def randint(self, bound):
self._x0 = (self._a * self._x0 + self._c) % self._m
return int(abs(self._x0 % bound))
#generate a sequence of 20 digits
rnd = LCG.lcg(datetime.now().timestamp()) #diff seed every time
for i in range(20):
print(rnd.randint(10), end='')
print()