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:
Once the predicate becomes true, it must remain true.
Problem
Given indices from to and a predicate , find the smallest index such that
If no such index exists, return .
Key Idea
Maintain a half-open interval that contains the first true position. At each step, test the midpoint.
If is true, the answer is at 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 lExample
Let the predicate values be:
| index | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| P(i) | false | false | false | true | true | true |
The first true index is .
Relation to Lower Bound
Lower bound is a special case where
So:
lower_bound(A, x):
return first_true(length(A), lambda i: A[i] >= x)Correctness
The invariant is:
- The answer is always inside
When is true, no index greater than is needed to find the first true position, so the right boundary moves to .
When is false, monotonicity implies that every index is false, so the left boundary moves to .
When the interval is empty, , and that position is the first true index or if none exists.
Complexity
| cost | value |
|---|---|
| predicate evaluations | |
| time | |
| space |
Here 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 lfunc 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
}