Algorithm Search⌘K

Proofs: External merge sort

These are the human proofs of External merge sort, at full length.

External merge sort sorts the file stably and does at most the stated number of I/Os full

This proof shows: correctness.

We use the line numbers of the pseudocode. A stable sort of a sequence SS is the sequence of its items in non-decreasing order, where equal items keep their order in SS. We write st⁡(S)\operatorname{st}(S) for it. We assume M≥3BM \ge 3B, so k=M/B−1≥2k = M/B - 1 \ge 2.

Lemma 1 (merge). Let S1,…,SgS_1, \ldots, S_g be sequences, and let S=S1S2⋯SgS = S_1 S_2 \cdots S_g be their concatenation. A merge of the runs st⁡(S1),…,st⁡(Sg)\operatorname{st}(S_1), \ldots, \operatorname{st}(S_g) that outputs the smallest head, and on equal heads the head of the run with the smallest index, outputs st⁡(S)\operatorname{st}(S).

Proof. Each run is sorted, so the head of run jj is its smallest item that is not yet output. So each output item is a minimum of the items that are not yet output, and the output is non-decreasing. Each item is output one time, so the output is a permutation of SS in non-decreasing order. Take two equal items xx and yy, where xx comes before yy in SS. If both are in the same part SjS_j, then xx comes before yy in st⁡(Sj)\operatorname{st}(S_j), and the merge outputs the items of one run in their order. If xx is in SiS_i and yy is in SjS_j with i<ji < j, consider the step that outputs yy. At this step yy is the head of run jj. If xx were not yet output, the head hh of run ii would satisfy h≤x=yh \le x = y. If h<yh < y, the merge would not output yy. If h=yh = y, the tie rule would output hh first. So xx is output before yy. Both cases give the order of SS. □\square

Invariant. At each test of line L6, there are consecutive parts F1,…,FrF_1, \ldots, F_r of FF, with F=F1⋯FrF = F_1 \cdots F_r, such that R=[st⁡(F1),…,st⁡(Fr)]R = [\operatorname{st}(F_1), \ldots, \operatorname{st}(F_r)]. If N≥1N \ge 1, each part is not empty.

Initialization. Lines L3 to L5 cut FF into pieces of MM consecutive items from left to right, the last piece possibly shorter, and append the stable sort of each piece to RR in this order. So the invariant is true with the pieces as the parts.

Maintenance. Line L8 cuts RR into groups of kk consecutive runs, from left to right. A group of runs st⁡(Fa),…,st⁡(Fb)\operatorname{st}(F_a), \ldots, \operatorname{st}(F_b) becomes one run. If a=ba = b, line L9 keeps it, and it is st⁡(Fa)\operatorname{st}(F_a). If a<ba < b, Lemma 1 shows that line L10 makes st⁡(Fa⋯Fb)\operatorname{st}(F_a \cdots F_b). The new parts Fa⋯FbF_a \cdots F_b are consecutive, in order, and their concatenation is FF. So after line L11 the invariant is true again.

Termination. If ∣R∣=r≥2|R| = r \ge 2 at line L6, the pass makes ⌈r/k⌉\lceil r/k \rceil runs. Because k≥2k \ge 2, ⌈r/k⌉≤⌈r/2⌉≤r−1\lceil r/k \rceil \le \lceil r/2 \rceil \le r - 1. So ∣R∣|R| drops by at least 1 in each pass, and the loop stops after at most r−1r - 1 passes.

Theorem. EXTERNAL-MERGE-SORT returns st⁡(F)\operatorname{st}(F).

Proof. If N=0N = 0, line L3 does nothing, RR is empty, and line L12 returns an empty file. If N≥1N \ge 1, the loop stops with ∣R∣=1|R| = 1, and by the invariant the one run is st⁡(F1)\operatorname{st}(F_1) with F1=FF_1 = F. ■\blacksquare

The I/O count. Let n=⌈N/B⌉n = \lceil N/B \rceil and r0=⌈N/M⌉r_0 = \lceil N/M \rceil, and assume that BB divides MM and N≥1N \ge 1. Run formation reads and writes each piece. All pieces except the last have MM items, a multiple of BB, so the pieces use nn blocks in total, and run formation costs 2n2n I/Os. By the invariant, in each pass all runs except the last hold a union of full pieces, so their lengths are multiples of BB, and the runs use nn blocks in total. A pass reads and writes each run of a group with two or more runs, and does no I/O for a group of one run, so a pass costs at most 2n2n I/Os. For integers x≥1x \ge 1 and k≥1k \ge 1, ⌈⌈x/k⌉/k⌉=⌈x/k2⌉\lceil \lceil x/k \rceil / k \rceil = \lceil x/k^2 \rceil. By induction, after pass ii there are ⌈r0/ki⌉\lceil r_0/k^i \rceil runs. This is 1 exactly when ki≥r0k^i \ge r_0, so the number of passes is ⌈log⁡kr0⌉\lceil \log_k r_0 \rceil. The total is at most 2n(1+⌈log⁡kr0⌉)2n(1 + \lceil \log_k r_0 \rceil). ■\blacksquare

Back to External merge sort