RNG Bias: 5 Steps Fixing Seeded Card Deck Shuffles

A shuffle that looks random and a shuffle that is random are different things, and the gap between them is where money, fairness and reproducibility quietly leak away. This article gives you five concrete steps to detect RNG bias in a seeded list shuffle, fix the ones that matter, and stop chasing the ones that do not.

Three different failures, all called "RNG bias"

Before you can fix bias you have to know which one you have, because the three common failures have different causes and completely different fixes.

The first is modulo bias. You generate a raw random integer and map it to an array index with the remainder operator. When the generator's range is not an exact multiple of the index count, some indices get one extra value and therefore win slightly more often. The shuffle loop is fine; the index selection is not.

The second is distribution bias in the generator itself. A short-period or badly mixed pseudo-random number generator produces values that are correlated across draws. Even a textbook Fisher-Yates loop inherits that correlation, because the loop is only as uniform as the indices you feed it.

The third, and the one that quietly destroys card-deck shuffles, is state-space starvation. A generator with a finite internal state can only produce a finite number of distinct sequences. If that number is smaller than the number of permutations in your list, some orderings are physically unreachable no matter how good the algorithm is.

Test for the first two and you will feel productive. Miss the third and your deck is still broken.

Step 1: make the shuffle reproducible before you test anything

You cannot measure a bias you cannot repeat. Every test in this article needs the same shuffle to be produced on demand, which means a seeded pseudo-random number generator, not a system entropy source.

That is worth being explicit about, because the tool on this site deliberately does the opposite. It draws every index from the browser's cryptographic random source and offers no seed box at all. That is the right design for fair draws, password generation and one-off selections — but it means the tool is the fixed endpoint, not the test harness. To audit your own algorithm you write a small seeded generator, keep the seed as a parameter, and compare runs at the same seed.

A minimal reproducible generator is enough to expose every structural bug discussed below. It does not have to be cryptographically strong. It does have to be deterministic.

Step 2: test three items before you test fifty-two

The instinct is to shuffle a deck and eyeball it. That tells you nothing. The smallest case that can actually fail is a list of three items, because three items produce six permutations and six is small enough to count.

Run the shuffle a few hundred thousand times, tally how often each of the six orderings appears, and apply a chi-square goodness-of-fit test. With six categories you have five degrees of freedom, and the critical value at the usual 5 percent threshold is 11.07. Any statistic far above that number is not bad luck.

That single test is the highest-value hour you will spend on shuffle quality, because the failure modes that break a six-permutation shuffle also break a fifty-two-card one.

Step 3: fix the index draw, not the swap loop

For a generator whose raw output spans a power-of-two range, mapping to an index with a plain remainder is biased whenever the index count does not divide that range exactly. The fix is rejection sampling: compute the largest multiple of your index count that fits inside the generator range, discard any raw value at or above it, and only then take the remainder.

Three lines of that discipline turn a skewed map into an exact one:

var limit = Math.floor(RANGE / n) * n;
var u;
do { u = rawRandom(); } while (u >= limit);
var index = u % n;

Note the shape of the loop. It rejects, it does not clamp or fold. Any attempt to "save" a rejected value by subtracting the limit reintroduces the same imbalance you were trying to remove.

Step 4: compare your state space to your permutation count

This is the step almost nobody performs, and it is the one that decides whether a card shuffle can ever be fair.

List sizePermutationsBits needed
3 items6about 2.6
10 items3,628,800about 21.8
13 items6,227,020,800about 32.5
52 cards8.07 x 10^67about 225.6

Read the third row carefully. A generator with only 32 bits of state can produce at most 4,295,000,000 distinct sequences, and a 13-item list needs 6,227,020,800. The generator literally cannot reach every ordering — roughly a third of the possible orders are unreachable, and the reachable ones are not equally likely either.

Now read the fourth row. A full 52-card deck needs about 226 bits of state. A 64-bit generator covers about 1.8 x 10^19 sequences; a 128-bit generator covers about 3.4 x 10^38. Both are microscopic next to 8.07 x 10^67. If your deck shuffle is driven by a seeded 32-bit or even 128-bit generator, the distribution is not "slightly biased" — the overwhelming majority of deck orders can never occur.

The practical rule: if you must be able to reproduce a shuffle, pick a generator whose state is at least as wide as the log base 2 of the permutation count, and seed it from at least that many bits of entropy. For anything smaller than about ten items that is easy. For a card deck it means 226 bits, which rules out every trivially seeded generator in common use.

Step 5: choose seeded or cryptographic, and then stop

Seeded and unseeded are not better or worse; they answer different questions.

Choose a seeded generator when you need the same order twice — regression tests, procedural level generation, replays, save-file determinism. Accept that reproducibility and unpredictability are in direct conflict: a deterministic sequence is guessable by definition, so never use a seeded shuffle to decide who wins a prize.

Choose a cryptographic source, drawn fresh with no stored seed, when the outcome must be unguessable — lottery draws, prize allocation, anything adversarial. This is what the tool here does, and it is why it will not let you type a seed: a seed box would turn a fair draw into a repeatable one.

What you should not do is mix them. A common and expensive mistake is to seed a deterministic generator and then describe the result as cryptographically secure. It is not, and the difference is exactly the property that makes the shuffle safe.

The classic bug, measured

The most widely copied shuffle on the internet is not Fisher-Yates at all. It is a one-liner that hands a random comparator to the array sort method:

arr.sort(function () { return Math.random() - 0.5; });

It looks elegant and it is badly broken, because the comparator is not consistent: the same pair of elements can compare differently on different calls, and the sort algorithm has no way to produce a uniform result from contradictory instructions. Measured over 600,000 runs of a three-item list, here is what the six orderings actually did:

OrderingObservedExpected
0 1 237.43%16.67%
0 2 16.23%16.67%
1 0 212.52%16.67%
1 2 06.20%16.67%
2 0 16.27%16.67%
2 1 031.35%16.67%

The chi-square statistic for that run is about 356,595 against a critical value of 11.07. The most likely ordering is six times more likely than the least likely. A proper Fisher-Yates shuffle of the same list over the same number of runs lands every ordering between 16.64 and 16.72 percent, with a chi-square of about 2.1 — comfortably inside the acceptance region.

Note that the exact percentages depend on the sort implementation in your engine, so your numbers will differ. The magnitude of the failure will not.

Why you cannot brute-force a ten-item test

The three-item test works because six categories is countable. Ten items is a different universe: 3,628,800 permutations means you would need tens of millions of runs just to average a few observations per ordering, and the test tells you nothing useful at a realistic sample size.

Use an aggregate statistic instead. Build a grid of item against position — ten items across ten positions is 100 cells — and count how often each item lands in each position. Under a fair shuffle every cell averages one tenth of the runs. The degrees of freedom are 81, and the critical chi-square value at the 5 percent threshold is about 103. This position-frequency test detects the same structural faults as the six-permutation test while remaining tractable at any list size.

Common misdiagnoses

Three beliefs waste the most time, and all three are stated confidently by people who have not measured anything.

The first is that a bad seed causes bias. It does not. A weak or reused seed makes a deterministic sequence guessable, which is a security problem, but with a correct algorithm and an adequate state space the distribution stays uniform. Confusing unpredictability with uniformity sends people to rewrite a generator that was never the problem.

The second is that a cryptographic generator fixes everything. It fixes entropy and predictability. It does not fix a modulo mapping error in your own loop, and it does not enlarge the state space if you seed it from a 32-bit value.

The third is that a shuffle is unbiased because every item appears roughly equally often overall. That test passes for a shuffle where the first element is always the largest and the ordering is otherwise scrambled. Uniform marginal counts are necessary, not sufficient. Only the joint distribution over orderings — sampled as above — tells you the truth.

The five-step checklist

  1. Make the shuffle deterministic at a fixed seed before you measure anything.
  2. Run the three-item chi-square test; treat any statistic above 11.07 with a sample of hundreds of thousands as a real defect.
  3. Replace every remainder-based index draw with rejection sampling that discards out-of-range values instead of folding them.
  4. Compare your generator's state width against the logarithm base 2 of the permutation count, and for card decks require roughly 226 bits.
  5. Choose seeded or cryptographic on purpose, and never present a reproducible shuffle as a secure one.

Do those five things and the question "is my shuffle biased" stops being a matter of opinion. You will have a number, a threshold and a verdict — and a deck that can actually reach every order it is supposed to.

Frequently Asked Questions

What is RNG bias in list shuffling?

RNG bias in list shuffling occurs when certain permutations appear more frequently than others, making the shuffle predictable. This usually happens when the random number generator's range is not evenly distributed across the possible outcomes of the list.

How does modulo bias affect seeded shuffling algorithms?

Modulo bias happens when you use the modulo operator to map a random number to an array index, causing smaller remainders to occur more frequently. To prevent this in seeded shuffling algorithms, you should use rejection sampling or a mathematically unbiased mapping method.

How can I detect bias in a random number generator?

You can detect RNG bias by running statistical tests like the NIST test suite or the Dieharder tests to evaluate the distribution of generated numbers. Additionally, you can shuffle a small list millions of times and perform a chi-square test to see if all permutations appear with equal probability.

Why is the Fisher-Yates shuffle considered unbiased?

The Fisher-Yates shuffle is considered unbiased because every possible permutation of the list is guaranteed to occur with exactly the same probability. However, it only remains truly unbiased if the underlying random number generator used to select indices is free of modulo bias.

Can a seeded pseudo-random number generator (PRNG) be biased?

Yes, a seeded PRNG can be biased if its internal state space is smaller than the number of possible permutations in the list you are shuffling. This limitation means the algorithm physically cannot generate every possible combination, leading to severe bias over time.

How do I prevent modulo bias when implementing a shuffle algorithm?

To prevent modulo bias, you must discard random numbers that fall into the upper unevenly distributed bracket of the generator's range before applying the modulo operator. This technique, known as rejection sampling, ensures that the remaining numbers map perfectly to your required indices without favoring specific outcomes.

What happens if I use a bad seed for list shuffling?

Using a poorly chosen seed does not inherently cause RNG bias, but it makes the sequence of shuffles predictable if the seed is guessed or reused. To maintain security and unpredictability, always generate seeds using a high-entropy source rather than static values like the current system time.

How many shuffles are needed to test for RNG bias?

To accurately test for RNG bias, you need to run the shuffling algorithm thousands or even millions of times depending on the size of the list. For a list of just 10 items, there are over 3.6 million permutations, meaning you need a massive sample size to achieve statistical significance in your distribution tests.

Is using a cryptographically secure PRNG (CSPRNG) necessary for shuffling?

While a standard PRNG is sufficient for basic applications like shuffling a music playlist, a CSPRNG is necessary for online gambling or security-sensitive applications. CSPRNGs pass rigorous statistical tests for randomness and are highly resistant to prediction, ensuring that the seeded shuffle remains completely unbiased and secure.

Does array size affect RNG bias in shuffling?

Yes, as the array size grows, the number of possible permutations increases factorially, which can quickly exceed the internal state size of your random number generator. If the PRNG cannot produce enough distinct states to cover every permutation, it will inherently produce a biased shuffle regardless of the algorithm used.