Algorithm Search⌘K

Proofs: Oscillating sort

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

Oscillating sort sorts in ceil(log_P S) merge levels full

This proof shows: correctness.

We use the line numbers of the pseudocode. Let P=T−2≥2P = T - 2 \ge 2. The work tapes are read backward, so each work tape is a stack of runs: line L4 and line L8 append a run at the end, and line L7 removes the last run. Reading a run backward gives its items in the reverse order. Write dir‾\overline{\mathrm{dir}} for the opposite direction of dir\mathrm{dir}.

Let F1,F2,…,FSF_1, F_2, \ldots, F_S be the consecutive parts of MM items of FF (the last may be shorter), so that the jj-th execution of line L4 reads FjF_j.

Lemma. Let n≥1n \ge 1. Suppose that BUILD(n,x,dir)(n, x, \mathrm{dir}) starts when line L4 has read F1,…,FjF_1, \ldots, F_{j}. Then it returns when line L4 has read F1,…,Fj+nF_1, \ldots, F_{j+n}, and at that time:

  1. tape xx holds its old runs and, after them, one new run, which holds the items of Fj+1,…,Fj+nF_{j+1}, \ldots, F_{j+n} sorted in the direction dir\mathrm{dir};
  2. every other work tape holds exactly its old runs;
  3. the recursion below this call has ⌈log⁡Pn⌉\lceil \log_P n \rceil levels of merges.

Proof. By strong induction on nn.

For n=1n = 1, lines L3 and L4 read Fj+1F_{j+1}, sort it in the direction dir\mathrm{dir}, and append it to xx. No other tape changes and there is no merge, and ⌈log⁡P1⌉=0\lceil \log_P 1 \rceil = 0.

For n≥2n \ge 2, line L5 gives parts n1≥⋯≥nPn_1 \ge \cdots \ge n_P with n1=⌈n/P⌉<nn_1 = \lceil n/P \rceil < n and sum nn, and PP distinct tapes y1,…,yPy_1, \ldots, y_P, all different from xx. Line L6 calls BUILD(ni,yi,dir‾)(n_i, y_i, \overline{\mathrm{dir}}) for each ii with ni>0n_i > 0, in the order of ii. By induction, the call for ii reads the next nin_i parts of FF, appends to yiy_i one run QiQ_i with these items sorted in the direction dir‾\overline{\mathrm{dir}}, and leaves every other work tape as it was. In particular, the later calls for i′>ii' > i do not change yiy_i, and no call changes xx. So when line L6 ends, the parts Fj+1,…,Fj+nF_{j+1}, \ldots, F_{j+n} have been read, each yiy_i holds its old runs followed by QiQ_i, and every other tape is as before.

Line L7 removes QiQ_i from each yiy_i, which restores yiy_i, and reads QiQ_i backward, which gives its items sorted in the direction dir\mathrm{dir}. Line L8 merges these sequences in the direction dir\mathrm{dir}, so the result is sorted in that direction and holds exactly the items of Fj+1,…,Fj+nF_{j+1}, \ldots, F_{j+n}. It appends the result to xx. This proves 1 and 2.

For 3, the deepest sub-call is for n1=⌈n/P⌉n_1 = \lceil n/P \rceil, and it has ⌈log⁡P⌈n/P⌉⌉\lceil \log_P \lceil n/P \rceil \rceil levels by induction. The calls with smaller nin_i have at most as many levels, because ⌈log⁡P⋅⌉\lceil \log_P \cdot \rceil is non-decreasing. Line L8 adds one level, and ⌈log⁡P⌈n/P⌉⌉+1=⌈log⁡Pn⌉\lceil \log_P \lceil n/P \rceil \rceil + 1 = \lceil \log_P n \rceil for integers n≥2n \ge 2, because ⌈n/P⌉≤Pq\lceil n/P \rceil \le P^q exactly when n≤Pq+1n \le P^{q+1}. ■\blacksquare

Theorem. OSCILLATING-SORT returns the items of FF in non-decreasing order, reads FF once, never rewinds a tape, and makes ⌈log⁡PS⌉\lceil \log_P S \rceil levels of merges.

Proof. Line L2 calls BUILD(S,W1,ascending)(S, W_1, \text{ascending}) with all work tapes empty and nothing read. By the lemma with j=0j = 0 and n=Sn = S, it reads F1,…,FSF_1, \ldots, F_S, which is all of FF, once and in order, and it leaves one run on W1W_1, which holds all items in non-decreasing order, and all other work tapes empty. All reads of a work tape are backward reads of its last run, and all writes are at its end, so no rewind is needed. Part 3 of the lemma gives the number of levels. ■\blacksquare

Back to Oscillating sort