Every "random" button hides a choice: where does the unpredictability come from? Some systems measure real-world noise, others run a formula that only looks random. Knowing the difference explains why a lottery can be provably fair while a programmer's quick % 10 trick quietly favours some numbers.
True randomness vs pseudo-randomness
- True (hardware) randomness comes from physical noise — atmospheric static, radioactive decay, mouse jitter. It cannot be predicted from prior outputs.
- Pseudo-randomness (PRNG) is produced by a deterministic algorithm from a starting value called a seed. Given the same seed, it repeats the exact same sequence.
A PRNG is not a flaw. For simulations, games and most UI work it is fine, and it is fast. It becomes a problem only when unpredictability must be unguessable, such as in cryptography or a high-stakes draw.
Uniformity: what "fair" means here
A fair generator produces a uniform distribution: over the range 1–100, every value should have the same probability, 1 in 100. Fairness is not "looks varied" — a sequence like 3, 3, 7, 3 can be perfectly random, and a sequence that never repeats is often less random. People mistake clustering for bias because true randomness clusters more than intuition expects.
The modulo bias trap
A common shortcut maps a random integer onto a smaller range with the remainder operator. Suppose a generator returns a byte, 0–255, and you want 0–9 via value % 10. There are 256 possible inputs; dividing by 10 leaves 6 as a remainder. So the digits 0 through 5 each appear 26 times and 6 through 9 appear 25 times. Six values are slightly more likely — a hidden bias most people never notice.
| Target value | Times it occurs (of 256) | Probability |
|---|---|---|
| 0–5 | 26 | 10.156% |
| 6–9 | 25 | 9.766% |
The fix is rejection sampling: discard inputs in the uneven tail and redraw, or scale the range so the total is evenly divisible. Good libraries do this automatically; writing it by hand is where the bias creeps in.
Seeds and reproducibility
Because a PRNG is deterministic, its seed controls everything. Math.random() in JavaScript seeds itself once and is not suitable for security. A reproducible seed is a feature in testing — the same seed yields the same sequence, so a failing case can be replayed — but a liability if an attacker can guess the seed and predict "random" values.
Cryptographic random numbers
When unpredictability matters, use a cryptographically secure generator, such as crypto.getRandomValues() in the browser or the operating system's entropy pool. These are designed so that past outputs reveal nothing about future ones. For a public prize draw this is the honest choice, because no one — not even the organiser — can bias the result after the fact.
Common mistakes
- Using modulo on an uneven range, as above, which biases the low values.
- Re-seeding every call. Seeding from the clock in quick succession can repeat values instead of varying them.
- Trusting
Math.random()for security or for anything that must be unguessable. - Assuming a short run must "balance out". Each draw is independent; past results do not influence the next.
- Calling a visible shuffle fair without checking the underlying source.
What makes a draw believable
Fairness is two things: an unbiased source and a process anyone can inspect. Freeze the pool, publish the count, use a uniform generator, and record the result. A well-implemented random number generator removes the temptation to fudge — and the suspicion of it.
Generate integers or decimals in any range with our random number generator. For the surrounding process, see how to randomly pick a winner fairly, and for precision in your results see the percent error formula.