Fact-checked by Grok 2 weeks ago

Fisher–Yates shuffle

The Fisher–Yates shuffle is an algorithm for generating a uniformly random permutation of a finite sequence of distinct elements, ensuring that every possible permutation occurs with equal probability. Originally devised as a manual method for randomizing statistical experiments, it has become a cornerstone of computational randomization due to its efficiency and lack of bias. The algorithm was first described in 1938 by British statisticians Ronald Fisher and Frank Yates in their book Statistical Tables for Biological, Agricultural and Medical Research, where it served as a practical tool for creating randomization schedules in agricultural and biological studies by iteratively selecting and eliminating elements from a list. This original formulation, while effective for hand computation, was inefficient for large datasets as it required physically crossing out selected items, leading to O(n²) complexity. In 1964, American programmer Richard Durstenfeld adapted it into an optimized computer algorithm, published as "Algorithm 235: Random permutation" in Communications of the ACM, which achieves O(n) time complexity by swapping elements in place from the end of the array toward the beginning. The method was further popularized by Donald Knuth in the 1969 first edition of The Art of Computer Programming, Volume 2: Seminumerical Algorithms, where it is presented as Algorithm P and analyzed for its probabilistic uniformity. In its modern form, the Fisher–Yates shuffle (often called the Knuth shuffle) operates on an of n elements indexed from 0 to n-1: for each index i from n-1 down to 1, select a random index j uniformly from 0 to i (inclusive) and swap the elements at positions i and j. This backward guarantees that each prefix of the is equally likely at every step, producing an unbiased result without the pitfalls of simpler shuffling techniques, such as repeated random swaps that can favor certain permutations. Widely implemented in programming languages like Python's random.shuffle() and Java's Collections.shuffle(), it is essential for applications including simulations, , and game development where true is critical.

History and Development

Fisher and Yates' Original Contribution

The Fisher–Yates shuffle first appeared in the 1938 book Statistical Tables for Biological, Agricultural and Medical Research by Ronald A. Fisher and Frank Yates, published by Oliver & Boyd in . This seminal work provided statistical tools, including techniques, tailored for researchers in , , and . Within the book, the method was introduced as a practical approach to generating random permutations of experimental units, essential for designing unbiased agricultural trials. In agricultural statistics, systematic biases could arise from environmental gradients across field plots; randomizing treatment assignments to blocks or plots ensured that such effects were not confounded with treatment differences, allowing valid inference about treatment efficacy. The procedure emphasized manual execution, relying on physical randomization aids like dice, coin flips, or precomputed tables of random digits to select positions without bias. The original process began with a numbered list of items from 1 to n. Starting from the end, for each position i from n down to 2, a random position j was chosen uniformly from 1 to i using the selected aid, and the items at i and j were swapped. This backward iteration progressively fixed the while maintaining uniformity. The book included dedicated tables—such as Tables XVII, XVIII, and XIX—containing sequences of random numbers to support this step-by-step selection, enabling researchers to perform the on paper without generating numbers anew. For a small list of 5 treatments labeled A (1), B (2), C (3), D (4), and E (5), the manual process might proceed as follows: Begin with the ordered list A B C D E. For i=5, consult a random table or roll a 5-sided die (or equivalent) to pick j=2; swap positions 5 and 2 to get A E C D B. For i=4, pick j=4 from 1–4 (no swap): A E C D B. For i=3, pick j=1; swap 3 and 1: C E A D B. For i=2, pick j=2 (no swap): C E A D B. The resulting order C E A D B assigns treatments randomly to experimental plots, minimizing bias. This table-based manual approach formed the basis for subsequent computational implementations.

Modern Adaptations by Durstenfeld and Knuth

In the 1960s, the Fisher–Yates shuffle evolved from its manual origins into a form optimized for computer implementation, marking a pivotal shift toward programmatic generation. Richard Durstenfeld introduced this computational adaptation in 1964 through Algorithm 235, titled "Random Permutation," published in the Communications of the ACM. Written in , the algorithm restructured the original swap-based logic to operate efficiently on arrays stored in , enabling direct in-place shuffling without auxiliary storage. This implementation addressed the limitations of manual methods by leveraging machine-generated random numbers for index selection, ensuring scalability for larger datasets in early computing environments. Durstenfeld's work represented the first published optimal O(n) algorithm for uniform random permutations, influencing subsequent developments in numerical computing. Donald Knuth further formalized and popularized the algorithm in the first edition of his seminal text The Art of Computer Programming, Volume 2: Seminumerical Algorithms (1969), where he presented it as "Algorithm P (Shuffling)." Knuth emphasized its linear time complexity and proven uniformity, integrating it into discussions of random number generation and combinatorial algorithms. By attributing the method to Fisher and Yates while crediting Durstenfeld's computational refinement, Knuth solidified its role as a cornerstone of unbiased shuffling in software design. The adaptations by Durstenfeld and Knuth had lasting historical impact, promoting widespread adoption in early programming languages such as and derivatives, as well as in statistical software for simulations and randomization tasks. Their versions became foundational for generating fair random orders in and , paving the way for integration into modern libraries like those in C++ and .

Algorithm Description

Original Method

The original Fisher–Yates shuffle, developed by statisticians and Frank Yates in 1938, offers a straightforward technique for producing a of a of n distinct items, such as numbers or labels written on slips of paper. This method was designed for statistical applications where computational resources were unavailable, emphasizing simplicity for hand execution. The procedure begins at the end of the list and works backward. For the current position i = n, select an index j uniformly at random from the set \{1, 2, \dots, i\} and interchange (swap) the items at positions i and j. Then set i = n-1 and repeat: choose a new j uniformly from \{1, 2, \dots, i\} and swap the items at i and j. Continue this process, decrementing i by 1 each time, until i = 1. At this point, the list contains a uniformly random permutation. The mathematical selection rule ensures uniformity: j is drawn such that P(j = k) = \frac{1}{i} for each k = 1, 2, \dots, i. In the pre-computer , generating the random j relied on analog tools like published tables of random digits, tosses to simulate choices (though less ideal for larger ranges), or for small i. Random number tables, often included in statistical handbooks, provided sequences of uncorrelated digits; to obtain j for a given i, one would consult the table and interpret digits (e.g., modulo i + 1 if needed, though simple sufficed for uniformity) until a value in the desired range appeared. This manual mirrored physical processes like labeled slips from a without replacement. While effective for modest n, the original method proves inefficient for large lists due to the cumulative manual labor of repeated random selections and swaps, each requiring consultation of tables or physical randomization, rendering it impractical beyond dozens of items without mechanical assistance.

Modern Iterative Version

The modern iterative version of the Fisher–Yates shuffle, also known as the Knuth shuffle, generates a uniformly random permutation of an array of n elements through a series of swaps, starting from the end of the array and progressively fixing positions from back to front. This adaptation by Richard Durstenfeld in 1964 and popularized by Donald Knuth provides an efficient computational implementation suitable for programming languages. The algorithm operates in-place, requiring no additional storage beyond the input and a source of uniform random integers, making it space-efficient with O(1) auxiliary space. Its is linear, , as it performs exactly n-1 swaps, each involving constant-time operations. Further performance improvements can be achieved through batching random number generation, as described in "Batched Ranged Random Integer Generation", to optimize for scenarios requiring multiple random selections. Here is the for the standard backward variant, assuming zero-based array indexing:
procedure shuffle([array](/page/Array) A of size n)
    for i from n-1 downto 1 do
        j ← uniform random integer in {0, 1, ..., i}
        swap A[i] and A[j]
In this loop, the last element is left untouched (as it is already in a random relative to itself), and for each preceding i, an element is randomly selected from the first i+1 positions (0 through i) and swapped into place i, ensuring the prefix up to i is randomized. An equivalent forward variant iterates from the beginning:
procedure shuffle(array A of size n)
    for i from 0 to n-2 do
        j ← uniform random integer in {i, i+1, ..., n-1}
        swap A[i] and A[j]
This fixes positions from front to back by swapping the current position i with a random later position. Both variants produce identical distributions when using unbiased , but the backward version is often preferred in modern implementations to minimize potential bias from imperfect generators in small ranges, as the range size decreases monotonically.

Uniformity Proof

The uniformity of the Fisher–Yates shuffle ensures that every one of the n! possible permutations of an of n distinct elements occurs with equal probability $1/n!. This property can be rigorously established through on n, the size of the . For the base case with n=1, the array has only one element and thus one possible permutation, which the algorithm produces with probability $1 = 1/1!. Assume the inductive hypothesis holds for all arrays of size k < n: the algorithm generates each of the k! permutations uniformly with probability $1/k!. For the inductive step with size n, consider the initial iteration of the modern backward version, where the nth position (index n-1 in 0-based indexing) is swapped with a uniformly random index j \in \{0, 1, \dots, n-1\}. This makes any of the n elements equally likely to end up in the nth position, each with probability $1/n. After this swap, the subsequent iterations apply the algorithm to the first n-1 positions, which contain the remaining n-1 elements and, by the inductive hypothesis, produce a uniform random permutation of those elements, each occurring with conditional probability $1/(n-1)!. Thus, the overall probability of any specific permutation \pi of n elements is (1/n) \times 1/(n-1)! = 1/n!. This induction confirms uniformity for all n. Equivalently, the probability of generating any fixed permutation \pi is the product of the uniform selection probabilities across all n-1 swaps: P(\pi) = \prod_{i=2}^{n} \frac{1}{i} = \frac{1}{n!}. Each swap preserves conditional uniformity on the prefix of the array, ensuring the choices at later steps remain unbiased given prior outcomes. This proof originates from the analysis in Knuth's The Art of Computer Programming.

Practical Examples

Manual Pencil-and-Paper Approach

The original provides a straightforward manual procedure for generating a random permutation of a small set of items, such as a deck of cards, using only pencil, paper, and a simple randomizer like coin flips to simulate uniform selection. This method involves iteratively selecting and removing an item from the remaining list at a randomly chosen position, building the shuffled sequence step by step, which is ideal for non-digital environments. Consider shuffling a small deck of 5 cards labeled A, B, C, D, and E, initially arranged in sorted order: A B C D E. The process begins with all cards remaining in the list. For each step from the first to the fourth selection (leaving the last card automatically in position), determine a random index k among the current remaining cards (from 1 to the number remaining), select the k-th card, append it to the shuffled sequence, and strike it out from the remaining list. To generate k uniformly with coin flips when the number of choices is not a power of 2, flip the coin multiple times to produce a binary number representing a value from 1 to the next power of 2 greater than or equal to the number of choices, then map it accordingly; if the result exceeds the needed range, repeat the flips. For example, with 5 remaining cards, flip the coin three times (for 8 possible outcomes, 000 to 111 in binary, valued 0 to 7), add 1 to get 1 to 8, and if 6, 7, or 8 results, re-flip to ensure uniformity. Suppose the flips yield k=3 for the first selection: strike out the 3rd card (C), appending C to the shuffled deck. Now 4 cards remain: A B D E. For the next, with 4 choices (a power of 2), two coin flips suffice (00=1, 01=2, 10=3, 11=4); suppose they yield k=4, selecting E, appending C E, remaining A B D. Continuing, suppose k=1 selects A (appending C E A, remaining B D), then k=2 selects D (appending C E A D, remaining B), and finally B is last (C E A D B). The following table visualizes this example walkthrough:
StepRemaining CardsRandom k (via coin flips)Selected CardShuffled Sequence So Far
InitialA B C D E---
1A B C D E3CC
2A B D E4EC E
3A B D1AC E A
4B D2DC E A D
5B(last)BC E A D B
For manual implementation, when coin flips result in an out-of-range value (a "tie" or excess outcome), simply discard and repeat the flips to maintain fairness without bias. This approach ensures each permutation of the 5 cards is equally likely, with 120 possible outcomes, though the coin-based selection approximates uniformity closely for small datasets when re-flips are used.

Step-by-Step Array Illustration

To illustrate the modern iterative version of the , consider a numeric array of length 5 initialized as [1, 2, 3, 4, 5]. The process begins at the last index (i=4) and iterates downward to i=1, selecting a random index j uniformly from 0 to i (inclusive) at each step, then swapping the elements at positions i and j; this operates in-place, modifying the original array without requiring extra storage beyond a few variables. The table below traces the execution with assumed random selections for j (e.g., j=2 when i=4, j=0 when i=3, and similarly for other steps) to demonstrate the flow and progressive changes.
iRandom jSwap ActionUpdated Array
--Initial state[1, 2, 3, 4, 5]
42Swap indices 4 and 2[1, 2, 5, 4, 3]
30Swap indices 3 and 0[4, 2, 5, 1, 3]
21Swap indices 2 and 1[4, 5, 2, 1, 3]
10Swap indices 1 and 0[5, 4, 2, 1, 3]
0-No action[5, 4, 2, 1, 3]
The final shuffled array is [5, 4, 2, 1, 3], one of the 120 possible permutations, each equally likely due to the uniform random selections.

Programming Implementations

Python Example

In Python, the Fisher–Yates shuffle can be implemented manually using the random module to generate uniform random indices for swapping elements in place. This approach adheres to the modern iterative version of the algorithm, ensuring each permutation is equally likely.
python
import random

def fisher_yates_shuffle(arr):
    for i in range(len(arr) - 1, 0, -1):
        j = random.randint(0, i)
        arr[i], arr[j] = arr[j], arr[i]
To demonstrate usage, consider shuffling a list of integers:
python
my_list = [1, 2, 3, 4, 5]
print("Before shuffle:", my_list)
fisher_yates_shuffle(my_list)
print("After shuffle:", my_list)
This might output something like "Before shuffle: [1, 2, 3, 4, 5]" followed by "After shuffle: [3, 1, 5, 2, 4]", though the result varies due to randomness. The function modifies the input array in place, avoiding additional memory allocation, and employs random.randint(0, i) to select a uniform random index from 0 to i inclusive, which is crucial for maintaining uniformity in the shuffle.

JavaScript Example

The modern iterative version of the Fisher–Yates shuffle is commonly implemented in JavaScript for shuffling arrays in both browser and Node.js environments. This approach modifies the array in place by iterating from the last index to the first, selecting a random index within the remaining unsorted portion, and swapping elements to generate a uniformly random permutation. Here is a standard JavaScript implementation using ES6 features:
javascript
function fisherYatesShuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {
    let j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}
An example of its usage follows, where an of integers is shuffled and logged to the console:
javascript
let arr = [1, 2, 3, 4, 5];
fisherYatesShuffle(arr);
console.log(arr);  // Example output: [3, 1, 5, 2, 4] (actual result varies)
This code relies on Math.random(), which produces a pseudo-random floating-point value in the range [0, 1) that is scaled by (i + 1) and floored with Math.floor() to yield an integer index j from 0 to i inclusive. The element swap employs ES6 destructuring assignment, allowing simultaneous reassignment of array values in a single line without a temporary variable.

Pseudocode Template

The modern iterative version of the Fisher–Yates shuffle is commonly represented in pseudocode as follows, providing a blueprint for generating a uniform of an in O(n) time and O(1) .
algorithm FisherYatesShuffle(A):
    n ← length(A)
    for i ← n-1 downto 1 do
        j ← random integer uniform in [0..i]
        swap A[i] and A[j]
    end for
This template assumes the array A is zero-indexed, with indices running from 0 to n-1, and operates in-place by iteratively swapping elements from the end of the array toward the beginning. The random selection function must generate an j that is uniformly distributed over the inclusive range [0, i] to ensure each has equal probability; any deviation from uniformity in the random source would introduce bias into the shuffle. The swap operation exchanges the values at positions i and j without auxiliary space beyond a few temporary variables. For adaptability to different indexing conventions, the can be adjusted for one-based arrays (indices 1 to n) by modifying the loop to iterate from i = n downto 2, selecting j uniformly from [1, i], and swapping A with A; however, the zero-based form aligns with most contemporary programming languages and libraries.

Variants and Extensions

Sattolo's Algorithm

Sattolo's algorithm, introduced by Sandra Sattolo in 1986, is a variant of the Fisher–Yates shuffle that generates a uniform random of an array of n distinct . Unlike the standard Fisher–Yates , which can produce permutations with multiple s or fixed points, Sattolo's modification ensures the output is a single of length n with no fixed points, forming a structured as one . The algorithm achieves this by altering the random index selection in the iterative swapping process: instead of choosing j uniformly from 0 to i (inclusive), it selects j from 0 to i-1 (exclusive of i), preventing self-swaps that could create fixed points. This simple change guarantees the cycle structure while maintaining computational efficiency, with O(n) and in-place operation. Such cyclic permutations are particularly useful in applications requiring connected cycle structures, such as scheduling to create balanced pairings and simulations in for modeling random traversals or cycles. The for Sattolo's , assuming a 0-indexed a of length n, is:
for i = n-1 downto 1 do
    j = random integer uniformly from 0 to i-1
    swap a[i] and a[j]
This procedure starts with the array in natural order and applies swaps that progressively build the single cycle. Sattolo proved that the algorithm produces each of the (n-1)! possible n-cycles with equal probability 1/(n-1)!, ensuring uniformity over the space of cyclic permutations. An independent correctness proof, based on invariant maintenance of cycle merging, confirms that the process always yields exactly one cycle without fixed points.

Inside-Out Shuffle

The inside-out shuffle is a forward-pass variant of the Fisher–Yates algorithm designed to generate a by randomizing elements as the is constructed or copied from a source. This approach, often referred to as building the permutation incrementally from the beginning, ensures that each prefix of the represents a shuffle of the corresponding initial elements at every step. It is particularly suited for applications where elements are generated or streamed sequentially, such as in simulations requiring progressive randomization without a preliminary filling . In the procedure, the array is initialized empty or with the source elements available for . For each i from 0 to n-1, the next is placed at position i, then a random j is chosen uniformly from 0 to i inclusive, and the elements at i and j are swapped to incorporate the new into the existing shuffled . This method maintains uniformity because the swap position for the new is equally likely among all positions in the current . A common is shuffling while building arrays in online algorithms or simulations, such as card dealing where cards are "dealt" one by one into a by inserting each new at a random position within the current ; the swap operation simulates this insertion efficiently without shifting elements. Another application appears in cryptographic specifications, where it is used to sample random polynomials by iteratively adding coefficients and randomizing their positions to ensure over bounded sets. The advantages of this include its integration of construction and into a single pass, which can reduce overhead in or streaming contexts by avoiding a separate post-filling step, though it still requires allocation of the full size upfront for efficient . Unlike the backward , it supports incremental updates where the full set of elements may not be known in advance, facilitating adaptability in dynamic environments. The following illustrates the inside-out shuffle for an A of length n, assuming elements are filled sequentially before each swap:
for i = 0 to n-1 do
    A[i] ← next element  // from source or generation
    j ← random integer such that 0 ≤ j ≤ i
    swap A[i] ↔ A[j]
This implementation runs in O(n) time and space, preserving the unbiased nature of the original Fisher–Yates method.

Parallel Variants

Parallel variants of the Fisher-Yates shuffle extend the algorithm to multi-core, GPU, or distributed environments, enabling efficient shuffling of large datasets while preserving the of permutations. A common approach involves dividing the into independent chunks, applying the standard Fisher-Yates shuffle to each chunk in parallel, and then merging the results through randomized swaps or selections that ensure global uniformity. This method leverages parallelism in the initial shuffling phase but requires careful merging to avoid introducing , as independent shuffles alone would produce a product distribution rather than a uniform one over all permutations. One influential parallel variant is the MergeShuffle algorithm, proposed by Bacher, Bodini, Hollender, and Lumbroso, which adopts a recursive divide-and-conquer akin to but randomized. The array is split into two roughly equal halves, each recursively shuffled in threads; the halves are then merged by repeatedly selecting elements from either subarray with probabilities proportional to their remaining sizes, using a random bit to decide which subarray contributes the next element. This process guarantees a random and achieves linear expected work with logarithmic parallel depth, significantly outperforming sequential Fisher-Yates for large n on multi-core systems. In distributed settings like , parallel shuffling can partition data across nodes for local Fisher-Yates shuffles, followed by a global all-to-all communication phase to interleave elements randomly, maintaining uniformity through coordinated random selections across partitions. For GPU implementations, variants use parallel generators to compute swap indices concurrently, often in phases where non-conflicting swaps (e.g., on disjoint array segments) are performed simultaneously, followed by atomic operations for resolution; this addresses for massive arrays but demands high-quality parallel RNGs to prevent correlation biases. Key challenges in these variants include synchronizing merges without excessive communication overhead and verifying uniformity under parallel RNG dependencies, as partial shuffles can amplify biases if not perfectly balanced. For instance, overlapping swap phases in multi-threaded implementations allow higher concurrency by scheduling swaps on non-adjacent indices first, reducing contention while preserving the algorithm's correctness. Recent shared-memory shuffles, such as PIpScShuf, achieve O(√(n log n)) parallel depth through batched, in-place operations, demonstrating up to 100x speedups on 64-core systems for n=10^9 elements compared to sequential baselines.

Comparative Analysis

Naïve Swapping Method

The naïve swapping method is a straightforward approach to shuffling an of n elements by performing a fixed number of swaps, typically n iterations. In each iteration, the method iterates over index i from 0 to n-1 and swaps the elements at positions i and a uniformly random index j chosen from 0 to n-1 (allowing for the possibility that j = i, in which case no swap occurs). This method mimics a casual "mixing" process but is fundamentally flawed for generating uniform random permutations. The primary issue with this method is that it does not produce all n! possible with equal probability, leading to a where some are more likely than others. Specifically, involving cycles or transpositions tend to be overrepresented, while others, including the identity , are underrepresented. To illustrate the , consider an of size n=3. Under a , each of the 6 should occur with probability 1/6 ≈ 0.1667. However, the naïve method generates 3^3 = 27 equally likely execution paths (one for each choice of in the three iterations). Enumerating the outcomes shows that the identity (1,2,3) arises in 4 paths, yielding a probability of 4/27 ≈ 0.1481, while three other (1,3,2; 2,1,3; 2,3,1) each arise in 5 paths, yielding 5/27 ≈ 0.1852. This deviation demonstrates the non-uniformity: to compute these values, one lists all 27 sequences of choices, simulates the sequential swaps starting from the sorted , and counts the frequency of each final . This bias persists because each swap affects the entire array without progressively reducing the space of possible arrangements, resulting in uneven coverage of the permutation space. Consequently, the method is inferior to unbiased alternatives like the Fisher–Yates shuffle, as achieving approximate uniformity requires far more than n iterations—on the order of n log n swaps for sufficient mixing—which increases computational cost without guaranteeing exact uniformity.

Sorting-Based Shuffles

Sorting-based shuffles generate a random permutation by assigning a random priority or key to each element in the array and then sorting the array according to these keys. This method relies on the fact that sorting by uniformly random, distinct priorities will produce a uniformly random ordering of the elements with high probability. The approach is particularly straightforward when leveraging built-in sorting functions in programming languages that support key-based sorting. To implement this, one typically creates a list of pairs, where each pair consists of a randomly generated priority and the corresponding original element. The priorities are drawn from a sufficiently large range to minimize the chance of ties; for an array of length n, integers uniformly selected from 1 to n^3 ensure that the probability of all priorities being unique is at least $1 - 1/n. The list is then sorted stably by the priority values, yielding a permuted array in the order of the second components of the pairs. This technique avoids direct swapping and instead uses the sorting algorithm's efficiency for reordering. A practical example in illustrates the simplicity: for an A of length n, construct pairs = [(random.random(), A[i]) for i in range(n)], then shuffled = [pair[1] for pair in sorted(pairs)]. Here, floating-point random numbers are used as keys, which are simple to generate but carry a small of ties due to finite , though this probability is negligible for typical n (on the order of $10^{-15} per pair for 64-bit floats). In languages like , this leverages the sorted function's key parameter, making the implementation concise without needing custom swap logic. While this method offers ease of use, especially in environments with optimized sorting libraries, it trades off efficiency compared to the Fisher–Yates shuffle. Sorting requires O(n \log n) time in the worst case and additional O(n) space to store the pairs or keys, whereas Fisher–Yates achieves O(n) time and constant extra space. The sorting approach may also introduce subtle biases if ties occur and the sort is not stable, potentially affecting uniformity, though careful priority selection mitigates this. Overall, it suits scenarios prioritizing code brevity over performance, such as prototyping or when n is small.

Complexity Evaluation

The Fisher–Yates shuffle exhibits optimal of O(n) for an of n elements, as it executes exactly n-1 swaps, with each swap and random index generation requiring constant time assuming an efficient generator. This operates in place, utilizing O(1) extra space beyond the input . The naïve swapping method, which generates uniformity approximately through k random pairwise swaps (typically with k set to n or larger for better mixing), has a of O(k), or O(n) when k = n, while maintaining O(1) extra space; however, it does not produce a over all permutations unless k grows superlinearly. Sorting-based shuffles, which assign independent uniform random keys to each element and sort the array by those keys, incur O(n log n) time complexity dominated by the comparison sort, along with O(n) extra space to store the random keys or auxiliary structures.
MethodTime ComplexitySpace ComplexityUniformity
Fisher–YatesO(n)O(1)Yes
Naïve SwappingO(n) (for k=n)O(1)No
Sorting-BasedO(n log n)O(n)Yes

Bias and Correctness Issues

Common Implementation Errors

A common implementation error in the Fisher–Yates shuffle arises from off-by-one mistakes in the loop bounds or random index generation, which can lead to biased permutations by incorrectly excluding or including certain elements. For instance, in the modern descending loop variant using 0-based indexing (for i from n-1 down to 1), the random index j should be chosen uniformly from 0 to i inclusive; a frequent off-by-one error is selecting j from 0 to i-1, which excludes the possibility of swapping an element with itself and biases the distribution by making certain permutations impossible. Confusion between zero-based and one-based indexing can exacerbate these issues, especially when adapting the original 1-based formulation into programming languages that use zero-based arrays. This mismatch often results in the random selection excluding the upper end of the range (e.g., not including i), ing the shuffle and failing to produce uniform randomness. Another frequent bug involves incorrect loop bounds, such as including i=0 in the descending loop or extending the ascending loop to n-1 without adjustment, which can force a self-swap on the last element or leave it fixed, violating uniformity. Self-swaps (j = i) are necessary and do not introduce ; excluding them (e.g., via j < i) is the error that skews results. An illustrative flawed code snippet in pseudocode for the ascending variant might look like:
for i from 0 to length-1 do
    j ← random(0, i)  // Incorrect: should be random(i, length-1); excludes later elements and biases low indices
    swap array[i] with array[j]
This excludes positions beyond i, leading to incomplete mixing. A correct ascending 0-based version loops i from 0 to n-2 and selects j from i to n-1. To mitigate these errors, implementations should use inclusive random ranges matching the chosen variant (e.g., 0 to i in descending) and test with small arrays (e.g., n = 3 or 4) to verify all n! permutations occur equally, revealing biases without advanced analysis.

Modulo Bias in Random Selection

In implementations of the Fisher–Yates shuffle, selecting a uniform random index j from 0 to i (where the range size is n = i+1) often relies on a pseudorandom number generator (PRNG) that produces integers uniformly distributed from 0 to M, inclusive. If M+1 is not a multiple of n, the common practice of computing j = rand() \% n introduces modulo bias, where lower values of j are selected with higher probability than higher values. This arises because the division of the PRNG's output range into n residue classes results in some classes containing more integers than others. Consider an example where the PRNG outputs values from 0 to 10 (M=10, 11 possible outcomes) and n=5 (for i=4). The residues modulo 5 are: 0 appears for inputs 0, 5, 10 (3 times); 1 for 1, 6 (2 times); 2 for 2, 7 (2 times); 3 for 3, 8 (2 times); and 4 for 4, 9 (2 times). Consequently, the probability P(j=0) = 3/11 \approx 0.2727, while P(j=1) = P(j=2) = P(j=3) = P(j=4) = 2/11 \approx 0.1818. This uneven distribution favors smaller indices, potentially leading to biased permutations where certain elements are less likely to end up in higher positions. In general, for a PRNG range of 0 to M and desired range n, let q = \lfloor (M+1)/n \rfloor and r = (M+1) \mod n. The biased probabilities are P(j=k) = (q+1)/(M+1) for k = 0 to r-1, and P(j=k) = q/(M+1) for k = r to n-1. When r > 0, the first r outcomes have a higher probability, amplifying the as n grows relative to M+1. This issue is well-recognized in for algorithms like the shuffle, as discussed in foundational texts on computational algorithms. To mitigate modulo bias and ensure uniformity, is the standard approach: generate r from the PRNG, compute the threshold t = n \cdot q, and if r < t, accept j = r \mod n; otherwise, discard r and regenerate. This method produces exactly uniform selections, with the expected number of generations per selection being (M+1)/t \leq 2, making it efficient when the PRNG range significantly exceeds n. Modern libraries often incorporate this technique internally for shuffle functions to guarantee correctness.

Pseudorandom Generator Dependencies

The Fisher–Yates shuffle relies on a (PRNG) that produces independent and uniformly distributed integers in the required range for each selection step, ensuring the output is uniformly random across all possible arrangements. If the PRNG fails to meet these criteria—such as by generating numbers with detectable correlations, short periods, or non-uniform distributions—the shuffle will exhibit , favoring certain permutations over others. Poor-quality PRNGs, particularly linear congruential generators (LCGs) with inadequate parameters like small moduli or multipliers that amplify low-bit correlations, can introduce periodic patterns or dependencies in the sequence of random indices selected during the shuffle. For instance, early implementations of the rand() function libraries often employed flawed LCGs with periods as short as $2^{31} and poor in lower bits, leading to clustered or predictable swaps that undermine shuffle uniformity. In contrast, high-quality options like the (MT19937), which offers a of $2^{19937} - 1 and excellent equidistribution across dimensions up to 623, are recommended for general-purpose to avoid such issues. For security-sensitive applications, cryptographically secure PRNGs, such as those based on hardware entropy sources, should be used to prevent predictability. The impact of a subpar PRNG manifests as non-uniform permutation distributions, where some arrangements occur more frequently than expected under true randomness, potentially skewing results in simulations, games, or statistical sampling. This bias can be empirically detected through statistical tests, such as the chi-squared goodness-of-fit test applied to the observed frequencies of element positions or full permutations across multiple shuffles, where deviations from the expected uniform distribution yield high test statistics indicating poor randomness. To mitigate these risks, developers should prioritize language-standard, vetted PRNGs like the Mersenne Twister implementations in libraries such as Python's random module or Java's java.util.Random, and eschew custom or outdated generators that lack rigorous statistical validation.

References

  1. [1]
    Algorithm 235: Random permutation | Communications of the ACM
    Algorithm 235: Random permutation. Author: Richard Durstenfeld. Richard ... Published: 01 July 1964 Publication History. 332citation4,654Downloads. Metrics.
  2. [2]
    Statistical Tables for Biological Agricultural and Medical Research
    Statistical Tables for Biological Agricultural and Medical Research by Prof. RA Fisher F. Yates. Pp. viii + 90. (London and Edinburgh: Oliver and Boyd, 1938.)
  3. [3]
    Statistical Tables for Biological, Agricultural and Medical Research ...
    Statistical Tables for Biological, Agricultural and Medical Research. By R. A. Fisher and F. Yates. London and Edinburgh: Oliver and Boyd. 1938.
  4. [4]
    O'Connor -- A Historical Note on Shuffle Algorithms - Academia.edu
    Durstenfeld's Random Permutation algorithm is the first optimal O(n) shuffle generator published in 1964. The Fisher-Yates Shuffle algorithm is not optimal ...
  5. [5]
    [PDF] Fisher-Yates shuffle - Semantic Scholar
    This work defines and proves the correctness of the Fisher–Yates shuffle [1, 2, 3] for shuffling – i. e. producing a random permutation – of a list. The ...
  6. [6]
    Fisher-Yates Shuffle Algorithm Application to Develop a ...
    The Fisher-Yates Shuffle algorithm is a part of the Shuffle Algorithm. This algorithm randomizes the C array of x elements to produce a permutation [23].
  7. [7]
    Statistical Tables for Biological, Agricultural and Medical Research
    Jan 18, 2019 · Title, Statistical Tables for Biological, Agricultural and Medical Research ; Authors, Sir Ronald Aylmer Fisher, Frank Yates ; Edition, 3.
  8. [8]
    The outstanding scientist, R.A. Fisher: his views on eugenics and race
    Jan 15, 2021 · Fisher RA, Yates F (1938) Statistical Tables for Biological, Agricultural and Medical Research. Oliver and Boyd: Edinburgh, UK. Galton F ...Missing: shuffle | Show results with:shuffle
  9. [9]
    [PDF] A Simulated Enhancement of Fisher-Yates Algorithm for Shuffling in ...
    THE ORIGINAL FYS. The original FYS was presented in 1938 by Fisher and Yates. [5] in the pages of their text “Statistical tables for biological, agricultural ...
  10. [10]
    History of the Statistical Design of Agricultural Experiments - jstor
    Fisher and Yates published in 1938 in Table XVII, XVIII, and Table XIX of their book. Statistical tables for biological, agricultural and medical research the ...Missing: shuffle original
  11. [11]
    prngCDAR17 slides - UC Berkeley Statistics Department
    Proof that the streaming Fisher-Yates-Knuth-Durstenfeld algorithm works¶. Induction: For i=1, obvious. At stage i, suppose all i! permutations are equally ...
  12. [12]
  13. [13]
    The Art of Computer Programming (TAOCP)
    This PDF includes the complete indexes of Volumes 1, 2, 3, 4A, and 4B, as well as the index to Volume 1 Fascicle 1. Registered owners of the earlier four-volume ...
  14. [14]
  15. [15]
    The art of computer programming, volume 2 (3rd ed.)
    The art of computer programming, volume 2 (3rd ed.): seminumerical algorithmsNovember 1997. Author: Author Picture Donald E. Knuth. Stanford Univ., Stanford ...
  16. [16]
    Math.random() - JavaScript - MDN Web Docs
    Jul 10, 2025 · The Math.random() static method returns a floating-point, pseudo-random number that's greater than or equal to 0 and less than 1, with approximately uniform ...Try it · Examples
  17. [17]
    Destructuring - JavaScript - MDN Web Docs - Mozilla
    Jul 8, 2025 · The destructuring syntax is a JavaScript syntax that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.Try it · Syntax · Description · Examples
  18. [18]
    The art of computer programming : Knuth, Donald Ervin, 1938
    Jul 1, 2021 · The art of computer programming. 2 v. : 25 cm. Includes bibliographical references and indexes. v. 1. Fundamental algorithms -- v. 2. Seminumerical algorithms.
  19. [19]
  20. [20]
    [PDF] Generating a - Random Cyclic Permutation° - David Gries, Jinyun Xue¹
    We present Sattolo's algorithm and then argue about its correctness. 1. This research was supported by the NSF under grant DCR-8320274. Visiting Cornell from ...Missing: original | Show results with:original
  21. [21]
    [PDF] Random and exhaustive generation of permutations and cycles
    ... Fisher-Yates shuffle” or the “Knuth shuffle”. S. Sattolo [Satt1986] introduced a very similar algorithm for uniform random generation of an element of Cn ...<|control11|><|separator|>
  22. [22]
    [PDF] STABILIZER: Statistically Sound Performance Evaluation
    The process for malloc and free is equivalent to one iteration of the inside-out Fisher-Yates shuffle. Figure 1 illustrates this procedure. STABILIZER uses ...
  23. [23]
    [PDF] Data Structures and Algorithms - Shuffling an Array - LRDE (Epita)
    This version of the algorithm will shuffle the array as it is being copied. Input: an array A. Output: a shuffled copy of A. INSIDEOUTFISHERYATESSHUFFLE(A). 1 n ...
  24. [24]
    [PDF] CRYSTALS-Dilithium
    Feb 8, 2021 · The algorithm we will use to create a random element in Bτ is sometimes referred to as an “inside-out ... The Art of Computer Programming, volume ...
  25. [25]
    MergeShuffle: A Very Fast, Parallel Random Permutation Algorithm
    Aug 13, 2015 · This article introduces an algorithm, MergeShuffle, which is an extremely efficient algorithm to generate random permutations (or to randomly permute an ...
  26. [26]
    [2106.06161] Bandwidth-Optimal Random Shuffling for GPUs - arXiv
    Jun 11, 2021 · We provide a method of generating pseudo-random permutations in parallel by fusing suitable pseudo-random bijective functions with stream compaction operations.Missing: MapReduce | Show results with:MapReduce
  27. [27]
    Calculating probabilities for the naive Fisher-Yates shuffling algorithm
    Oct 1, 2025 · So, the probability to get the identity permutation using the naive algorithm is 4/27. All assuming, I found all the correct choices of j0,j ...
  28. [28]
    The Danger of Naïveté - Coding Horror
    Dec 7, 2007 · Let's take a look at the correct Knuth-Fisher-Yates shuffle algorithm. ... Knuth credits Moses and Oakford (1963) and Durstenfeld (1964).Missing: adoption | Show results with:adoption
  29. [29]
    Complexity of the Fisher-Yates Shuffle Algorithm
    Aug 16, 2010 · This question is in regard to the Fisher-Yates algorithm for returning a random shuffle of a given array. The Wikipedia page says that its complexity is O(n).
  30. [30]
    Verifying the Fisher-Yates Shuffle Algorithm in Dafny - arXiv
    Jan 10, 2025 · Abstract:The Fisher-Yates shuffle is a well-known algorithm for shuffling a finite sequence, such that every permutation is equally likely.Missing: original | Show results with:original<|separator|>
  31. [31]
    Zero Tolerance for Bias - ACM Queue
    May 29, 2024 · 22. O'Connor, D. 2014. A historical note on shuffle algorithms. Academia; https://www.academia.edu/1205620/ ...
  32. [32]
    (PDF) Comparison of Fisher-Yates Shuffle and Linear Congruent ...
    Aug 7, 2025 · The results show that the Chi-Square value for the Fisher-Yates algorithm is 3.8 and for the Linear Congruent algorithm is 4.3, both of which ...Missing: bad PRNG uniformity
  33. [33]
    Batched Ranged Random Integer Generation
    Demonstrates an efficient method for batching ranged random integer generation to improve the performance of shuffling algorithms such as the Fisher-Yates shuffle.