Algorithm Search⌘K

Proofs: Jump search

These are the human proofs of Jump search, at full length.

Jump search returns the first index of x, or NIL when x is not in A full

This proof shows: correctness, termination.

Let A[0…n−1]A[0 \ldots n-1] be sorted in non-decreasing order, n≥0n \ge 0, and let xx be a key. Let ℓ=∣{ j:A[j]<x }∣\ell = |\{\, j : A[j] < x \,\}| be the lower bound of xx. Because AA is sorted, A[j]<xA[j] < x for j<ℓj < \ell and A[j]≥xA[j] \ge x for ℓ≤j<n\ell \le j < n. We prove that JUMP-SEARCH(A,x)(A, x) stops and returns ℓ\ell when ℓ<n\ell < n and A[ℓ]=xA[\ell] = x, and NIL otherwise. This is the smallest index of xx when xx is present, since the copies of xx are exactly the indices j≥ℓj \ge \ell with A[j]=xA[j] = x, and they start at ℓ\ell when there are any.

Invariant of the jump loop. Before each test of line L3:

  1. 0≤lo≤hi≤n0 \le lo \le hi \le n and hi−lo≤mhi - lo \le m, where m≥1m \ge 1 is the value of line L1.
  2. A[j]<xA[j] < x for each j<loj < lo, that is, lo≤ℓlo \le \ell.
  3. hi=nhi = n or hi=lo+mhi = lo + m.

Start. Line L2 sets lo=0lo = 0 and hi=min⁡(m,n)hi = \min(m, n). Then 0≤lo≤hi≤n0 \le lo \le hi \le n, hi−lo≤mhi - lo \le m, statement 2 is empty, and statement 3 holds.

Step. The body runs only when hi<nhi < n and A[hi−1]<xA[hi - 1] < x. Since AA is sorted, A[j]≤A[hi−1]<xA[j] \le A[hi - 1] < x for each j<hij < hi. Line L4 sets lo′=hilo' = hi and hi′=min⁡(hi+m,n)hi' = \min(hi + m, n). Then statement 2 holds for lo′lo'. Also lo′=hi<nlo' = hi < n gives lo′≤hi′≤nlo' \le hi' \le n and hi′−lo′≤mhi' - lo' \le m, and hi′hi' is nn or lo′+mlo' + m.

Exit. The loop stops when hi=nhi = n or A[hi−1]≥xA[hi - 1] \ge x. In the first case ℓ≤n=hi\ell \le n = hi. In the second case hi≥1hi \ge 1 and A[hi−1]≥xA[hi - 1] \ge x, so ℓ≤hi−1<hi\ell \le hi - 1 < hi. In both cases, with statement 2,

lo≤ℓ≤hi.lo \le \ell \le hi.

Termination of the jump loop. Each pass makes hi′−hi=min⁡(m,n−hi)≥1hi' - hi = \min(m, n - hi) \ge 1, since m≥1m \ge 1 and hi<nhi < n. So hihi grows by at least 1 in each pass and never passes nn, and there are at most nn passes. More exactly, while hi<nhi < n, statement 3 gives hi=lo+mhi = lo + m, so hihi grows by mm in each pass but the last, and there are at most ⌈n/m⌉−1\lceil n/m \rceil - 1 passes.

The scan. Line L5 sets i=loi = lo. Before each test of line L6, lo≤i≤ℓlo \le i \le \ell and i≤hii \le hi. This holds at the start by the exit statement. A pass runs only when i<hii < hi and A[i]<xA[i] < x, which means i<ℓi < \ell; so the new i+1i + 1 satisfies i+1≤ℓi + 1 \le \ell and i+1≤hii + 1 \le hi. Each pass increases ii by 1 and i≤hi≤ni \le hi \le n, so the scan stops. When it stops, either i=hii = hi, and then ℓ≤hi=i≤ℓ\ell \le hi = i \le \ell; or A[i]≥xA[i] \ge x with i<hi≤ni < hi \le n, and then i≥ℓi \ge \ell by the definition of ℓ\ell. In both cases i=ℓi = \ell.

Result. Line L8 runs with i=ℓi = \ell. If ℓ<n\ell < n and A[ℓ]=xA[\ell] = x, it returns ℓ\ell, the smallest index of xx. Otherwise xx is absent: if ℓ=n\ell = n then every item is less than xx, and if A[ℓ]>xA[\ell] > x then every j≥ℓj \ge \ell has A[j]≥A[ℓ]>xA[j] \ge A[\ell] > x and every j<ℓj < \ell has A[j]<xA[j] < x. So line L9 returns NIL correctly. ■\blacksquare

The number of comparisons. The jump loop tests A[hi−1]A[hi - 1] only while hi<nhi < n, and by the termination argument this happens at most ⌈n/m⌉−1\lceil n/m \rceil - 1 times. The scan compares at most hi−lo≤mhi - lo \le m items. Line L8 makes at most one comparison. So the total is at most ⌈n/m⌉+m\lceil n/m \rceil + m.

Back to Jump search