Algorithm Search⌘K

Proofs: Funnelsort

These are the human proofs of Funnelsort, at full length.

Funnelsort returns the stable sort of its input full

This proof shows: correctness.

We use the line numbers of the pseudocode. For a sequence XX, write st⁡(X)\operatorname{st}(X) for its stable sort. For sorted sequences X1,…,XrX_1, \ldots, X_r, the stable merge mg⁡(X1,…,Xr)\operatorname{mg}(X_1, \ldots, X_r) is st⁡(X1X2⋯Xr)\operatorname{st}(X_1 X_2 \cdots X_r): the items in non-decreasing order, and equal items in the order of X1,…,XrX_1, \ldots, X_r and of their positions. The cache bound is not proved here; see the cache-oblivious model page.

The funnel

For an inner node vv of a funnel, let σ(v)\sigma(v) be the sequence of all items that line L9 appends to the buffer of vv, over all calls of FILL, in order. For a leaf vv, let σ(v)\sigma(v) be its input, which is sorted. Let X1,…,XrX_1, \ldots, X_r be the leaves below vv, from left to right.

Lemma 1. Each call FILL(v)(v) ends. At any time, the items that vv has appended form a prefix of mg⁡(X1,…,Xr)\operatorname{mg}(X_1, \ldots, X_r). When vv is marked exhausted, this prefix is all of mg⁡(X1,…,Xr)\operatorname{mg}(X_1, \ldots, X_r).

Proof. By induction on the height of vv. A leaf is exhausted from the start, and its buffer holds its whole sorted input, so the claim holds.

Let vv be an inner node with children c1c_1 and c2c_2, and let Y1=mg⁡Y_1 = \operatorname{mg} of the leaves below c1c_1 and Y2Y_2 the same for c2c_2. By induction, the items that cic_i has appended form a prefix of YiY_i, and its buffer holds the part of this prefix that vv has not yet taken, in order. So at each moment, the items that vv has taken from cic_i are a prefix of YiY_i, and the head of the buffer of cic_i, if any, is the next item of YiY_i.

Termination. Each pass of the loop of line L6 either returns at line L8 or moves one item at line L9. The number of items is finite, and each inner call of line L7 ends by induction, so FILL(v)(v) ends.

Prefix. Line L7 fills each child buffer that is empty and whose child is not exhausted. By induction, a call FILL(ci)(c_i) ends with a full buffer or with cic_i exhausted. If it ends exhausted with an empty buffer, then vv has taken all of YiY_i. So after line L7, for each ii, either the buffer of cic_i holds the next item of YiY_i, or vv has taken all of YiY_i. Line L9 then appends the smaller of the two next items, or the left one on ties, or the only one. This is exactly the next step of the stable merge of Y1Y_1 and Y2Y_2, because Y1Y_1 and Y2Y_2 are sorted and every item of Y1Y_1 comes before the equal items of Y2Y_2 in mg⁡(X1,…,Xr)\operatorname{mg}(X_1, \ldots, X_r). And mg⁡(Y1,Y2)=mg⁡(X1,…,Xr)\operatorname{mg}(Y_1, Y_2) = \operatorname{mg}(X_1, \ldots, X_r), because the leaves below c1c_1 are X1,…,XqX_1, \ldots, X_q and those below c2c_2 are Xq+1,…,XrX_{q+1}, \ldots, X_r for some qq.

Exhaustion. Line L8 marks vv exhausted only when both child buffers are empty after line L7. By the argument above, vv has then taken all of Y1Y_1 and all of Y2Y_2, so it has appended all of mg⁡(Y1,Y2)\operatorname{mg}(Y_1, Y_2). ■\blacksquare

Lemma 2. In line L4, FILL(root) leaves in the root buffer the sequence mg⁡(P1,…,Pk)\operatorname{mg}(P_1, \ldots, P_k), where P1,…,PkP_1, \ldots, P_k are the sorted parts from left to right.

Proof. The empty leaves that round kk up to a power of 2 add no items. The root buffer holds nn items, the number of all items. By Lemma 1, the loop of line L6 appends a prefix of mg⁡(P1,…,Pk)\operatorname{mg}(P_1, \ldots, P_k), and it stops when the buffer is full, which is after all nn items, or when the root is exhausted, which by Lemma 1 is also after all items. Nothing removes items from the root buffer. ■\blacksquare

The sort

Theorem. FUNNELSORT(A)(A) leaves st⁡(A)\operatorname{st}(A) in AA.

Proof. By strong induction on nn. If n≤8n \le 8, line L1 uses insertion sort, which is stable. Else, k=⌈n1/3⌉≥3k = \lceil n^{1/3} \rceil \ge 3 and each part of line L2 has at most ⌈n/k⌉<n\lceil n/k \rceil < n items, so by induction line L3 replaces the part AjA_j by st⁡(Aj)\operatorname{st}(A_j). The parts are consecutive, so st⁡(A)=mg⁡(st⁡(A1),…,st⁡(Ak))\operatorname{st}(A) = \operatorname{mg}(\operatorname{st}(A_1), \ldots, \operatorname{st}(A_k)): an item of AjA_j comes before an equal item of Aj′A_{j'} in AA exactly when j<j′j < j'. By Lemma 2, line L4 computes this sequence, and line L5 writes it to AA. ■\blacksquare

Back to Funnelsort