Why Sorting a List by Random Values Can Create Bias
Sorting a list with a comparator that returns random positive or negative values is not a reliable shuffle because sorting algorithms expect a consistent comparator. Use Fisher–Yates instead: for index i, choose one uniformly random index from 0 through i and swap once.
A worked example with numbers
For A, B, and C, a comparator may say A precedes B on its first call and B precedes A on a later call. A sort implementation is not required to repair that contradiction, so some final orders can be more common than others. Fisher–Yates instead makes three choices: select one of 3 items for the final slot, then one of 2 for the next, then the last item, producing 3 × 2 × 1 = 6 equally reachable orders.
How to set the rule before the result
Use the list shuffler rather than coding `array.sort(() => Math.random() - 0.5)`. If code is required, copy the input, walk from the last index down to 1, choose an integer j from 0 through i inclusive, and swap positions i and j.
Common mistakes that change the odds or the process
Do not use a random sort comparator merely because it appears to change the order in a short test. Do not use `Math.round` to map a floating value to an index; endpoints receive different interval widths. Do not mutate the original roster if people must audit it later.
Where this method stops being appropriate
MDN states that a sort comparator is expected to be pure, stable, reflexive, anti-symmetric, and transitive in MDN’s Array.prototype.sort() reference. A random comparator breaks those conditions, so its apparent randomness is not a uniform-permutation guarantee.
How the random source fits into the rule
MDN’s Array.prototype.sort() reference
Apply the rule to the actual input
Use the list shuffler rather than coding `array.sort(() => Math.random() - 0.5)`. If code is required, copy the input, walk from the last index down to 1, choose an integer j from 0 through i inclusive, and swap positions i and j.
Audit the calculation or allocation
For A, B, and C, a comparator may say A precedes B on its first call and B precedes A on a later call. A sort implementation is not required to repair that contradiction, so some final orders can be more common than others. Fisher–Yates instead makes three choices: select one of 3 items for the final slot, then one of 2 for the next, then the last item, producing 3 × 2 × 1 = 6 equally reachable orders.
Do not import a different rule by accident
Do not use a random sort comparator merely because it appears to change the order in a short test. Do not use `Math.round` to map a floating value to an index; endpoints receive different interval widths. Do not mutate the original roster if people must audit it later.
Limit of this specific method
MDN states that a sort comparator is expected to be pure, stable, reflexive, anti-symmetric, and transitive in MDN’s Array.prototype.sort() reference. A random comparator breaks those conditions, so its apparent randomness is not a uniform-permutation guarantee.
Source and reproducibility
MDN’s Array.prototype.sort() reference
Use an index range with no missing endpoint
| Loop index i | Valid random swap indices j |
|---|---|
| 3 | 0, 1, 2, 3 |
| 2 | 0, 1, 2 |
| 1 | 0, 1 |
For an array of length 4, the first step must include index 3 itself. A formula such as `floor(random * (i + 1))` makes all listed indices equally reachable when the raw random value is uniform on the half-open interval from 0 to 1. The later swaps preserve the already chosen final positions.
Test a shuffle by counting permutations, not appearance
For three labelled values, record many complete outputs and compare counts for all six orders. A screen that looks scrambled after one execution says nothing about their probabilities. Unit tests can also check that output contains the same multiset of entries and that the input copy was not mutated. Those tests do not prove browser entropy, but they catch the concrete mapping and state errors that a random comparator hides.
Reproduce this result before relying on it
A random comparator is a sorting callback whose answer for the same pair may change, violating the consistency required by a sorting algorithm. For A, B, and C, a comparator may say A precedes B on its first call and B precedes A on a later call. A sort implementation is not required to repair that contradiction, so some final orders can be more common than others. Fisher–Yates instead makes three choices: select one of 3 items for the final slot, then one of 2 for the next, then the last item, producing 3 × 2 × 1 = 6 equally reachable orders.
Choose the action that matches the stated rule
Use the list shuffler rather than coding `array.sort(() => Math.random() - 0.5)`. If code is required, copy the input, walk from the last index down to 1, choose an integer j from 0 through i inclusive, and swap positions i and j. Do not use a random sort comparator merely because it appears to change the order in a short test. Do not use `Math.round` to map a floating value to an index; endpoints receive different interval widths. Do not mutate the original roster if people must audit it later.
What the number does not decide
MDN states that a sort comparator is expected to be pure, stable, reflexive, anti-symmetric, and transitive in MDN’s Array.prototype.sort() reference. A random comparator breaks those conditions, so its apparent randomness is not a uniform-permutation guarantee. MDN’s Array.prototype.sort() reference
State the complete decision model
Sorting a list with a comparator that returns random positive or negative values is not a reliable shuffle because sorting algorithms expect a consistent comparator. Use Fisher–Yates instead: for index i, choose one uniformly random index from 0 through i and swap once. A random comparator is a sorting callback whose answer for the same pair may change, violating the consistency required by a sorting algorithm.
Before publishing or using the outcome
For A, B, and C, a comparator may say A precedes B on its first call and B precedes A on a later call. A sort implementation is not required to repair that contradiction, so some final orders can be more common than others. Fisher–Yates instead makes three choices: select one of 3 items for the final slot, then one of 2 for the next, then the last item, producing 3 × 2 × 1 = 6 equally reachable orders. Use the list shuffler rather than coding `array.sort(() => Math.random() - 0.5)`. If code is required, copy the input, walk from the last index down to 1, choose an integer j from 0 through i inclusive, and swap positions i and j.