Picking a random integer sounds simple, but under the hood a random number generator has to decide two things: where the randomness comes from, and how to turn that raw randomness into a number inside your chosen range. This section walks through both, with the actual math.
Pseudo-Random (PRNG) vs True Random (TRNG)
A pseudo-random number generator (PRNG) uses a deterministic algorithm seeded from something time-like (the current millisecond, a counter, a process ID). Examples include JavaScript's Math.random, the Mersenne Twister used by Python and many game engines, and xorshift variants used in high-speed simulations. Given the same seed, a PRNG always produces the same sequence. It is fast, reproducible, and statistically uniform over long runs, but it is not secret.
A true random number generator (TRNG) pulls entropy from a physical process: atmospheric radio noise (random.org), thermal noise in a resistor, radioactive decay, or the timing jitter of keyboard presses and disk I/O that feeds /dev/random on Linux. TRNGs are slower and harder to reproduce, but their output cannot be predicted even if you know everything about the hardware.
Linear Congruential Generator (LCG) Formula
The oldest and simplest PRNG is the linear congruential generator. It advances one number at a time using:
Xn+1 = (a × Xn + c) mod m
a = multiplier, c = increment, m = modulus, X0 = seed
Pick good constants and you get a long, well-distributed cycle. Pick bad constants and the output repeats quickly or clumps in predictable patterns. Modern languages have mostly moved on to Mersenne Twister, PCG, or xoshiro families, but LCGs still show up inside embedded systems and older library code.
Uniform Integer Draw From [min, max]
Once the underlying PRNG produces a floating-point value rand() in [0, 1), turning it into a uniform integer in the inclusive range [min, max] uses one line of math. This is the exact formula the widget above runs:
r = min + ⌊rand() × (max − min + 1)⌋
Example: min = 1, max = 100, rand() = 0.7324
r = 1 + ⌊0.7324 × 100⌋
r = 1 + 73
r = 74
The floor (⌊ ⌋) and the + 1 inside the multiplier are what make both endpoints reachable. Without the + 1, the maximum would never come up. Without the floor, you would get a decimal instead of an integer.
Drawing Without Replacement (Fisher-Yates Concept)
When the "no duplicates" box is checked, the draws are no longer independent. Each number that is picked has to be removed from the pool so it cannot come up again. The clean way to do this is a Fisher-Yates shuffle: fill an array with every number from min to max, then swap random pairs until the array is shuffled, then read off the first N values. The widget above uses an equivalent pool-and-splice variant.
Step 1: pool = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Step 2: pick index 3 → draw 4, pool = [1, 2, 3, 5, 6, 7, 8, 9, 10]
Step 3: pick index 7 → draw 9, pool = [1, 2, 3, 5, 6, 7, 8, 10]
Step 4: pick index 0 → draw 1, pool = [2, 3, 5, 6, 7, 8, 10]
... repeat until you have the count you asked for
Because each draw changes the pool, the probability of the remaining numbers shifts every step. On a lottery-style 6-of-49 draw, the first number has a 1 in 49 chance, the second has a 1 in 48, and so on.
Probability of Each Number in a Uniform Draw
In a single uniform draw from [min, max], every integer has the same probability:
p = 1 / (max − min + 1)
Range 1 to 100: p = 1 / 100 = 1.00%
Range 1 to 6: p = 1 / 6 ≈ 16.67%
Range 0 to 1: p = 1 / 2 = 50.00%
Range 1 to 1000: p = 1 / 1000 = 0.10%
That 1 in 100 figure is why "pick a random number between 1 and 100" is such a common fairness test: it is easy to see by eye if a supposedly random sequence is overweighted toward certain endpoints or digits.
Quick Reference: Common RNG Types Compared
| Generator | Seed Source | Period | Speed | Typical Use |
|---|
| Math.random (V8) | Time + counter | ~2^128 | Very fast | Games, UI, non-security |
| Mersenne Twister | Time or user seed | 2^19937 − 1 | Fast | Python, simulations |
| xorshift / xoshiro | User seed | 2^64 to 2^256 | Very fast | Game engines, GPU code |
| Linear congruential (LCG) | User seed | Up to 2^64 | Very fast | Embedded, legacy libs |
| crypto.getRandomValues | OS entropy pool | Effectively infinite | Fast | Tokens, keys, passwords |
| /dev/urandom (Linux) | OS entropy pool | Effectively infinite | Fast | Server-side secrets |
| /dev/random (Linux) | Hardware entropy | Blocks on low entropy | Slow | Long-term key material |
| Hardware TRNG (Intel RDRAND) | Thermal noise | Effectively infinite | Moderate | Crypto, secure boot |
The widget on this page uses Math.random, which is excellent for the use cases it targets (games, draws, sampling, teaching) and should not be used for anything where an attacker guessing the next number would cause harm.