Published · · Last reviewed · · 6 min read
English · 한국어
How computers make random numbers
A computer is a machine that executes fixed instructions in a fixed order. Given the same input it always gives the same output. Can such a machine make anything random? The answer is that there are two ways to make it look as though it can, and the two have different characters. This page explains what pseudo-random and cryptographically secure numbers are, where bias creeps in, and then states plainly how this site's number generator and Lottery Lab actually pick numbers in code.
Pseudo-random numbers: chance from a formula
The oldest method is a recurrence. Fix a formula that computes the next value from the previous one; give it a starting value (the seed) and the formula produces everything after. A linear congruential generator, for example, is:
next = (1664525 × current + 1013904223) mod 232
These constants are widely used in numerical-methods textbooks, and the recurrence is known to have a "full period", cycling through all 232 values once before repeating (the constants satisfy the full-period conditions for linear congruential generators). The demo below feeds a seed into this formula and produces six lottery-style numbers. Change the seed and the numbers change; keep it and you get the same numbers however often you press. The demo does not filter duplicates, so the same number can appear twice.
"Same seed, same output" is the most important property of pseudo-random numbers. It is both a drawback and an advantage. For simulation and testing, reproducibility is a big advantage, which is why this site's Lottery Lab has a seed box. On the other hand, anyone who knows the seed and the formula can predict all the rest. For draws or security that is fatal.
Cryptographic random numbers: built to be unpredictable
The second method aims at unpredictability. The operating system gathers outside uncertainty (entropy), such as hardware noise and the precise timing of events, and runs a generator seeded from it. That generator is designed so that seeing some of its output does not let you work out the next. Browsers expose it. The developer documentation (MDN) says crypto.getRandomValues() gives "cryptographically strong random values", and also notes that implementations are not a truly random generator but a pseudo-random generator seeded with enough entropy. The algorithm may vary across browsers but is suitable for cryptographic purposes. The same documentation sets JavaScript's Math.random() apart as a non-cryptographic source.
So the border between "truly random" and "pseudo-random" is blurrier than it seems. Cryptographic randomness is a formula inside too. The differences are whether the seed was made unpredictably and whether the design prevents reconstructing internal state from output. This site draws the distinction but does not claim "true randomness".
The modulo trap: modulo bias
Getting a random value is not the end of the problem. A computer gives a large integer from 0 to 232 − 1. To make a number from 1 to 45, a common trick is to take the remainder after dividing by 45 and add 1. This introduces a slight bias: unless the number of values is a multiple of 45, some numbers get one extra chance of being picked.
| Size of the random value | Number of values | Ways for each of the first 31 numbers | Other 14 numbers | Relative difference |
|---|---|---|---|---|
| 8 bits (0–255) | 256 | 6 | 5 | first numbers 20% likelier |
| 32 bits | 4,294,967,296 | 95,443,718 | 95,443,717 | about 0.0000001% |
With 8-bit values, numbers 1–31 would come out 20% likelier than 32–45. Such bias is a mistake one can really make. With 32-bit values the difference shrinks to about one in a hundred million and is practically invisible, but the bias does not disappear. The way to remove it completely is rejection sampling: if a value from the ragged end that does not fit a multiple of 45 comes up, throw it away and draw again. Taking the remainder only within a range cut down to a multiple of 45 makes every number exactly equally likely.
Numbers without repeats: the Fisher–Yates shuffle
A lottery draw never repeats a number. The correct way to make that is to shuffle a list of 1 to 45 with the Fisher–Yates shuffle and take the first six. Working from the back, each position is swapped with a random position among those up to and including it, and every ordering comes out with exactly equal probability. A wrongly built shuffle biases the ordering. You can check that with a simulator in Are shuffles, draws and ladder games really fair?.
What LottoFlick actually uses (from the code)
This section was written from reading this site's source code.
- Number generator (
js/number-generator.js): the function that picks an integer takes a 32-bit value from the browser'scrypto.getRandomValuesand uses the remainder modulo the range (45 for Lotto 6/45, or the game's own range), but applies the rejection sampling described above: a value at or above the limit cut to a multiple of the range is discarded and redrawn. Numbers without repeats are made by shuffling the whole candidate list with a Fisher–Yates shuffle and taking the needed count from the front. - An exception: in an environment where
cryptois unavailable, the same function draws with JavaScript'sMath.random()instead. That fallback path has no rejection sampling. - Other modes: besides fully random, the generator has birthday, destiny-number and balanced modes. These partly reflect numbers you enter or adjust numbers to be spread evenly. They restrict part of the randomness, so they are not uniform draws, and they do not raise the chance of winning. Every number set has the same probability.
- Lottery Lab (
js/lab-engine.js): for speed the simulation uses xoshiro128**, a non-cryptographic generator. With no seed entered it takes a 128-bit seed from the browser's secure source; with a seed entered it hashes the text (cyrb128) into the seed, so the same seed gives the same result. For picking numbers it uses the same rejection sampling and a partial Fisher–Yates. A run with a seed is recorded in the result summary as reproducible, one without as not reproducible.
This is the structure read from the code. Which algorithm the browser's crypto uses internally varies by browser (we did not check). Nor has this site any external certification of its randomness.
How do we check "is it random?"
Proving that a random source is good is close to impossible; there are only statistical tests that catch bad ones. They look at whether frequencies are even, whether consecutive values are uncorrelated, and whether the order is unbiased. A method used in this site's hot and cold numbers article, building many fake histories and comparing with the real values, is one of them. Passing a test means "the defect that test looks for is absent", not "perfectly random".
Does the quality of random numbers relate to the chance of winning?
No. Whatever randomness this site's generator uses, one ticket's chance of winning is unchanged relative to the numbers the real draw machine picks. Real lottery winning numbers come from a separate drawing device, not this site's generator. That the generator's randomness is good means only "numbers are picked without clustering", not that the chance of matching is raised. The next article, How draws guarantee fairness, deals with real draw devices.
Limits
- The code description is from reading the source files at the time of writing. It may change if the code changes.
- Internal behaviour of libraries and browsers was not checked. MDN's description is of typical implementations.
- The modulo-bias figures were computed by simple division.
- The demo's formula (a linear congruential generator) is for explanation and is not what this site's generator uses.
LottoFlick is independent and not an official lottery operator. Numbers are random and cannot be predicted.
Sources and verification
- MDN: Crypto.getRandomValues() (cryptographically strong values from a pseudo-random generator seeded with enough entropy; Math.random is non-cryptographic) — verified
- LottoFlick generator source: randomInt (secure source with rejection sampling) and Fisher–Yates shuffle — computed by LottoFlick
- LottoFlick Lab engine source: xoshiro128** seeded from the secure source or from a user seed (cyrb128 hash) — computed by LottoFlick