Skip to content

First True Binary Search

Find the first position where a monotone predicate becomes true.

First true binary search finds the first index where a monotone Boolean predicate becomes true. It generalizes lower bound from array values to any ordered search space.

The predicate must have the form:

false,false,false,true,true,true false, false, false, true, true, true

Once the predicate becomes true, it must remain true.

Problem

Given indices from 00 to n1n - 1 and a predicate P(i)P(i), find the smallest index ii such that

P(i)=true P(i) = true

If no such index exists, return nn.

Key Idea

Maintain a half-open interval [l,r)[l, r) that contains the first true position. At each step, test the midpoint.

If P(m)P(m) is true, the answer is at mm or to its left. Otherwise, the answer is to the right.

Algorithm

first_true(n, P):
    l = 0
    r = n

    while l < r:
        m = l + (r - l) // 2

        if P(m):
            r = m
        else:
            l = m + 1

    return l

Example

Let the predicate values be:

index012345
P(i)falsefalsefalsetruetruetrue

The first true index is 33.

Relation to Lower Bound

Lower bound is a special case where

P(i)A[i]x P(i) \equiv A[i] \ge x

So:

lower_bound(A, x):
    return first_true(length(A), lambda i: A[i] >= x)

Correctness

The invariant is:

  • The answer is always inside [l,r)[l, r)

When P(m)P(m) is true, no index greater than mm is needed to find the first true position, so the right boundary moves to mm.

When P(m)P(m) is false, monotonicity implies that every index m\le m is false, so the left boundary moves to m+1m + 1.

When the interval is empty, l=rl = r, and that position is the first true index or nn if none exists.

Complexity

costvalue
predicate evaluationsO(logn)O(\log n)
timeO(lognTP)O(\log n \cdot T_P)
spaceO(1)O(1)

Here TPT_P is the cost of evaluating the predicate.

Use Cases

  • Lower bound in sorted arrays
  • Minimum feasible value
  • First failing test
  • First time a threshold is reached
  • First day when a condition holds

Implementation

def first_true(n, pred):
    l, r = 0, n
    while l < r:
        m = l + (r - l) // 2
        if pred(m):
            r = m
        else:
            l = m + 1
    return l
func FirstTrue(n int, pred func(int) bool) int {
    l, r := 0, n
    for l < r {
        m := l + (r-l)/2
        if pred(m) {
            r = m
        } else {
            l = m + 1
        }
    }
    return l
}