Algorithm Search⌘K

Proofs: External stack

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

The external stack returns the right items and makes at most n/B block transfers full

This proof shows: correctness, complexity.

We use the pseudocode of the page with block size B≥1B \ge 1. The state is the list of disk blocks D[0],…,D[k−1]D[0], \ldots, D[k-1] and the buffer bufbuf. Let σ=D[0]⋅D[1]⋯D[k−1]⋅buf\sigma = D[0] \cdot D[1] \cdots D[k-1] \cdot buf be their concatenation.

Invariant. At the start and after each operation: (I1) σ\sigma is the abstract stack, from the bottom to the top; (I2) each D[j]D[j] has exactly BB items; (I3) ∣buf∣≤2B|buf| \le 2B.

Base. The empty stack has k=0k = 0 and bufbuf empty, so σ\sigma is empty and (I2) and (I3) hold.

Push. If ∣buf∣=2B|buf| = 2B, lines L2 and L3 append the first BB items of bufbuf to the disk part as a new block and remove them from bufbuf. The concatenation σ\sigma does not change, the new block has BB items, and ∣buf∣=B|buf| = B. Line L4 appends xx to bufbuf, so σ\sigma becomes the old stack followed by xx, which is the new stack. Before line L4, ∣buf∣≤2B−1|buf| \le 2B - 1: either it was below 2B2B, or it is now B≤2B−1B \le 2B - 1. So ∣buf∣≤2B|buf| \le 2B after it.

Pop. If the abstract stack is empty, σ\sigma is empty, so bufbuf is empty and k=0k = 0, and line L6 reports the error. Otherwise, if bufbuf is empty, then k>0k > 0, and line L7 moves the last block into bufbuf, which does not change σ\sigma and gives ∣buf∣=B≥1|buf| = B \ge 1. Line L8 removes the last item of bufbuf, which is the last item of σ\sigma, the top of the stack. (I1) holds, and (I2) and (I3) hold because no block was written and bufbuf only became smaller or equal to BB.

So by induction the invariant holds after every operation, and each pop returns the top of the stack. □\square

Theorem (I/O bound). Starting from the empty stack, any sequence of nn pushes and pops makes at most ⌊n/B⌋\lfloor n/B \rfloor block transfers.

Proof. Each operation makes at most one transfer: a push writes only if ∣buf∣=2B|buf| = 2B, and a pop reads only if bufbuf is empty, and these are not both true in one operation. Let the transfers happen in the operations o1<o2<⋯<oto_1 < o_2 < \cdots < o_t of the sequence, numbered from 1.

The first transfer is a write, since a read needs k>0k > 0, and kk grows only by writes. A write needs ∣buf∣=2B|buf| = 2B, and each operation before it is a push or a pop without transfer, which changes ∣buf∣|buf| by one, starting from 0. So at least 2B2B operations come before it, and o1≥2B+1o_1 \ge 2B + 1.

After the transfer in ojo_j, and before the change of line L4 or L8, ∣buf∣=B|buf| = B. So at the end of ojo_j, ∣buf∣|buf| is B+1B + 1 after a write or B−1B - 1 after a read. The transfer in oj+1o_{j+1} needs ∣buf∣=2B|buf| = 2B or ∣buf∣=0|buf| = 0 at the start of oj+1o_{j+1}. The operations oj+1,…,oj+1−1o_j + 1, \ldots, o_{j+1} - 1 make no transfer, so each changes ∣buf∣|buf| by exactly one. From B+1B + 1 or B−1B - 1 to 2B2B or 0 needs at least B−1B - 1 such steps, so oj+1−oj≥Bo_{j+1} - o_j \ge B.

Hence ot≥2B+1+(t−1)B>tBo_t \ge 2B + 1 + (t - 1)B > tB, and since ot≤no_t \le n, t<n/Bt < n/B when t≥1t \ge 1. So t≤⌊n/B⌋t \le \lfloor n/B \rfloor. ■\blacksquare

Back to External stack