Counting

The questions a sort asks twice

Selection sort makes 32,640 comparisons on 256 elements and 19,561 of them have answers it already holds. Remove every one and it still makes 7.8 times the information floor, because a question can be new and nearly worthless: insertion sort repeats nothing at all and removes 0.72 of a bit per comparison where merge sort removes 0.96. And bubble sort, less its repeats, makes exactly insertion sort's comparisons — at every size.

The floor under every comparison sort is an argument about counting leaves: the answer is one of n!n! orderings, each comparison has two outcomes, so no algorithm averages fewer than log2n!\log_2 n! comparisons. On 256 elements that is 1,684 comparisons, and selection sort makes 32,640.

The usual account of that gap is asymptotic — one algorithm is quadratic and the other is not — and it explains nothing about what the extra comparisons are doing. This page opens them.

A sort’s comparisons build a partial order, and the partial order’s transitive closure answers more questions than were asked. If a sort has established a<ba < b and b<cb < c, it already knows a<ca < c, and a comparison between them can only confirm it. Such a comparison eliminates no ordering at all. It is, in the exact sense the floor is about, free of information.

How many are already answered

The closure is maintained exactly while each sort runs, as bitsets of predecessors and successors, and the sort is handed a comparator that consults it. Nothing about the algorithm changes: a comparison sort learns about its input only through the comparator, so replacing the comparator is the one intervention that leaves the algorithm what it was.

5 of these 10 sorts never ask a question they have already answered, and selection sort asks 60% of its questions twiceThe share of each sort's comparisons whose answer followed from its earlier comparisons, on 256 elements in random order. A comparison whose answer is already implied eliminates no ordering and carries no information. Merge sort: 1,729 comparisons, 0 of them already answered (0.0%), leaving 1,729 — which is 1.03 times the information floor of 1,684. Quicksort, random pivot: 1,956 comparisons, 0 of them already answered (0.0%), leaving 1,956 — which is 1.16 times the information floor of 1,684. Quicksort, first-element pivot: 1,995 comparisons, 0 of them already answered (0.0%), leaving 1,995 — which is 1.18 times the information floor of 1,684. Merge sort with an insertion cutoff: 2,209 comparisons, 0 of them already answered (0.0%), leaving 2,209 — which is 1.31 times the information floor of 1,684. Insertion sort: 16,552 comparisons, 0 of them already answered (0.0%), leaving 16,552 — which is 9.83 times the information floor of 1,684. Shellsort: 2,452 comparisons, 240 of them already answered (9.8%), leaving 2,212 — which is 1.31 times the information floor of 1,684. Quicksort, median-of-three: 2,357 comparisons, 363 of them already answered (15.4%), leaving 1,994 — which is 1.18 times the information floor of 1,684. Heapsort: 3,316 comparisons, 868 of them already answered (26.2%), leaving 2,448 — which is 1.45 times the information floor of 1,684. Bubble sort: 32,585 comparisons, 16,033 of them already answered (49.2%), leaving 16,552 — which is 9.83 times the information floor of 1,684. Selection sort: 32,640 comparisons, 19,561 of them already answered (59.9%), leaving 13,079 — which is 7.77 times the information floor of 1,684.0%25%50%75%Merge sort0.0% · 1.03× the floorQuicksort, random pivot0.0% · 1.16× the floorQuicksort, first-element pivot0.0% · 1.18× the floorMerge sort with an insertion cutoff0.0% · 1.31× the floorInsertion sort0.0% · 9.83× the floorShellsort9.8% · 1.31× the floorQuicksort, median-of-three15.4% · 1.18× the floorHeapsort26.2% · 1.45× the floorBubble sort49.2% · 9.83× the floorSelection sort59.9% · 7.77× the floorshare of comparisons whose answer was already implied256 elements, random orderlight: repeats nothing
Fig. 1 The share of each sort’s comparisons whose answer its earlier comparisons had already established, on 256 elements in random order. Merge sort, both quicksorts with an unbiased pivot, the merge-and-insertion hybrid and insertion sort: none at all. Shellsort 9.8%, heapsort 26.2%, median-of-three quicksort 15.4%, bubble sort 49.2%, selection sort 59.9%. The right-hand column is what each sort costs once the repeats are removed, against the information floor.

Five of the ten never ask a question they have already answered, and the division does not follow the complexity classes. Insertion sort is quadratic and repeats nothing; heapsort is linearithmic and repeats a quarter of its questions.

The two quadratic sorts that do repeat are the page’s easiest finding. Selection sort scans the remaining range for its minimum on every pass, and the second pass re-compares elements whose relative order the first pass established — 19,561 comparisons of 32,640. Bubble sort’s later passes re-compare adjacent pairs it has already settled: 16,033 of 32,585.

What removing them would buy

Not enough, and that is the page’s real argument.

Strip every repeated question from selection sort and 13,079 comparisons remain — still 7.8 times the floor. Strip them from bubble sort and 16,552 remain, 9.8 times the floor. A hypothetical selection sort with perfect memory would be no better than a bad linearithmic sort.

So redundancy is not where a quadratic sort’s cost is. It is a large share of it and it is not the mechanism.

The arithmetic is worth doing slowly, because the share and the ratio pull in opposite directions and it is easy to read the larger of the two as the more serious. Selection sort’s 59.9% is the bigger-looking number and it accounts for a factor of 2.5. Its remaining comparisons are the smaller-looking number and they account for a factor of 7.8. A defect that explains a quarter of a gap is not the explanation of the gap, however large its percentage looks written down, and the only reason the percentage looks like the explanation is that nothing else on the page had been measured yet.

Bubble sort's comparisons less the ones it had already answered are exactly insertion sort's, at every size: 304, 1,190, 4,374, 16,552, 63,990Comparisons against the number of elements, on logarithmic axes, for bubble sort as it runs, bubble sort with its repeated questions removed, and insertion sort. The second and third are the same number at every size drawn. 32 elements: bubble sort 495, of which 191 repeat a question, leaving 304 — and insertion sort makes 304. 64 elements: bubble sort 2,013, of which 823 repeat a question, leaving 1,190 — and insertion sort makes 1,190. 128 elements: bubble sort 8,113, of which 3,739 repeat a question, leaving 4,374 — and insertion sort makes 4,374. 256 elements: bubble sort 32,585, of which 16,033 repeat a question, leaving 16,552 — and insertion sort makes 16,552. 512 elements: bubble sort 130,606, of which 66,616 repeat a question, leaving 63,990 — and insertion sort makes 63,990.326412825651210³10⁴10⁵elementscomparisonsbubble sort, as it runsbubble sort without its repeats —and insertion sortrandom arrangementstwo lines, and the lower is both
Fig. 2 Comparisons against the number of elements, on logarithmic axes: bubble sort as it runs, bubble sort with its repeated questions removed, and insertion sort. The lower line is both of the last two. At 32, 64, 128, 256 and 512 elements bubble sort’s non-repeating comparisons number 304, 1,190, 4,374, 16,552 and 63,990 — and insertion sort makes exactly those numbers.

That identity is exact at every size drawn and it is not a coincidence. Both sorts learn the order by comparing adjacent elements and moving the smaller one left, so the set of new facts each establishes is the same set: one comparison for each inversion, plus one for each place the scan stops. Insertion sort asks each of those once. Bubble sort asks each of them once and then asks a great many of them again, because it has no record of which pairs it has settled.

The phrase “new facts” is doing work there and can be made precise. A comparison is new when the pair it names is incomparable in the closure of everything asked so far, and for a sort that only ever compares neighbours in the current arrangement the incomparable neighbouring pairs are exactly the ones still out of order, plus the pair at which a scan stops. Insertion sort’s inner loop walks left until it finds a key no larger, so it asks one question per inversion crossed and one to stop; bubble sort’s pass does the same thing across the whole array, discards what it learned at the end of the pass, and begins again.

So bubble sort is insertion sort with amnesia, in a sense that can be measured rather than gestured at, and curing the amnesia would make it insertion sort exactly — which is still quadratic.

The other kind of waste

If removing every repeat leaves insertion sort’s cost, and insertion sort repeats nothing, then insertion sort’s cost is not redundancy. It is something else, and the something else can be measured too.

The orderings still consistent with what a sort knows are the linear extensions of the partial order its comparisons have built, and they can be counted exactly by a walk over subsets. A comparison’s information is then the logarithm of the ratio between the counts before and after it: a comparison that halves the space carries one bit, a repeated one carries zero, and most carry something in between.

A comparison can remove at most one ordering in two, and these remove between 0.51 and 0.96 of a bit eachBits removed per comparison, averaged over 24 arrangements of 9 elements, for each sort. The bits are exact: the orderings still consistent with what a sort knows are the linear extensions of the partial order its comparisons have built, counted by a walk over subsets, and a comparison's information is the logarithm of the ratio before and after. Merge sort: 0.963 bits a comparison over 19.3 comparisons, 0.0 of which repeated a question. Quicksort, first-element pivot: 0.910 bits a comparison over 20.7 comparisons, 0.0 of which repeated a question. Quicksort, random pivot: 0.871 bits a comparison over 22.0 comparisons, 0.0 of which repeated a question. Shellsort: 0.855 bits a comparison over 22.1 comparisons, 1.8 of which repeated a question. Merge sort with an insertion cutoff: 0.716 bits a comparison over 26.4 comparisons, 0.0 of which repeated a question. Insertion sort: 0.716 bits a comparison over 26.4 comparisons, 0.0 of which repeated a question. Quicksort, median-of-three: 0.611 bits a comparison over 30.4 comparisons, 11.0 of which repeated a question. Heapsort: 0.582 bits a comparison over 31.9 comparisons, 11.9 of which repeated a question. Bubble sort: 0.544 bits a comparison over 34.2 comparisons, 7.8 of which repeated a question. Selection sort: 0.513 bits a comparison over 36.0 comparisons, 14.5 of which repeated a question.0.000.250.500.751.00Merge sort0.96 · 0.0 repeatsQuicksort, first-element pivot0.91 · 0.0 repeatsQuicksort, random pivot0.87 · 0.0 repeatsShellsort0.86 · 1.8 repeatsMerge sort with an insertion cutoff0.72 · 0.0 repeatsInsertion sort0.72 · 0.0 repeatsQuicksort, median-of-three0.61 · 11.0 repeatsHeapsort0.58 · 11.9 repeatsBubble sort0.54 · 7.8 repeatsSelection sort0.51 · 14.5 repeatsbits removed per comparison, at 9 elements24 arrangements of 9light: repeats nothing · dark: repeats something
Fig. 3 Bits removed per comparison, averaged over 24 arrangements of nine elements. Merge sort 0.963, first-element quicksort 0.910, random-pivot quicksort 0.871, Shellsort 0.855, insertion sort and the hybrid 0.716, median-of-three quicksort 0.611, heapsort 0.582, bubble sort 0.544, selection sort 0.513. The light bars are the sorts that repeat nothing.

Insertion sort repeats nothing and still removes only 0.716 of a bit from each question. Merge sort removes 0.963. The difference is small in a ratio and it is the whole of the difference in class, because it compounds over every comparison: a sort that gets 0.72 bits from each question needs log2n!/0.72\log_2 n! / 0.72 questions and one that gets 0.96 needs log2n!/0.96\log_2 n! / 0.96 — except that the shortfall is not constant, and a sort whose questions get steadily less informative as it proceeds is exactly a sort whose count grows faster than the floor.

That is what insertion sort does. Comparing a new key against the sorted prefix asks “is this key smaller than that one” when the answer is very likely no — and an answer that is likely removes little. Merge sort’s merge compares the fronts of two runs that are entirely incomparable, so either answer is about equally likely and the comparison is close to a full bit.

Merge sort removes 1.03 of a bit a comparison over 28; Insertion sort removes 0.61 of a bit a comparison over 47 — and the shape of the two sequences differs as much as the means doEvery comparison of one run on 12 elements, in the order it was made, drawn at the number of orderings it eliminated expressed in bits. A full bit halves what is left; zero eliminates nothing. Merge sort: 28 comparisons removing 28.84 bits in all, 1.030 each, of which 0 removed nothing; the first five removed 1.00, 0.58, 1.00, 1.00, 0.58 and the last five 1.00, 0.81, 1.00, 1.32, 2.00. Insertion sort: 47 comparisons removing 28.84 bits in all, 0.614 each, of which 0 removed nothing; the first five removed 1.00, 0.58, 1.00, 0.42, 0.58 and the last five 0.26, 0.32, 0.42, 0.58, 1.00.Merge sort — 1.03 bits a comparison0.00.51.0Insertion sort — 0.61 bits a comparison0.00.51.0comparisons, in the order they were madeone run on 12 elementsheight: bits removed · dark: removed nothing
Fig. 4 Every comparison of one run on twelve elements, in the order it was made, drawn at the bits it removed. Merge sort makes 28 comparisons removing 28.84 bits — 1.03 each — and its last five remove 1.00, 0.81, 1.00, 1.32 and 2.00. Insertion sort makes 47 removing the same 28.84 bits, 0.61 each, and its last five remove 0.26, 0.32, 0.42, 0.58 and 1.00. Both must remove 28.84 bits in total, because that is how many orderings of twelve elements there are to eliminate.

The two sequences sum to the same number by construction — every sort must remove log2n!\log_2 n! bits, since it starts with n!n! orderings and finishes with one — so the only question is how many comparisons it takes to remove them, and that is the mean height of the bars.

Merge sort’s bars are tall and nearly uniform, and the last is worth two bits: the final merge’s last comparison resolves the only ambiguity left, which by then is a choice between four orderings rather than two. Insertion sort’s are ragged and mostly short, and the pattern is legible — a run of small bars as a key is compared against a long sorted prefix it belongs near the end of, then a tall one when the scan finally stops.

Selection sort removes 0.44 of a bit a comparison over 66; Bubble sort removes 0.44 of a bit a comparison over 66 — and the shape of the two sequences differs as much as the means doEvery comparison of one run on 12 elements, in the order it was made, drawn at the number of orderings it eliminated expressed in bits. A full bit halves what is left; zero eliminates nothing. Selection sort: 66 comparisons removing 28.84 bits in all, 0.437 each, of which 35 removed nothing; the first five removed 1.00, 0.58, 0.42, 2.32, 0.26 and the last five 2.00, 0.00, 0.00, 0.00, 1.00. Bubble sort: 66 comparisons removing 28.84 bits in all, 0.437 each, of which 19 removed nothing; the first five removed 1.00, 0.58, 0.42, 0.32, 0.26 and the last five 0.58, 0.42, 1.00, 0.58, 1.00.Selection sort — 0.44 bits a comparison0.00.51.0Bubble sort — 0.44 bits a comparison0.00.51.0comparisons, in the order they were madeone run on 12 elementsheight: bits removed · dark: removed nothing
Fig. 5 The same run for the two sorts that repeat themselves: 66 comparisons each on twelve elements, 0.44 of a bit each. The dark bars are comparisons that removed nothing — nineteen for selection sort and eight for bubble sort — and they are not spread evenly. Selection sort’s cluster towards the end, where the remaining range is small and almost everything in it has already been compared; bubble sort’s cluster in its later passes, for the same reason.

The dark bars clustering at the end is a remark about when a sort stops learning rather than about how much it learns. Both of these sorts spend their last third of comparisons confirming an order they have essentially finished establishing, which is the visual form of the arithmetic above.

The share that is not a share

There is a temptation, having a percentage, to treat it as a budget: selection sort wastes 60% of its comparisons, so a perfect selection sort would be 2.5 times faster. The plates above say that reading is wrong twice over.

It would be 2.5 times cheaper and still 7.8 times the floor, which is the arithmetic of the second section. A fix that removes the whole of a measured waste and leaves the method eight times worse than necessary is a fix worth having only if nothing better is available, and something better is: one run, four counts, four answers has nine other sorts on the same plate.

And the 60% is not a share of the work a machine does. A repeated comparison costs exactly what a new one costs — a load, a compare, a branch — so the percentage is a share of questions rather than of time, and the only sense in which the repeats are waste is the information-theoretic one. That is the sense the floor is stated in, which is why the measurement belongs beside it, and it is not the sense a profiler would report. The count is not the time is the standing caution and it cuts both ways here: a count can overstate a cost as easily as understate it.

The second measurement is the first one turned over

A reader who has got this far has two numbers for each sort and should be told what the second one is not.

Every sort removes exactly log2n!\log_2 n! bits, because every sort starts with n!n! orderings and ends with one. So the mean bits per comparison is log2n!\log_2 n! divided by the comparison count, and as a ranking of sorts it carries no information the count did not already carry. The bits plate’s order — merge, first-element quicksort, random-pivot quicksort, Shellsort, insertion sort, median-of-three, heapsort, bubble, selection — is the comparison count’s order written backwards, and it could not have been anything else.

What the second measurement buys is three things, and none of them is a ranking.

It prices a single comparison, which a count cannot. That is the whole of the trace plate: the mean is arithmetic, and the shape around it is not.

It gives the gap to the floor a unit. Merge sort makes 19.3 comparisons where the floor is 18.47, and saying it removes 0.963 of a bit rather than a full one states the same fact in the currency the floor is stated in. A ratio of counts is a number; a shortfall in bits per question is a diagnosis.

And it contains the first measurement exactly. A comparison removes zero bits precisely when its answer was already implied — so the repeats are not merely similar to the uninformative comparisons, they are the ones priced at exactly zero. The two quantities are computed by two procedures that share nothing: one maintains a transitive closure as bitsets of predecessors and successors, the other counts linear extensions by a walk over subsets and takes a logarithm. They agree to the comparison on every sort at every size checked — Shellsort 1.8 and 1.8, median-of-three 11.0 and 11.0, heapsort 11.9 and 11.9, bubble 7.8 and 7.8, selection 14.5 and 14.5.

That agreement is worth more than either number. It says the first section measured a boundary case of the second and not a different thing, which is what makes the sentence at the head of the next section a statement about one quantity rather than a comparison of two.

Two different defects, and one is not the other

The page now has two quantities and they rank the sorts differently.

Repeats put selection sort worst at 59.9% and insertion sort joint-best at zero. Bits per question put selection sort worst at 0.513 and insertion sort seventh of ten at 0.716.

A reader could reasonably have expected one to imply the other, and the failure is instructive. A repeated question is a question with probability one of a known answer — the extreme case of an uninformative question. Everything between is invisible to the first measurement and visible to the second, and most of the waste lives there.

Stated as a rule: a sort is inefficient when its questions have predictable answers, and repeating a question is only the case where the answer is certain. The information floor is a statement about the second quantity; the first is a special case of it that happens to be much easier to measure.

The share of questions a sort asks twice: selection sort settles near 59% and heapsort falls as the array growsThe share of each sort's comparisons whose answer its earlier comparisons had already established, against the number of elements, on logarithmic axes. Selection sort: 53.8% at 32, 58.0% at 64, 56.8% at 128, 59.9% at 256, 59.4% at 512. Bubble sort: 38.6% at 32, 40.9% at 64, 46.1% at 128, 49.2% at 256, 51.0% at 512. Heapsort: 36.8% at 32, 34.2% at 64, 30.2% at 128, 26.2% at 256, 24.5% at 512. Quicksort, median-of-three: 28.3% at 32, 22.9% at 64, 18.1% at 128, 15.4% at 256, 13.6% at 512. Merge sort: 0.0% at 32, 0.0% at 64, 0.0% at 128, 0.0% at 256, 0.0% at 512.32641282565120.0010.010.1elementsshare of comparisons already answeredSelection sortBubble sortHeapsortQuicksort, median-of-threeMerge sortrandom arrangementsa repeated question eliminates nothing
Fig. 6 The share of comparisons already answered, against the number of elements, on logarithmic axes. Selection sort rises from 53.8% at 32 elements towards 59%; bubble sort from 38.6% to 51.0%; heapsort falls from 36.8% to 24.5%; median-of-three quicksort stays near 15%; merge sort is zero at every size.

Heapsort’s line falling is the one worth explaining. Its repeats come from sift-downs comparing two siblings whose order an earlier sift already settled, and the number of such pairs grows with the heap’s shape rather than with the work, so the share shrinks as the array grows. Selection sort’s rises because its redundancy is quadratic and its total is quadratic with a smaller constant.

What this measures and what it does not

A comparison sort learns about its input only through the comparator, which is what makes both measurements possible and is the same door the adversary who knows the seed uses for a different purpose. Nothing here changes an algorithm; the comparator is replaced and the counts it reports are the counts the algorithm would have made.

Transitivity only. The closure used is the transitive closure of the comparisons made, which is everything a sort can deduce about the order — but a sort may also know things about positions, and a sort that recorded which pairs it had compared could avoid its repeats without any closure at all. The measurement says what is deducible, not what is remembered.

One arrangement per point, except where it is averaged. The share plate is one random arrangement of 256 elements; the bits plate averages 24 arrangements of nine, because counting linear extensions is a walk over 2n2^n subsets and nine is where it stops being free.

A comparison can remove more than one bit on a particular input. The worst-case bound is one bit, against an adversary. On a fixed arrangement a lucky comparison can eliminate more than half the remaining orderings, and random-pivot quicksort averages 0.871 with individual runs above one.

Self-comparisons count as repeats. The median-of-three pivot rule compares a position with itself on a short range, and those are counted as questions whose answer was already known — which is what they are, and which is why its 15.4% is a little above the other quicksorts’ zero.

The hybrid’s row is insertion sort’s row, and for a dull reason. The merge-and-insertion sort reads 0.716 bits over 26.4 comparisons and so does insertion sort, to three figures, because at nine elements the hybrid’s cutoff has not been crossed and it is insertion sort. Its row on that plate is a measurement of the cutoff rather than of the method, and the 256-element plate above is where it says something.

Twenty-four arrangements do not separate two pivot rules. On the bits plate a first-element pivot reads better than a random one — 0.910 against 0.871, or 20.7 comparisons against 22.0 — and over 400 arrangements the order reverses, to 21.5 against 20.2. Twenty-four arrangements of nine elements is ample to rank a quadratic sort against a linearithmic one and nowhere near enough to rank two rules that differ by a few per cent; what randomising the pivot buys is the page that separates them properly, and on average is not a number is why a mean over two dozen draws was never going to.

And the floor is the average-case one. log2n!\log_2 n! is the information floor for sorting; two floors that can be added is about a different question with a different floor, and the “times the floor” column here is against the sorting floor throughout.

Still open: the sort that is told what it knows

Bubble sort with a record of which pairs it had settled would be insertion sort. That is one point on a line, and the line is worth measuring: for each sort, what does it cost to carry the memory that would remove its repeats, and is any of them better off carrying it?

The memory is not free and its price is the interesting part. A bitset of settled pairs is n2n^2 bits, which for 256 elements is 8 KB against the array’s own 2 KB — a structure larger than the data, consulted before every comparison, to avoid comparisons that cost one instruction each. A sort would have to repeat a great many questions before that paid.

The measurement that follows gives each sort that record, counts the comparisons it removes against the bitset reads it adds, and asks where the exchange rate would have to sit for any of them to come out ahead — since a repeated comparison and a bitset lookup are both cheap, and the exchange rate nobody wrote down is the page that had to construct such a rate by hand before it could rank anything. The prediction is that none of them comes out ahead at any rate a real machine offers, which would make redundancy a property worth measuring and not a defect worth fixing.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

What links here

Every essay whose body links to this one.

The objects this essay names

Each one links to every other essay that touches it.

Comparison countCounting conventionInformation floorLower boundMeasurement designMerge sortPartial orderRedundancySelection sortSpecificationTransitivity