The pivot: where the quadratic returns
Compare with your guess from step 2.
Shuffled data — all fine
$ algo dc -in s10k.jsonl -order shuffled
n = 10000, order = shuffled
algorithm comparisons moves wall
merge 120440 133616 7.5ms
quick (first pivot) 149913 75946 2.005ms
quick (median-of-3) 146520 78217 2.999ms
quick (random) 156381 89939 2.499ms
All four at ~120–156 thousand comparisons. In lesson 7 the same slice cost insertion sort 24,893,296. That is a 200× difference, and it is the whole reason this lesson exists.
Quicksort makes more comparisons than merge sort and is still faster: it works in place, so it moves less data (75,946 against 133,616).
Sorted data — and there it is
$ algo dc -in s10k.jsonl -order sorted
n = 10000, order = sorted
algorithm comparisons moves wall
merge 64608 133616 6.583ms
quick (first pivot) 49995000 9999 325.219ms
quick (median-of-3) 131343 70826 1ms
quick (random) 159057 89759 2.008ms
49,995,000. Up from 149,913.
That is 333× more comparisons and 162× longer — purely because the data was already tidy.
And the number is familiar: 49,995,000 = n(n−1)/2. Exactly what lesson 7's bubble and selection sorts cost. A quicksort with a first-element pivot, on sorted input, is an elementary sort.
The same -sorted flag that gave insertion sort its best case in lesson 7 gives
quicksort its worst here. That is the second of the three payoffs lesson 1
promised.
Merge sort, meanwhile, has an easier time: 64,608 instead of 120,440. It has no bad case — and the test checks that on all three orders.
Fixing the pivot — and where it is not enough
Median-of-three rescues sorted input completely: 131,343 instead of 49,995,000. Three hundred and eighty times better, and even slightly less than on shuffled data.
But run it on reverse-sorted input:
$ algo dc -in s10k.jsonl -order reverse
algorithm comparisons moves wall
merge 69008 133616 5.004ms
quick (first pivot) 49995000 25009999 506.951ms
quick (median-of-3) 8379139 4187234 94.662ms
quick (random) 151569 87173 2.083ms
Median-of-three: 8,379,139. Not 50 million, but not 146 thousand either — 57× more than on shuffled data.
Is it still quadratic growth? Measure at two sizes:
| n | median-of-three, reverse | ratio |
|---|---|---|
| 1,000 | 86,890 | — |
| 10,000 | 8,379,139 | ×96 |
Ten times the n, a hundred times the work. Yes, still quadratic — just with a constant about six times smaller.
The random pivot over the same range: 10,837 → 151,569, a ×14 — which is what O(n log n) growth actually looks like.
Median-of-three fixes the case you thought to check. A random pivot fixes the ones you did not.
This result depends on the partitioning scheme — this code uses a Lomuto-style
Partition. With a different one the numbers differ. It is not a general claim
about median-of-three; it is a measurement of this code, and that is exactly
what makes it interesting: a fix that looks general turns out to be entangled
with the implementation. The drills ask you to find out why.