I have a problem with defining the invariant of finding the first element of a binary search. (I have a sorted array a and I want to find the first element that is equal to some number q and if it does not exist return -1)
First, I set this invariant for my while.
My invariant
"Always a[l]<= q and also a[r] > q" ==> "Always l <= ind and also > ind".
Up to my invariant, I wrote this code:
int l=0,r=n;
while(l<r){
int mid=(r+l)/2;
if(a[mid]==q){
r=mid+1;
}
else{
if(a[mid]>q){
r=mid;
}else if(a[mid]<q) l=mid+1;
}
}
return l;
But there is a problem that if(a[mid]==q)
then I must pick an r
that doesn't violate my invariant.
If I choose mid-1
I will violate it because a[r]
will be <= q
.
And I must iterate through my indices until I find an index I that a[i]>q
and then set r
to that index. (r=i)==>But If I do this it's not O(log n)
And I have seen some code implementing lower_bound
that if(a[mid]==q)
the set r
to mid
but I think they are violating they invariant but they code are correct and return the correct value.
Like this code:
1- int l = 0;
2- int r = n; // Not n - 1
3- while (l < r) {
4- int mid = (l + r) / 2;
5- if (q <= a[mid]) {
6- r = mid;
7- } else {
8- l = mid + 1;
9- }
10- }
11- return l;
At the first, The invariant is like my invariant(i
is on the range of [l,r)
) but in line 5 consider if(q==a[mid])
then obviously it's violating because its( [l,r]
because r
is equal and it can be the first occurrence).
Am I right or I don't have the correct understanding concept of invariant?
Suppose we have a sequence
..., <q, <q, <q, q, q, ..., q, q, >q, >q, >q, ...
^ (*)
where <q
(>q
) stands for any element < q
(> q
). We want to find the point (*).
We have two pointers, left < right
. How can we use them to distinguish that point? The answer is simple: left
should point to the last <q
element, right
should point to the first q
element:
..., <q, <q, <q, q, q, ..., q, q, >q, >q, >q, ...
^ right
^ left
The invariant is: *left < q
and *right >= q
.
The invariant that you suggested, *left <= q
and *right > q
corresponds to the last element in that sequence:
..., <q, <q, <q, q, q, ..., q, q, >q, >q, >q, ...
^ right
^ left
Some references that can be useful:
Col.4: Writing correct programs – J.Bentley. Programming pearls. Addison-Wesley, 2nd ed., 1999. Book website
J.Bentley. Programming pearls: Writing correct programs. Communications of the ACM 26, 1040 (1983). Full text
Invariants for binary search – Dr.Dobb's Journal Part I, Part II, Part III, Part IV, Part V, Part VI, Part VII, Part VIII, Part IX, Part X