6 min read
How random is your random?

Computers are deterministic machines. At their core, they always follow fixed rules, and given the same inputs and the same starting state, they will always produce the exact same outputs. Yet so much of computing depends on randomness: simulations, cryptography, games, A/B tests, and more.

There are many ways to generate this randomness, and not all “random” is created equal.

Level 1 - Pseudorandom Numbers (PRNGs)

A pseudorandom number generator (PRNG) produces a deterministic sequence that looks random, given an initial seed. The classic textbook example is the linear congruential generator (LCG):

Xn+1=(aXn+c) mod mX_{n+1} = (aX_n + c)\ mod\ m

  • X0X_0 is the seed
  • aa = multiplier, cc = increment, mm = modulus

This recursive function generates a sequence of numbers which is seemingly random, “seemingly” being the key word.

Below is a simple Python implementation which generates 10 random numbers.

import time

def lcg(seed):
    x = seed
    a = 1664525 
    c = 1013904223
    m = 2**32
    while True:
        x = (a * x + c) % m
        yield x

seed = int(time.time())
generator = lcg(seed)
random_numbers = [next(generator) for i in range(10)]

Here we use the current time as a seed, so the sequence is entirely repeatable if you know (or can guess) when it was generated.

There are other flaws here: in higher dimensions, consecutive outputs lie on a small number of parallel hyperplanes. Spectral tests can expose such artifacts (this happened to IBM’s early computers which used a variation of LCG called RANDU).

Modern standard libraries prefer stronger algorithms. Python’s random module uses a Mersenne Twister sequence which has a huge period, equal distribution in up to 623 dimensions, and is blazingly fast. Even then, it is not recommended for serious applications such as cryptography. After observing enough outputs, it’s possible to clone the internal state of the RNG and predict the next sequence of numbers. This GitHub repo does some clever untwisting to do that.

Level 2 - True Random Numbers

To move from “looks random” to “can’t be predicted,” we rely on physical processes that are effectively unpredictable. Think of flipping a coin: it’s random because it’s impossible in practice to track all the variables that determine the outcome. This unpredictable information is also known as entropy.

All modern operating systems have some way of collecting entropy from the underlying hardware. For example:

  • Apple’s Secure Enclave uses multiple ring oscillators, essentially inverters in a loop whose jitter (driven by thermal noise) is sampled.
  • Cloudflare uses Lavarand: an array of lava lamps with cameras pointed at them. The unpredictable movements of the lamps are used as a source of entropy for SSL.
  • Hard disks (HDDs) have small, unpredictable variations in how long it takes for the head to reach a sector. Disk drive seek times are a classic source of entropy in Linux.

OSes pool entropy from multiple sources and pass it through a Cryptographically Secure PRNG (CSPRNG)—a more robust version of Level 1. Any application that needs randomness can then draw bytes from the CSPRNG.

On UNIX-like OSes, you can read from /dev/urandom to get a stream of random bytes. For example:

> head -c 16 /dev/urandom | xxd -p    # get 16 random bytes, hex-encoded
df88ea1bc1b4c491bf4a87581e9e3b1c

In Python, the os or secrets module provides a similar function. If you need unpredictability for security, you should always use those. In practice, “true” RNGs (via the OS + CSPRNG) suffice for 99.9999% of applications.

However, we call this “truly random” only because of our ignorance: it’s practically impossible to track all variables, just like in the coin-toss case. The randomness is epistemic. If, hypothetically, we could simulate every detail—air currents, initial conditions—we could predict the outcome. If you want something that’s indeterministic at the level of the underlying theory, you look to quantum mechanics.

Level 3 - True True Random Numbers

In standard quantum theory, some measurement outcomes are intrinsically probabilistic: even with perfect knowledge of the system, you can only predict probabilities. Some interpretations (e.g., Many-Worlds, Bohmian mechanics) keep underlying determinism, but they still don’t let you predict which single outcome you’ll actually observe. For engineering purposes, a quantum measurement gives you a bit you couldn’t have known in advance.

Practical quantum random number generators (QRNGs) measure a quantum process and digitize the result, then clean it up:

  • Beam-splitter bit: Send single photons at a half-silvered mirror. Detect “transmit” vs. “reflect” as 0/1.
  • Photon arrival times: Measure when individual photons arrive—tiny, inherently random variations.
  • Vacuum fluctuations / phase noise: Use optical setups (e.g., homodyne detection) to sample quantum noise in the electromagnetic field.

Raw quantum signals aren’t perfectly balanced, so devices run unbiasing and extraction and continuous health tests to remove bias and catch failures. The cleaned bits typically seed or reseed a CSPRNG, so you can get arbitrarily many high-quality bytes at high speed.

You can buy USB dongles and chips that output quantum-seeded randomness. Some systems use Bell-test setups to certify randomness even if you don’t fully trust the device internals. A few organizations publish live quantum randomness streams you can sample as an extra entropy source. For example, the Python snippet below fetches 16 random 8-bit integers from the ANU QRNG service:

# This is just for demo. You shouldn't get random numbers
# from a public API for OBVIOUS REASONS
import requests

url = "https://qrng.anu.edu.au/API/jsonI.php?length=16&type=uint8"
response = requests.get(url)
random_numbers = response.json()

QRNGs are rare in practice. Consider them when you need independently auditable, physics-based entropy… or when you simply want the coolest dice possible.