Random 3 digit number generator
Uniform draws over all 1,000 three-digit strings, from 000 to 999, with leading zeros preserved.
A thousand values, and 9.97 bits
Three decimal digits describe 1,000 distinct values. Each digit carries log2(10) = 3.321928 bits, so a three-digit draw is 3 x 3.321928 = 9.97 bits of entropy. That is small enough to enumerate: an attacker with no rate limit walks the entire space in under a second, and a person with a pencil could do it in an afternoon.
This is not a defect, because nothing at this length is meant to resist guessing. Three digits are for things that need to be short, spoken aloud, written on a whiteboard or typed by a child. Classroom exercises, table numbers, seat allocations, quick draws in a lesson on probability. Use it where the value has to be readable, never where it has to be secret.
Three digits are not three dice
People reach for a three-digit generator expecting dice, and the two distributions are not the same shape. Three six-sided dice produce a total between 3 and 18, and the totals in the middle are far more likely than the ends, because there are many ways to make 10 and exactly one way to make 3. A three-digit draw is flat: every one of the 1,000 outcomes has probability 1/1000, and 000 is exactly as likely as 512.
If you want dice, you want a sum of independent small draws, which is a different tool. If you want a number where no value is favoured over any other, this is it. Saying which one you have is the entire point, and it is the thing most three-digit generators leave to the reader to guess.
The three digits here are independent of one another. 777 and 194 are equally likely, and a run of repeated digits is not evidence that the generator is broken.
Leading zeros are part of the space
Many generators implement this as a random integer between 100 and 999. That range holds 900 values, not 1,000, so every draw beginning with a zero has been silently removed. The entropy drops from 9.97 bits to log2(900) = 9.81 bits, and more importantly the output is no longer what the page claims to produce.
This tool draws each digit independently, so 007, 042 and 000 all appear at their correct rate of one in a thousand. If you are pasting results into a spreadsheet, format the column as text before you paste, or the leading zeros will be stripped by the importer rather than by the generator.
Repeats arrive quickly at this size
A space of 1,000 is small enough that duplicates are the normal outcome, not an anomaly. Drawing independently, the chance that a batch contains at least one repeated value passes 50% at 38 draws. In a classroom of 30 the odds of two people getting the same number are better than one in three.
That matters if you were planning to use three-digit values as identifiers. If every value must be distinct, either draw without replacement from the 1,000 available, or use more digits. Retrying on collision works but degrades badly as the batch approaches the size of the space.
| Draws | Probability of a repeat |
|---|---|
| 10 | 4.4% |
| 20 | 17.3% |
| 30 | 35.3% |
| 38 | 50.5% |
Doing this in your own code
JavaScript
// 3 independent digits, uniform, leading zeros preserved.
// 250 is Math.floor(256 / 10) * 10. Bytes at or above it are thrown away,
// which is what keeps every digit at exactly 1 in 10.
function randomDigits(n) {
const buf = new Uint8Array(1);
let out = "";
while (out.length < n) {
crypto.getRandomValues(buf);
if (buf[0] >= 250) continue;
out += buf[0] % 10;
}
return out;
}
randomDigits(3); // a 3-character string, never a number The usual shortcut, Math.floor(Math.random() * 9e5) + 1e5, cannot produce a leading zero and draws from a PRNG whose state is recoverable from its output.
Python 3.6+
import secrets
# Draw the digits. This never becomes an int, so leading zeros survive.
"".join(secrets.choice("0123456789") for _ in range(3))
# Or draw one integer and pad it back to width.
f"{secrets.randbelow(10 ** 3):03d}" random.randint() uses the Mersenne Twister and is not a security boundary. secrets draws from os.urandom.
Go
import (
"crypto/rand"
"fmt"
"math/big"
)
limit := new(big.Int).Exp(big.NewInt(10), big.NewInt(3), nil)
v, err := rand.Int(rand.Reader, limit)
if err != nil {
panic(err)
}
// The width verb is the part that keeps the leading zeros.
fmt.Printf("%03d\n", v) Java
import java.security.SecureRandom;
SecureRandom rnd = new SecureRandom();
StringBuilder sb = new StringBuilder(3);
for (int i = 0; i < 3; i++) {
sb.append(rnd.nextInt(10));
}
String value = sb.toString(); nextInt(bound) already rejects the unusable top of the range, so it is unbiased for a bound of 10.
PHP 7+
// random_int() is backed by the system CSPRNG.
// rand() and mt_rand() are not, and must not be used here.
$value = str_pad((string) random_int(0, 10 ** 3 - 1), 3, '0', STR_PAD_LEFT); Shell
# 3 digits straight from the kernel CSPRNG.
# tr keeps only ASCII digits, so every byte that survives is uniform over 0-9.
LC_ALL=C tr -dc '0-9' < /dev/urandom | head -c 3; echo Questions
Can this generate 000?
Yes, at exactly the same rate as every other value, one in a thousand. Generators that produce a number between 100 and 999 cannot, which quietly removes a tenth of the space and makes the first digit non-uniform.
Is a three-digit number the same as rolling dice?
No. Three digits give a flat distribution over 1,000 outcomes. Three dice give a peaked distribution over the totals 3 to 18, where 10 and 11 are about six times as likely as 3 or 18. If you need dice, sum independent draws instead.
How many can I draw before two are the same?
The chance of at least one duplicate passes 50% at 38 draws and reaches 35% at 30. Three digits is too small a space for uniqueness by luck, so use an explicit uniqueness check if you need distinct values.
Is it random enough for a prize draw?
The source is crypto.getRandomValues(), the same CSPRNG the browser uses for TLS, and the digit mapping uses rejection sampling so no digit is favoured. For a draw with real stakes, the harder problem is auditability rather than randomness: run it in front of the participants, or use a published, verifiable draw.
Does anything leave my browser?
No. Generation happens locally in JavaScript. Open your browser network panel during a draw and it stays empty. Nothing is transmitted, logged or stored.