Randomness in Programming

May 28, 20267 min read

What Is Randomness in Code?

Randomness in programming falls into two categories: pseudo-random and cryptographically secure. Most code you write uses pseudo-random number generators (PRNGs) — fast algorithms that produce sequences appearing random but are fully deterministic. Cryptographic randomness draws from hardware or OS entropy sources and is unpredictable even if you know the algorithm state.

Choosing the wrong type creates real problems. A PRNG in a password generator lets attackers predict credentials. A CSPRNG in a game simulation wastes CPU cycles for no security benefit.

How PRNGs Work

PRNGs start from a seed value and apply a mathematical formula to produce each output. Same seed, same sequence — every time. This determinism is useful for testing and reproducibility but disqualifies PRNGs from security contexts.

Linear Congruential Generator (LCG)

The oldest and simplest PRNG family. It computes each number as:

next = (a * current + c) mod m

LCG powers java.util.Random and was the default in C's rand(). It is fast but has poor statistical quality — low bits cycle in short periods, and the output forms a hyperplane pattern in multidimensional space.

Use it for: trivial demos, never for serious work.

Mersenne Twister (MT19937)

The most widely used PRNG. It has a period of 2¹⁹⁹³⁷−1 and passes most statistical tests. Python's random module, C++'s std::mt19937, and Ruby's default all use Mersenne Twister.

Drawbacks: its 2.5 KB state is large, it fails some modern statistical suites (TestU01 Big Crush), and it is not cryptographically secure. Observing 624 consecutive outputs lets you reconstruct the full state and predict all future values.

xoshiro / xorshift Family

Modern alternatives designed by Sebastiano Vigna. The xoshiro256** variant offers excellent statistical quality, small state (32 bytes), and high speed. It is the default PRNG in V8 (JavaScript), Rust's SmallRng, and many game engines.

// V8 uses xoshiro256** under the hood
Math.random() // → 0.4621848491...

Trade-off: like all PRNGs, xoshiro is predictable. If you need security, look elsewhere.

Cryptographically Secure PRNGs (CSPRNGs)

CSPRNGs pull entropy from the operating system — hardware noise, disk timings, keystroke intervals — and mix it into a pool. Even if an attacker observes previous outputs, they cannot predict future values.

Browser: crypto.getRandomValues

const array = new Uint32Array(4);
crypto.getRandomValues(array);
// → [2847936452, 1067498723, 3891746501, 723948102]

This is the only secure way to get random data in the browser. Math.random() is fast but predictable.

Node.js: crypto.randomBytes

const crypto = require('crypto');
const bytes = crypto.randomBytes(16);
// → <Buffer 7a 3f b2 ...>

Python: secrets Module

import secrets
token = secrets.token_hex(16)  # 32 hex chars
number = secrets.randbelow(100)  # 0–99 inclusive

Python 3.6+ provides secrets specifically for security use cases. The older random.SystemRandom calls os.urandom under the hood.

Java: SecureRandom

import java.security.SecureRandom;

SecureRandom sr = new SecureRandom();
byte[] bytes = new byte[16];
sr.nextBytes(bytes);

On Linux, SecureRandom reads from /dev/urandom by default. On Windows, it uses the CryptoAPI.

PRNG vs CSPRNG Comparison

PropertyPRNG (Math.random, random)CSPRNG (crypto, secrets)
SpeedVery fastSlower (entropy mixing)
PredictabilityDeterministic from seedUnpredictable
State recoveryPossible from outputsComputationationally infeasible
PeriodFixed (2¹⁹⁹³⁷ for MT)No fixed period
Use for passwordsNeverAlways
Use for simulationsPreferredOverkill
Use for gamesPreferredOnly for gambling

Language API Quick Reference

LanguagePRNGCSPRNG
JavaScriptMath.random()crypto.getRandomValues()
Pythonrandom.random()secrets.token_hex()
Javajava.util.Randomjava.security.SecureRandom
Gomath/randcrypto/rand
Rustrand::thread_rng()rand::rngs::OsRng
PHPrand() / mt_rand()random_bytes()

Seeding and Reproducibility

Controlling the seed gives you reproducible results — useful for tests, procedural content, and scientific experiments.

import random

random.seed(42)
print(random.random())  # 0.6394267984578837 — same every time

random.seed(42)
print(random.random())  # 0.6394267984578837 — exact repeat

Warning: reusing the same seed in production is a security risk. If an attacker guesses your seed, they can reproduce your entire random sequence. Only seed PRNGs deliberately in testing or creative coding.

In JavaScript, Math.random() does not expose a seed API. For seeded randomness, use a library like seedrandom or implement xoshiro directly.

Common Mistakes

  • Using Math.random() for tokens or passwords. It is predictable. Always use crypto.getRandomValues() or randomBytes().
  • Seeding a CSPRNG. You override the entropy source and defeat its purpose. Let the OS handle it.
  • Truncating random values. Taking Math.random() % 6 for dice introduces modulo bias — lower numbers appear slightly more often. Use rejection sampling instead.
  • Sharing a PRNG instance across threads. Mersenne Twister is not thread-safe. Concurrent calls corrupt the state.

Key Takeaways

  • PRNGs are deterministic sequences; CSPRNGs pull from OS entropy and are unpredictable.
  • Mersenne Twister and xoshiro dominate PRNG usage; choose xoshiro for speed and smaller state.
  • Use CSPRNGs (crypto, secrets, SecureRandom) for anything security-related — passwords, tokens, keys.
  • Seeding gives reproducibility in tests but is a vulnerability in production.
  • Modulo truncation creates bias — use proper range reduction techniques.

Try It Yourself

Generate cryptographically random values right in your browser. Use our Random Number Generator to produce secure random integers, our Password Generator to create strong passwords from CSPRNG entropy, or our UUID Generator to generate RFC-compliant unique identifiers — all free, no signup required.