Paper

Binary Search Invariants: A Formal Treatment

February 21, 2025 · algorithms , correctness

Binary search is often taught as a pattern to memorize. This paper gives a formal treatment using loop invariants, so you can derive correct implementations and avoid off-by-one errors.

The Problem

Given a sorted array A[0..n1]A[0..n-1] and a value vv, find index ii such that A[i]=vA[i] = v, or determine that vv is not present.

The Invariant

We maintain indices lolo and hihi such that:

  1. Invariant: If vv is in AA, then vA[lo..hi]v \in A[lo..hi].
  2. Bounds: 0lohi<n0 \leq lo \leq hi < n (or hi=1hi = -1 when the range is empty).

Initially, lo=0lo = 0 and hi=n1hi = n - 1, so the invariant holds: the full array contains vv if it exists.

The Loop

Each iteration computes mid=(lo+hi)/2mid = \lfloor (lo + hi) / 2 \rfloor and compares A[mid]A[mid] with vv:

In each case, we shrink the range while preserving the invariant. When lo>hilo > hi, the range is empty and vv is not in the array.

Termination

The quantity hilo+1hi - lo + 1 (the size of the search range) decreases by at least 1 each iteration. It starts non-negative, so the loop terminates.

Implementation

def binary_search(A: list[int], v: int) -> int:
    lo, hi = 0, len(A) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if A[mid] == v:
            return mid
        if A[mid] < v:
            lo = mid + 1
        else:
            hi = mid - 1
    return -1

The invariant guides the implementation. No memorization required.