DiceDecide

Why Sorting a List by Random Values Can Create Bias

Be the first to rate this page.

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 iValid random swap indices j
30, 1, 2, 3
20, 1, 2
10, 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.

Related DiceDecide tools

Enter your values, review the result, then use it with confidence.

Rate this page

Be the first to rate this page.