How Shuffling Actually Works
Fisher-Yates, one swap at a time, and why it is the only shuffle worth using.
Shuffling a list means producing an ordering chosen uniformly at random from all the orderings that exist. For a list of ten items that is 3,628,800 possibilities; for a deck of cards it is a number with 68 digits. A correct shuffle makes every one of them equally likely, and there is exactly one simple algorithm that does it.
That algorithm is Fisher-Yates, sometimes called the Knuth shuffle after the person who wrote it down for computers. It is about five lines long, runs in time proportional to the list length, and is easy to get subtly wrong in a way that still looks shuffled.
This page covers what it does, why it is uniform, the two standard bugs, and how to check an implementation you did not write.
The algorithm, one swap at a time
Start at the last position of the list. Pick a random position from the start of the list up to and including where you are, and swap the two items. Move one position to the left and repeat, always picking from the range that has not yet been finalised. Stop when you reach the first position.
That is the entire method. A ten-item list takes nine swaps; a fifty-two card deck takes fifty-one. Nothing is copied, nothing is sorted, and no item is ever considered twice.
- Walk from the end of the list toward the start.
- At each position i, draw a random index j between 0 and i inclusive.
- Swap the items at i and j.
- The item now at position i is final and is never touched again.
Why every ordering comes out equally likely
The argument is short and worth following, because it is what distinguishes a correct shuffle from one that merely looks shuffled.
On the first step there are n items and every one of them has an equal chance of being chosen for the last position — that is a 1-in-n chance each. On the second step, n−1 items remain and each has a 1-in-(n−1) chance of the second-to-last position. Multiply those probabilities all the way down and any specific complete ordering has probability 1 divided by n factorial.
Since there are exactly n factorial orderings and each has probability 1/n!, they are all equally likely, and none is unreachable. That is the definition of a uniform shuffle, and Fisher-Yates achieves it with the minimum possible number of random draws.
A three-item list, A B C
- Step 1: choose from {A, B, C} for position 3 — each has a 1-in-3 chance.
- Step 2: choose from the remaining two for position 2 — each has a 1-in-2 chance.
- Position 1 takes whatever is left. Total: 3 × 2 × 1 = 6 orderings, each at probability 1/6.
Bug one: drawing from the whole list instead of the remainder
The commonest error is choosing the swap partner from anywhere in the list rather than from the unfinalised range. It looks harmless, it produces output that is visibly shuffled, and it is measurably not uniform.
The reason is a counting mismatch. Drawing from the whole list every time gives n possibilities on each of n steps, so n to the power n equally likely execution paths. But there are only n factorial orderings, and n^n is not divisible by n! for n greater than 2 — so the paths cannot distribute evenly across the orderings, and some orderings must come out more often than others.
For a three-item list, the buggy version has 27 execution paths distributed across 6 orderings. Two of the orderings get 4 paths and four get 5, which means some orderings are 25% more likely than others. On a larger list the distortion is less obvious and does not go away.
Bug two: a biased random index
The second error is in the index draw rather than the algorithm. If the random index is produced by taking a remainder of a raw random byte — the classic `byte % (i+1)` — then some indices come up more often than others whenever the range does not divide evenly into 256.
Fisher-Yates makes n−1 index draws, so a small per-draw bias is applied repeatedly and compounds. The fix is rejection sampling: discard any byte that falls in the uneven leftover range and draw again. It costs a handful of extra draws and eliminates the bias entirely.
- The shuffle can be correct while the index draw is biased — both have to be right.
- Rejection sampling discards a small fraction of draws to make the remaining ones exactly uniform.
- The bias is invisible in output and only shows up in a frequency count over thousands of runs.
Checking a shuffle you did not write
The practical test is a frequency count. Shuffle a three-item list ten thousand times and tally how often each of the six orderings appears. A correct shuffle gives roughly 1,667 of each, with the variation you would expect from ten thousand samples — a spread of a few dozen either way.
A shuffle with the wrong-range bug gives a visibly lopsided table, with two orderings clearly under-represented. Three items is the right size for this test because six outcomes are easy to tally and the distortion is proportionally largest at small n.
For a larger list, the equivalent check is to track where a single item ends up. Shuffle a ten-item list ten thousand times and count how often item A lands in each position: a correct shuffle gives roughly a thousand per position.
Frequency test on 3 items, 10,000 shuffles
- Correct Fisher-Yates: each of the 6 orderings appears about 1,667 times.
- Wrong-range version: two orderings appear about 1,481 times and four about 1,852.
- That is a 25% gap, which is unmissable in a table and invisible in the output.
What a shuffle does not give you
A uniform shuffle makes every ordering equally likely, which is not the same as making every ordering look random. The ordering that leaves the list unchanged is one of the n! possibilities and it is exactly as likely as any other — a ten-item list will come back unshuffled about once in every 3.6 million attempts, and that is correct behaviour.
It also gives no guarantee about local structure. Three items that were adjacent can remain adjacent, and a shuffled playlist can put three tracks by the same artist together — which is a real problem with a correct answer, covered separately.
Frequently asked questions
Is one Fisher-Yates pass enough, or should I shuffle twice?
One pass is enough and a second changes nothing about correctness. After one complete pass every ordering is already exactly equally likely; unlike a physical riffle, it does not improve with repetition.
Why is the wrong-range version biased if it looks shuffled?
Because n^n execution paths cannot distribute evenly across n! orderings for n greater than 2. Some orderings necessarily get more paths, and the output looking shuffled tells you nothing about that.
Does the direction matter — end to start or start to end?
No. Both directions are correct as long as the draw range is the unfinalised portion. Walking backwards is the conventional form because the range expression is simpler.
How do I check whether an implementation is correct?
Shuffle a three-item list ten thousand times and tally the six orderings. Roughly 1,667 each is correct; a visible lopsidedness is the wrong-range bug.
Can a shuffle return the original order?
Yes, and it must be able to. The identity ordering is one of the n! possibilities and is exactly as likely as any other.
Does the random source matter as well as the algorithm?
Yes. Fisher-Yates makes n−1 index draws, so a biased index draw compounds. Rejection sampling on each draw is what removes it.
Is Fisher-Yates the only uniform shuffle?
It is the standard one and the simplest. Others exist — sorting by random keys is uniform if the keys are distinct — but they are slower and have their own failure modes.
How long does it take on a large list?
Time proportional to the list length. Fifty-two cards is fifty-one swaps; ten thousand items is 9,999 swaps, which is still instant.
Tools that use this
Order Randomizer
Shuffle any list into a fair random order — presentation order, turn order, anything.
Card Deck Shuffler
Shuffle a standard 52-card deck and deal hands — with the 52! number made concrete.
Playlist Shuffler
A shuffle that refuses to stack two tracks by the same artist together.
Team & Group Generator
Split any list of names into fair, balanced teams or groups.
Related guides
Why sort(() => Math.random() - 0.5) Is Biased
The one-line shuffle everyone copies, measured — and the distribution it actually produces.
Modulo Bias and Rejection Sampling
Why % 6 on a random byte quietly favours low numbers, and the two-line fix.
The Birthday Problem and Collisions
Why 23 people is enough for a shared birthday, and where else the same maths ambushes you.