Skip to main content

How to Pick a Random Number You Can Actually Trust

A practical guide to generating a random number with ranges, no-duplicate batches, and raffle sampling — plus why crypto-grade randomness beats Math.random for fair draws.

Published By 李雷
#random number #rng #raffle #sampling #cryptography

How to Pick a Random Number You Can Actually Trust

"Pick a number between 1 and 100." It sounds trivial, and for a single casual choice it is. But the moment money, fairness, or test data is on the line, the quiet questions start: Is this draw actually unbiased? Can someone replay it and rig the outcome? Did I just audit the same receipt twice? This guide walks through the parts of random-number generation that bite people in practice — ranges, deduplication, batch draws, sampling — and the one technical detail that separates a toy from a tool you can defend in front of an audience.

You can follow along in the Random Number Generator, which does all of this in the browser with no data leaving the page.

Ranges Are Inclusive on Both Ends

The first thing to nail down is the range, because an off-by-one error here is silent and ugly. A good generator treats the range as inclusive on both ends: a range of 1 to 6 yields exactly six outcomes, which is a fair die. If your tool quietly excludes the top value, your "six-sided die" only ever shows five faces, and you will not notice until a player does.

Negative numbers should work too. Set a minimum of -100 and a maximum of 100, and you should get a clean draw across all 201 integers. Treat the inclusive boundary as a feature you test, not an assumption you trust — type 1 to 2 and confirm you actually see both 1 and 2 over a handful of clicks.

Batches and the No-Duplicate Trap

Single draws are easy. Batches are where most spreadsheet workflows fall apart. The classic failure is RANDBETWEEN in a column of 12 cells: nothing stops two cells from landing on the same value, so you end up hand-cleaning collisions. A proper batch mode with a "no duplicates" switch does sampling without replacement — every number it hands back is distinct.

There is one rule that surprises people. If you ask for 100 unique numbers from the range 1 to 50, only 50 distinct values exist. A correct tool returns all 50 and stops; it does not loop forever or invent a 51st. So if you request more unique numbers than the range can hold, you get fewer than you asked for, and that is the right answer, not a bug. The fix is to widen the range or turn deduplication off.

A Concrete Example: 5 Unique Numbers from 1 to 100

Here is the exact draw most people mean when they say "pick some numbers." Set the minimum to 1, the maximum to 100, batch size to 5, and turn on "no duplicates." One click produces something like:

Range: 1–100, batch 5, unique
Output: 73, 8, 41, 96, 27

Five distinct integers, none repeated, every one inside the inclusive range. Refresh and you get a completely different five — 12, 55, 3, 88, 60 — because each click pulls fresh entropy. That non-repeatability is intentional, and it matters more than it looks, which brings us to the part everyone skips.

Pseudo-Random vs Cryptographic Random

This is the detail that decides whether your draw is defensible. JavaScript's Math.random is a pseudo-random number generator (PRNG). It is fast and fine for shuffling a list of background colors, but it is seeded from internal state that is not designed to resist prediction. In some engines an observer who sees enough outputs can reconstruct the seed and predict the rest. For a public raffle, that is a real attack surface.

The browser ships a better source: crypto.getRandomValues, a cryptographically secure PRNG (CSPRNG). It draws from the operating system's entropy pool — /dev/urandom on Linux, getentropy on macOS, BCryptGenRandom on Windows — the same family of randomness that powers TLS handshakes and WebAuthn. The difference at the call site is small but the guarantee is night and day:

// Predictable: fine for a color picker, not for a draw worth money
const weak = Math.floor(Math.random() * 100) + 1;

// CSPRNG: same entropy source as TLS, no reconstructable seed
const buf = new Uint32Array(1);
crypto.getRandomValues(buf);
const strong = (buf[0] % 100) + 1;

A tool built on crypto.getRandomValues cannot be "predicted" by watching previous outputs, which is exactly the property you want when nobody trusts the person clicking the button. If your use case instead needs a reproducible sequence — a research sample others must verify — that is the one time you want a seeded PRNG in a script instead, because a CSPRNG is deliberately non-reproducible.

Where I Actually Reach for This

I run small giveaways from a streaming channel, and the recurring accusation is always the same: "you picked your friend." The first time it happened I was using a half-remembered Math.random snippet pasted into the browser console, and I had no honest answer to "how do I know that was fair?" Now the flow is boring in the best way. I set the range to 1 and the comment count, draw once with a CSPRNG-backed tool, and screen-record the moment the number appears. The draw is unseeded and one-shot, so there is nothing to replay and nothing to rig. The complaints stopped — not because people suddenly trust me, but because the method is auditable and I can point at the recording.

Common Mistakes to Avoid

A few traps come up again and again:

  • Over-requesting unique values. Asking for 60 distinct numbers in a range of 1 to 50 returns only 50. Widen the range or drop the uniqueness rule.
  • Forgetting the inclusive boundary. A 1-to-6 range is six outcomes, not five. This off-by-one silently skews dice rolls and lottery simulations.
  • Blaming the tool for huge-batch lag. A 10,000-number batch is slow because the browser renders 10,000 DOM nodes, not because the math is hard. Generate in chunks of about 1,000.
  • Confusing fairness with reproducibility. A fair draw and a replayable draw are opposites. Pick the one your situation actually needs.

Wrapping Up

Random-number generation only looks simple. Inclusive ranges, deduplicated batches, sampling-without-replacement limits, and the PRNG-versus-CSPRNG distinction are the difference between a draw you can defend and one you have to apologize for. When the outcome matters, use a CSPRNG-backed generator, keep your ranges honest, and record anything you might be asked to justify later.

For related fair-draw and generation workflows, try the Dice Roller for tabletop and probability work, or the Password Generator when you need crypto-grade randomness shaped into a secret rather than a number. All of them run entirely in your browser.


Made by Toolora · Updated 2026-06-13