Selection Sort
A shuffling algorithm aims to put the elements of an array into a random order, with the typical case being shuffling a deck of cards.
Try it: Selection Sort
How selection sort repeatedly finds the smallest (or largest) remaining value and swaps it into the next position.
How it works
- Scan the unsorted part and remember the position of the smallest value.
- Swap it into the first unsorted position.
- That position is now final; repeat on the rest.
- Always n(n−1)/2 comparisons, at most n−1 swaps; not stable.
Default run (27 steps): Selection sort, ascending: [8, 3, 5, 1, 9, 2]. … Sorted ascending: [1, 2, 3, 5, 8, 9] — 15 comparisons, 5 swaps.
Simplified: Up to 12 numbers from 0 to 99. The comparison table runs all five algorithms on the same input. Python's own sort() uses Timsort.
Loading the simulation…
A Deck That Must Come Out Unpredictable
Most of the work in this chapter has been about putting the elements of an array into a particular order, such as smallest to largest. This article turns that goal around. Instead of asking for a specific order, we ask how to put the elements into a random order. This problem is much less common than sorting, but it is a useful companion to it, and it is worth studying because it reuses a familiar structure: selection sort.
The standard picture is shuffling a deck of cards. A shuffled deck is one in which no card's position can be predicted from the order it started in. Keep that picture in mind. The array plays the role of the deck, and the algorithm's job is to rearrange it so that the final order is unpredictable. Selection sort, by contrast, is the procedure that finds the smallest remaining item and places it at the front of the unfinished part, and it is the structure we will trace throughout this article.
What do you think happens?
Before reading on: if a procedure always picks the item at the last unfinished position instead of a random one, does the result come out unpredictable?
Reveal answer
No. Removing the randomness from the choice turns the process into a fixed rearrangement, so the result is fully determined by the starting order.
The source points out that randomness enters at a single point, the random pick. Remove that pick and the same input always yields the same output, which is no longer a shuffle.
Tracing a Shuffle Step by Step
A shuffle works in rounds. At the start the whole array is unfinished. In each round one item is chosen at random from the unfinished part and moved to the end of that part. The unfinished part is then treated as one element shorter, and the process repeats on what remains. The loop stops when no unfinished part is left to process.
The source introduces a shuffle subroutine for an array, but the code itself is not reproduced in the material this article draws on. The illustration below is written to match the description: it works from the end of the array toward the front, picks a random position from the unfinished part, and swaps that item into place. Read it as a faithful rendering of the idea rather than the textbook's exact code.
The printed order varies from run to run, because the pick is random. For example, one run might print [9, 4, 7, 3].range(len(items) - 1, 0, -1)walks from the last unfinished position toward the frontrandom.randint(0, last)is the single point where randomness enters- The swap is what actually moves the chosen item into the slot at the end of the unfinished part
Because the choice is random, two runs on the same array will usually produce different orders. That is the point of the exercise, and it is also why the function cannot be tested by checking one fixed output. A test instead checks properties, such as whether the result contains exactly the same items as the input.
One fixed run of the shuffle
Shuffle [7, 3, 9, 4] with the picks fixed at 1, then 0, then 1, so the trace can be followed exactly.
- 1Round 1: last index is 3
The unfinished part is indices 0 through 3. The pick is index 1, which holds 3. Swap index 1 with index 3: the array becomes [7, 4, 9, 3]. Index 3 is now finished.
- 2Round 2: last index is 2
The unfinished part is indices 0 through 2. The pick is index 0, which holds 7. Swap index 0 with index 2: the array becomes [9, 4, 7, 3]. Index 2 is now finished.
- 3Round 3: last index is 1
The unfinished part is indices 0 and 1. The pick is index 1, which is the item itself. The swap changes nothing, and the array stays [9, 4, 7, 3].
- 4Stop
Only index 0 remains, and no further rounds are needed. The final array is [9, 4, 7, 3].
The original items 7, 3, 9, 4 now appear in the order 9, 4, 7, 3. Every original item is still present, which is the property a correct shuffle must keep.
Every cell in this final state differs from the starting array [7, 3, 9, 4]. That is a coincidence of this particular trace, not a rule. A random pick can land on the item itself, as it did in round 3, and then that slot keeps its value. The rule that matters is that each pick comes only from the unfinished positions, and that each chosen item is actually swapped into place.
Where Randomness Enters the Loop
The control flow of the shuffle is short. At the top of the loop there is a decision: is any unfinished part left? If yes, the algorithm performs the pick, the swap, and the shrinking of the unfinished part, then returns to the decision. If no, the shuffle is done. The decision is deterministic. The pick is the only step that depends on chance, and it is the only place where randomness enters.
The phrase at random is the whole difference from selection sort. In selection sort, the choice is determined by the values in the array, because the largest or smallest remaining item is always the one picked. In a shuffle, the value of the item plays no role in the choice. The algorithm picks a position by chance, and whatever sits there is moved to the end of the unfinished part.
| Question | Selection sort | Shuffle |
|---|---|---|
| Goal | A specific order, such as smallest to largest | A random, unpredictable order |
| How the item is chosen | By its value, such as the smallest remaining item | By a random position in the unfinished part |
| Where randomness enters | Nowhere; the result is fixed by the values | At the single random pick |
| Shared skeleton | Repeatedly work on the unfinished part and place one item at its boundary | The same skeleton, with the choice rule changed |
Shuffling and sorting use the same structure but aim at opposite kinds of output.
The Two-Region Picture of Selection Sort
The easiest way to picture selection sort is to split the list into two regions. The front region holds values that are already in their final positions. The back region holds everything not yet placed. Each pass looks only at the back region, finds its smallest value, and moves that value to the first position of the back region. That position then becomes part of the front region, so the boundary moves one step to the right. When the back region has only one item left, the whole list is in order.
Selection sort repeatedly finds the smallest remaining item in the unsorted region and places it at the boundary, so the sorted region grows by one position per pass.
The source describes the same idea with the largest item moving to the end of an increasing list, since that is where the largest value belongs. The mirror-image version, used here, moves the smallest remaining item to the front. Both follow the same reasoning: select the next item that belongs in place, put it there, and repeat the same choice on what is left.
Tracing Selection Sort Through Its Passes
What do you think happens?
Start with [5, 2, 8, 1]. The first pass finds the smallest value, 1, and moves it to the front. What is the list after the first pass?
Reveal answer
[1, 2, 8, 5]
The 1 at index 3 swaps with the 5 at index 0. The 2 and the 8 do not move, and the 5 takes the 1's old place.
[1, 2, 5, 8]- boundary marks the first position of the unsorted back region
- The inner loop is the search step: it records the index of the smallest value in the back region
- The swap is the placement step: it moves that smallest value to the boundary
Three passes over [5, 2, 8, 1]
Sort [5, 2, 8, 1] in increasing order by repeatedly finding the smallest remaining item and placing it at the front of the unsorted part.
- 1Pass 1: boundary at index 0
The back region is the whole list. The smallest value is 1 at index 3. Swap index 0 and index 3: the list becomes [1, 2, 8, 5]. The front region is now [1].
- 2Pass 2: boundary at index 1
The back region is [2, 8, 5]. The smallest value is 2, already at index 1. The swap changes nothing, and the list stays [1, 2, 8, 5]. The front region is now [1, 2].
- 3Pass 3: boundary at index 2
The back region is [8, 5]. The smallest value is 5 at index 3. Swap index 2 and index 3: the list becomes [1, 2, 5, 8].
- 4Stop
Only one item remains in the back region, so the whole list is in order.
The final list is [1, 2, 5, 8]. Pass 1 changed two cells, pass 2 changed none, and pass 3 changed two cells.
Notice that pass 2 does not change the list at all. That is normal: the smallest value of the back region was already at the boundary. The second and third passes are still needed, because the 5 and the 8 are not settled until the back region has been reduced to one item.
Card Sorting With a New List
The source applies the same idea to a hand of playing cards. Each Card object offers getSuit() and getValue() methods, and the hand is stored in an ArrayList of Card. The method described does not swap cards inside the original list. Instead it creates a second list, repeatedly selects the smallest remaining card from the old list, removes that card from the old list, and adds it to the new list. When the old list is empty, the new list becomes the hand. The source notes that this is not the most efficient procedure, but a hand of cards is small enough that the inefficiency is negligible.
- 1Look through the old list and find the card that is smallest under the comparison rule.
- 2Remove that card from the old list.
- 3Add that card to the end of the new list.
- 4Repeat until the old list is empty, then treat the new list as the sorted hand.
The comparison rule is the part that most often confuses readers. Cards of the same suit are grouped together, and within a suit they are ordered by value. A card c1 replaces the current minimum c when its suit is smaller, or when its suit is equal and its value is smaller. The source also treats aces as the lowest value, 1. This is a two-key comparison: suit is checked first, and value is used only as a tie-breaker when the suits match.
Mistakes That Break Shuffles and Sorts
Common mistake Picking from positions that are already finished
What it looks likeIn a shuffle, the pick ranges over the whole array on every round, including the slots already placed at the end.
Why it failsA correct shuffle only chooses from unfinished positions. Re-picking finished slots can move items that should stay fixed and breaks the round structure.
Do this insteadLimit the random range to the unfinished part, which shrinks by one each round.
Common mistake Choosing a random item but never performing the swap
What it looks likeThe code computes pick = random.randint(0, last) but then does not exchange items[last] with items[pick].
Why it failsThe chosen item is not moved, so the array keeps its original order and the shuffle does nothing useful.
Do this insteadFor each chosen item, actually swap it into the slot at the end of the unfinished part.
Common mistake Testing a shuffle against one expected output
What it looks likeA test asserts that shuffle([7, 3, 9, 4]) returns [9, 4, 7, 3].Why it failsThe result is random, so a fixed expected output is only one of many valid results and the test will fail on other correct runs.
Do this insteadTest properties instead, such as that the result contains exactly the same items as the input.
Common mistake Off-by-one error in the selection boundary
What it looks likeThe search starts at boundary instead of boundary + 1 and the minimum is compared with itself in a confusing way, or the loop runs past the last position.
Why it failsThe search step records the wrong index, so the placement step moves the wrong value to the front.
Do this insteadSearch only the back region, which starts at boundary + 1, and place the result at boundary.
Common mistake Comparing only values when the rule is two-key, such as suit then value, which puts cards in the wrong order
When a finished list is wrong, do not start by rereading the whole algorithm. Trace the list pass by pass and check the boundary after each one. The first pass that produces a value you did not expect is the location of the divergence, and the mistake is almost always in that pass's search step or swap step.
Before running code, write down the list you expect after the first pass. Then run the code and compare. The points where your prediction and the real output disagree are exactly where debugging should start.
Tracing a New List on Your Own
Trace selection sort on the list [4, 9, 2, 6]. Write the list after each pass, marking which part is the settled front region and which is the unsorted back region. State the pass where the list first stops changing.
Hints
- Pass 1 looks at the whole list and finds the smallest value, 2, at index 2.
- After pass 1 the front region has one item, so pass 2 searches indices 1 through 3.
- The list stops changing for a pass when the smallest value of the back region is already at the boundary.
Key Takeaways
- A shuffle aims for a random, unpredictable order, while selection sort aims for a specific order such as smallest to largest.
- A shuffle reuses the selection sort skeleton: it works on the unfinished part, chooses one item, and places it at the end of that part. Randomness enters only at the pick.
- Selection sort grows a sorted front region by one position per pass by finding the smallest remaining item and swapping it to the boundary.
- The card-sorting method in the source builds a new list and compares by suit first, using value only as a tie-breaker.
- When a trace goes wrong, find the first pass whose result differs from your prediction; the error is usually in that pass's search or swap.