Whats the difference between os.urandom() and random?

You can read up on the distinction of cryptographically secure RNG in this fantastic answer over at Crypto.SE.

The main distinction between random and the system RNG like urandom is one of use cases. random implements deterministic PRNGs. There are scenarios where you want exactly those. For instance when you have an algorithm with a random element which you want to test, and you need those tests to be repeatable. In that case you want a deterministic PRNG which you can seed.

urandom on the other hand cannot be seeded and draws its source of entropy from many unpredictable sources, making it more random.

True random is something else yet and you'd need a physical source of randomness like something that measures atomic decay; that is truly random in the physical sense, but usually overkill for most applications.


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()

So whats the difference between os.urandom() and random?

Random itself is predicable. That means that given the same seed the sequence of numbers generated by random is the same. Take a look at this question for a better explanation. This question also illustrates than random isn't really random.

This is generally the case for most programming languages - the generation of random numbers is not truly random. You can use these numbers when cryptographic security is not a concern or if you want the same pattern of numbers to be generated.

Is one closer to a true random than the other?

Not sure how to answer this question because truly random numbers cannot be generated. Take a look at this article or this question for more information.

Since random generates a repeatable pattern I would say that os.urandom() is certainly more "random"

Would the secure random be overkill in non-cryptographic instances?

I wrote the following functions and there doesn't appear to be a huge time difference. However, if you don't need cryptographically secure numbers it doesn't really make sense to use os.urandom(). Again it comes down to the use case, do you want a repeatable pattern, how "random" do you want your numbers, etc?

import time
import os
import random


def generate_random_numbers(x): 
  start = time.time()
  random_numbers = []
  for _ in range(x):
    random_numbers.append(random.randrange(1,10,1))
  end = time.time()
  print(end - start)


def generate_secure_randoms(x):
  start = time.time()
  random_numbers = []
  for _ in range(x):
    random_numbers.append(os.urandom(1))
  end = time.time()
  print(end - start)


generate_random_numbers(10000)
generate_secure_randoms(10000)

Results:

0.016040563583374023
0.013456106185913086

Are there any other random modules in python?

Python 3.6 introduces the new secrets module