Randomness in Programming
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
| Property | PRNG (Math.random, random) | CSPRNG (crypto, secrets) |
|---|---|---|
| Speed | Very fast | Slower (entropy mixing) |
| Predictability | Deterministic from seed | Unpredictable |
| State recovery | Possible from outputs | Computationationally infeasible |
| Period | Fixed (2¹⁹⁹³⁷ for MT) | No fixed period |
| Use for passwords | Never | Always |
| Use for simulations | Preferred | Overkill |
| Use for games | Preferred | Only for gambling |
Language API Quick Reference
| Language | PRNG | CSPRNG |
|---|---|---|
| JavaScript | Math.random() | crypto.getRandomValues() |
| Python | random.random() | secrets.token_hex() |
| Java | java.util.Random | java.security.SecureRandom |
| Go | math/rand | crypto/rand |
| Rust | rand::thread_rng() | rand::rngs::OsRng |
| PHP | rand() / 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 usecrypto.getRandomValues()orrandomBytes(). - Seeding a CSPRNG. You override the entropy source and defeat its purpose. Let the OS handle it.
- Truncating random values. Taking
Math.random() % 6for 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.