# Binary Search in General
## Intuition
Suppose we are given a **sorted** array, say
$$A = [1, 1, 2, 4, 6, 9]$$
then we can see that all numbers in $A[0:3]$ are **smaller** than $4$, while all in $A[3:6]$ are **equal or greater than** $4$.
Thus, we can define a **predicate**,
$$f(x) := (x >= 4)$$
Let $B$ be the array of $f(A)$, then
$$B = [0, 0, 0, 1, 1, 1]$$
As we can see, $B$ is of the form $0^*1^*$ (here I'm abusing the usage of regex, but it's just to clarify that we will first have contigous $0$'s and then contiguous $1$'s in the predicate array.)
## Algorithm
The following algorithm will help us find the **smallest** index $i$ in $B$ for which the predicate $f(i)$ holds true.
That is, we want to find the **first** $1$ in $B$.
(Keep this in mind !)
```python=
function find(n, f):
i, j := 0, n
while i < j:
mid = (i+j)/2 # floor
if f(mid):
j = m # preserve f(j) = 1
else
i = m+1 # preserve f(i-1) = 0
return i # Note i == j here
```
## Proof of Correctness
Let $n = len(A)$
Define $f(-1) = 0, f(n) = 1$
### Lemma 1
Invariant:
- $f(i-1) = 0$
- $f(j) = 1$
#### Proof by induction
- At first, $f(-1) = 0$ and $f(n) = 1$, true.
- Later on, if $f(mid) = 0$, then we set $i = mid+1$, preserving $f(i-1) = 0$.
- if $f(mid) = 1$, then we set $j = mid$, still $f(j) = 1$ is preserved.
### Lemma 2
When the loop in line 3 breaks, $i = j$
#### Proof
Since $i<j$ at first, we have $i \le\ mid < j$ (since we always take mid using floor).
Consider the last iteration,
- If we update i, since $mid+1 \le\ j$, then $i$ must be $j$
- If we update j, since $i \le\ mid$, then $j$ must be $i$
### Theorem
By the property of sorted array, and Lemma 1, 2.
We can confirm that the returned index $i$ is the **smallest** index such that $f(i) = 1$
## Application
### Find the first element greater or equal to x
Let $f(v) = (v \geq x)$
```go=
func firstGreaterEqual(x int, nums []int) int {
i, j := 0, len(nums)
for i < j {
m := int(uint(l+r)>>1) // prevent overflow
if nums[m] >= x { // f(v) = (v >= x)
j = m
} else {
i = m+1
}
}
return i
}
```
### Find the largest element < x
```go=
func largestLess(x int, nums []int) int {
// can you see why ?
return firstGreaterEqual(x, nums)-1
}
```
### Find the last x if duplicated x exist in A
Let $f(v) = (v > x)$
```go=
func lastDuplicate(x int, nums []int) int {
i, j := 0, len(nums)
for i < j {
m := int(uint(i+j)>>1)
if nums[m] > x {
j = m
} else {
i = m+1
}
}
return i - 1
}
```
### Find the first x if duplicated x exist in A
Let $f(v) = (v \geq x)$
```go=
func firstDuplicate(x int, nums []int) int {
i, j := 0, len(nums)
for i < j {
m := int(uint(i+j)>>1)
if nums[m] >= x {
j = m
} else {
i = m+1
}
}
return i
}
```
###### tags: `Algorithm` `Binary Search`