How to implement a lower_bound binary search algorithm in Java? - java

I want to find a target value 4 firstly appeared place in a sequence [1, 1, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6]. when I use java.util.Arrays.binaySearch, it returns index is 9, but I expected 7.
I look java.util.Arrays.binaySearch source code
and I found some comments:
If the array contains multiple elements with the specified value, there is no guarantee which one will be found.
So how to implement a lower_bound binary search algorithm in Java, which returns the target value firstly appeared place.
Note: The lower_bound concept comes from C++, but I don't understand C++ well.

I think the implementation below will do the job correctly:
int firstOccurrence(int[] sequence, int x) {
int min = 0;
int max = sequence.length - 1;
int result = -1;
while (min <= max)
{
// find the mid value and compare it with x
int mid = min + ((max - min) / 2);
// if x is found, update result and search towards left
if (x == sequence[mid]) {
result = mid;
max = mid - 1;
} else if (x < sequence[mid]) {
// discard right half
max = mid - 1;
} else {
// discard left half
min = mid + 1;
}
}
// return the leftmost index equal to x or -1 if not found
return result;
}
Edit:
Change the way to compute mid to avoid overflow with larger sums
// Previously, can overflow since we add two integer
int mid = (min + max) / 2;
// Now
int mid = min + ((max - min) / 2);
// Another way using the unsigned right shift operator
int mid = (low + high) >>> 1;
// The left operands value (low + high) is moved right
// by the number of bits specified (2 in this case) by the right operand and
// shifted values are filled up with zeros.
// The >>> treats the value as unsigned

Building on this answer to another binary search question:
How can I simplify this working Binary Search code in C?
This is a search that is equivalent to lower_bound from C++. It returns the number of elements smaller than the value you want to find. That would be
the index of the first occurrence, or where one would be inserted if there is no occurrence:
int numSmaller(int[] seq, int valueToFind)
{
int pos=0;
int limit=seq.length;
while(pos<limit)
{
int testpos = pos+((limit-pos)>>1);
if (seq[testpos]<valueToFind)
pos=testpos+1;
else
limit=testpos;
}
return pos;
}
Note that we only need to do one comparison per iteration.
The linked answer highlights several advantages of writing a binary search this way.

It think it will help you
public static boolean binarysearch(int[] data, int target, int low, int high){
if(low>high){
System.out.println("Target not found");
return false;}
else{
int mid=(low+high)/2;
if(target==data[mid])
return true;
else if(target<data[mid])
return binarysearch(data, target, low, high);
else
return binarysearch(data, target, low, high);
}
}

Related

Using a Variant of BinarySearch to Find the Maximum in a Bitonic Array

Here is my code, where a is the array to find the maximum of (each element is distinct). From my perspective, if a[mid] is less than a[mid-1] then the position of the maximum should be in the range of [lo, mid-1]. However, when I implement my idea, the program is endless. The solution is to use [lo, mid] as the next iteration.
My question is: Why shouldn't I use mid-1 instead of mid in Line 9?
First edit: Some people ask why didn't I just sort and choose the first element? Since the problem is to find a key in a bitonic array (where elements ascend first then descend). My solution is to find the maximum of the array, separate it into two parts and respectively use BinarySearch to find the key. If I sort the array I will destroy the original order.
Second edit: add more code in detail and the expected output.
public static int getMaxIndex(int[] a) {
int hi = a.length - 1;
int lo = 0;
while (lo < hi && a[lo] <= a[lo+1]) {
int mid = lo + (hi - lo) / 2;
if (a[mid] > a[mid-1]) {
lo = mid;
} else {
hi = mid - 1; // Should be mid, not mid-1, why?
}
}
return lo;
}
public static void main(String[] args) {
int[] a = {1, 2, 3, 5, 7, 9, 8, 6, 4}; // Bitonic array
System.out.println(getMaxIndex(a)); // Should be 5
}
bitonic array
The numbers are strictly increasing and after an index, the numbers are strictly decreasing
eg: [15, 20, 26, 5, 1]
If the array contains only unique elements, then maximum can be found using following conditions
iterate while left index < right index
compute middle index (towards left incase no single middle index)
if the value at middle is smaller than next element, then move towards right
else move left towards left
arr[mid] < arr[mid + 1] will never throw out of bounds exception as left < right invariant is maintained on the loop. hence mid(index) will always be lesser than right(index) and hence there is atleast one index after mid
int left = 0, right = arr.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < arr[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return arr[left]; // or arr[right]
15 20 26 5 1
l=0, r=4 l m r
m=2
a[m] > a[m+1] l r
so, r=m
l=0, r=2 l m r
m=1
a[m] < a[r+1] l,r
m=l+1
Now exit while loop l == r and return value at l or r
Analysis of OP's code
A simple reproducible example is [1, 2] - this will cause lo to be stuck to same index and causes infinite loop.
Any dataset that reduces to lo + 1 == hi and arr[lo] < arr[hi] will set mid to lo (lo + (hi - lo) / 2 will be lo) and hence the assignment lo = mid causes an infinite loop
The problem you had was that you were not always changing mid. In your example, at one point lo is 4 and pointing to 7 in the array while hi is 5 and points to 9. In the situation where lo and hi are just 1 apart, mid is set to lo. Here you are comparing a[mid] > a[mid-1]. Problem 1, mid-1 is out of range and in rare cases gives an ArrayIndexOutOfBoundsException. Second, if the condition is true, you set lo to mid, which is the value it already had. You are getting nowhere. You are in an infinite loop.
I tried your method with
System.out.println(getMaxIndex(new int[] { 3 , 8 })); // Expecting 1
I got java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 2
The solution is in the answer by Horse.
Here, the word bitonic means, array is sorted in increasing order, and after some point (may be) it starting to decrease.
Likewise, you have to find the pivot point where array is starting to decrease (if it is exist), if you can able to find the point, that's it, this pivotal point is your answer.
so i am attaching some input/output sequence to make it more clear:
{1, 2, 3, 5, 7, 9, 8, 6, 4}; --> should return 5 (index at which element is maximum)
{15, 20, 26, 5, 1} --> should return 2 (index at which element is maximum)
{0, 1, 2, 3} --> should return 3 (index at which element is maximum)
now you have to modify your code like following:
public static int getMaxIndex(int[] arr) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if(mid == 0 || mid == arr.length-1) {
// if no such pivotal point exist in sequence/array
return mid;
}else if(arr[mid] > arr[mid+1] && arr[mid] > arr[mid-1]){
// it's the pivotal point, where element at left side smaller and element at right side too
return mid;
}
if (arr[mid] < arr[mid + 1]) {
left = mid + 1;
} else {
right = mid;
}
}
return -1;
}

Binary Search Program

The code is a simple binary search program.
I tried tracing the program but it only made me more confused. I can't figure out why the nested if has data, min, midpoint - 1, & target
vs. the bottom else if statement has data, midpoint + 1, max, target.
public static boolean binarySearch(int[] data, int min, int max, int target){
boolean found = false;
int midpoint = (min + max) / 2; // determine the midpoint
if (data[midpoint] == target)
found = true;
else if (data[midpoint] > target)
{
if (min <= midpoint - 1)
found = binarySearch(data, min, midpoint - 1, target);
}
else if (midpoint + 1 <= max)
found = binarySearch(data, midpoint + 1, max, target);
return found;
}
The array data is already sorted from smallest to largest
Therefore it finds if the value at the midpoint is greater than the target, then the target would appear in the values before midpoint. So we recursively call the method only on the left of the midpoint i.e. all the values from the min till the value before the midpoint.
Similarly if the midpoint is less than the target, the target could be found after the midpoint, therefore we recursively call the method only on the right of the midpoint i.e. all the values from the value after the midpoint to the end.
Each time time we don't include the midpoint as that is checked in the line
if (data[midpoint] == target)
e.g
Array 3 6 8 10 13 14 20. target = 14
the midpoint would be = 10 9index 4). Checking target and midpoint, we'd see that the target is greater than the midpoint and falls on the right.
So we now check for target in 13 14 20 --- from midpoint+1 (index 5) till the end.
The midpoint would be 14. And the above if statement would return true.
Binary search searches the left half (min... mid-1) and the right half (mid+1...max) recursively.
You are checking mid against target, so that is why it isn't included in that range.
You really should have a base-case for if (min >= max) return false; to prevent going out of bounds of the array.
Here is a cleaner implementation as I find it easier to understand
public static boolean binSearch(int[] data, int target) {
return _binSearch(data, target, 0, data.length);
}
private static boolean _binSearch(int[] data, int target, int low, int high) {
int mid = (low + high) / 2;
if (low >= high) return false;
if (data[mid] == target) return true;
boolean foundLeft = _binSearch(data, target, low, mid);
boolean foundRight = !foundLeft && _binSearch(data, target, mid+1, high);
return foundLeft || foundRight;
}
You divide your data into a half smaller than midpoint, with a range (min, mid-1) and bigger than midpoint, with a range (mid+1, max).
if you have an input of {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}, and min = 0; max= 10, then
int midpoint = (0 + 10) / 2; // which is 5
Iff data[midpoint] is not what you were looking for, need need to look for everything left or right (but not midpoint itself, thats why there is a -1 and a +1 in there..)
So the range for the left half is (0, 4) and the range for the right half is (6, 10).
If you are looking for, lets say 12, we'd go left, because data[midpoint] == 13 && 13 > 12:
int midpoint = (0 + 4) / 2; // which is 2
since data[2] < 10, we'd go right, with min = 3; max = 4; and so on..
It seems the code incorrectly compares the current midpoint with the min and max index. Instead
if (min <= midpoint - 1)
:
else if (midpoint + 1 <= max)
it should use
if (min < midpoint - 1)
:
else if (midpoint + 1 < max)
Try the below attempt to correct it:
public static boolean binarySearch(int[] data, int min, int max, int target){
if (max > min) {
int midpoint = (min + max) / 2; // determine the midpoint
if (data[midpoint] == target) {
return true;
}
if (data[midpoint] > target) { // use lower half
return binarySearch(data, min, midpoint-1, target);
}
else { // use upper half
return binarySearch(data, midpoint+1, max, target);
}
}
return false;
}
See DEMO

Detect nth term of binary sequence

I have a binary sequence that follows a specific logic such that
it starts with 0 and
nth term = sequence of (n-1)th term + 0 + inverse(reverse(n-1)th term)
eg:
0
0 + 0 + 1
001 + 0 + 011
0010011 + 0 + 0011011
Here, I need to find out the nth term of the kth sequence.
My take:
I've written a recursive function to calculate the number of terms in k'th sequence
public static long noOfElements(long elements){
long answer;
if(elements == 1)
return 1;
else
answer = 1 + 2*noOfElements(elements-1);
return answer;
}
After analysis, I found out the sequence follows a certain pattern, the k'th sequence can be broken down by half and switching the values of 0 and 1 I could keep track of the result.
So, My function below breaks the given sequence down to [0,0,1] recursively
public static long letsBegin(long reqd, long length){
long mid = (length + 1)/2;
if(length <= 3){
return reqd;
}else{
if(reqd > mid){
reqd = reqd - 2*(reqd-mid);
switcher(); //Switcher stores if the value is switched
return letsBegin(reqd, mid);
}else{
return letsBegin(reqd, mid);
}
}
}
In the end I have index 1, 2 or 3 in [0,0,1] and I output the value accordingly.
The problem here is
It fails for some unknown case (Probably my logic's wrong).
The number of sequences can be upto 50, making number of elements = 1125899906842623 and hence, takes too long to output value (>2sec)
What may have went wrong? Is my logic incorrect
Easily done with recursion, the number of elements in k-th sequence is 2^(k+1)-1:
static int foo(long n, int k) { //n-th element (indexed from 0) in k-th sequence
long length = (2L << k) - 1; // computes 2^(k+1)-1
if(n >= length) return -1; // prevent invalid inputs
if(n == length/2) return 0; // middle point
if(n < length/2) return foo(n, k-1); //left half
return 1 - foo(length - n - 1, k-1); //right half
}
In the last recursive call, you both flip the array and the return value.
EDIT:
Be sure to use (2L << k) and not (2 << k) otherwise this will cause overflow and may lead to endless recursion.
You are missing a case (when reqd == mid) and calling the recursive function with incorrect length (mid instead of mid-1). After these fixes the function looks like this:
public static long letsBegin(long reqd, long length){
long mid = (length + 1)/2;
if(length <= 3){
return reqd;
} else{
if(reqd > mid){
reqd = reqd - 2*(reqd-mid);
switcher(); //Switcher stores if the value is switched
return letsBegin(reqd, mid-1);
} else if(reqd < mid) {
return letsBegin(reqd, mid-1);
} else {
return 0;
}
}
}
Also, the code is more complicated than necessary. Try using the following recurrence relation instead:
T(n, k) = T(n, k-1) if n < 2^{k-1}
= 0 if n = 2^{k-1}
= 1 - T(2^k-n, k-1) otherwise

BinarySearch Implementation does not work in certain cases

I'm a java beginner and I'm trying to code an implementation of the binary search algorithm.
This is my code:
protected int doSearch(List<Integer> list, int key) throws SearchException{
int min = 0;
int max = list.size()-1;
while(max > min){
int mid = min + ((max-min)/2);
if(list.get(mid)==key){
return mid;
}
else if(list.get(mid) < key){
min = mid +1 ;
}
else{
max = mid - 1;
}
}
throw new SearchException("");
}
I tried to copy it from this link http://en.wikipedia.org/wiki/Binary_search_algorithm and tried to get it working for lists.
The input list is [1, 2, 3, 4, 5, 7, 9]
If I search for the key 2 the output is 1 which is fine, but if I try for example 1 the SearchException is fired.
I can't explain why. I tried to debug the code by reproducing it on paper, but it worked on the paper.
Thank you!
You're currently inconsistent about whether max is an inclusive lower bound, as suggested here:
int max = list.size()-1;
...
max = mid - 1;
or an exclusive lower bound, as suggested here:
while (max > min)
You can make it work either way, so long as you're consistent. Personally I'd suggest using an exclusive upper bound, as that's consistent with things like list.size() and computer science in general. So if mid is too large, you need to change max to equal mid. Your code will look like this:
int max = list.size(); // Note change here
while(max > min) {
int mid = min + ((max - min) / 2);
if (list.get(mid) == key) {
return mid;
} else if (list.get(mid) < key) {
min = mid +1 ;
} else {
max = mid; // Note change here
}
}
(I've fiddled with the formatting to make it easier to read IMO as well. See whether you prefer it or not.)
Easiest fix is to change:
while (max > min) {
to:
while (max >= min) {
You throw the exception in the above because you don't handle that case.

Binary search 1/4 modification

My task is to write 1/4 - 3/4 binary search algorithm modification wherein the first element compared, when searching for an item in the list, is the 'pivot' element which is at a distance of 1/4th from
one end of the list(assuming the end chosen is the start of the 'remaining'
list). If there is no match ('pivot' element is not equal to the search key) and
if the part of the list that should be examined further for the search is 1/4th
part of the list, continue with the same strategy. Whenever the part of the
list that should be examined further for the search is of size 3/4th, switch to
a binary search once and return to the 1/4th-3/4th strategy.
My code is here, but it doesnt work, and i dont know even if i am doing it right:
public static int ThreeFour(int[] Array,int item)
{
int counter =0;
int high=Array.length-1;
int low=0;
int pivot = 0;
boolean split = true;
boolean last =true;
while(high >= low) {
if(split){
pivot = (high+low)/4;
last=true;}
else
{ pivot = (high+low)/2;
split=true;
last=false;
}
if(Array[pivot] == item)
{ counter++;
System.out.println("Pivot"+pivot);
return counter;
}
if(Array[pivot] < item) {
low = pivot + 1;
counter++;
}
if(Array[pivot] > item) {
high = pivot - 1;
counter++;
if (last)
split=false;
}
}
return 0;
}
It doesnt work, maybe there is a simplier strategy to do that? The hardest part is to make it remember that it already splited in half once:/
Your formula to determine the pivot is wrong for the 3/4 split. If you want to split an interval between low and high at some point c with 0 <= c <=1, you get:
pivot = low + c * (high - low)
= (1 - c) * low + c * high
This wil give you low for c == 0, highfor c == 1 and for your 3/4 split:
pivot = 0.75 * low + 0.25 * high
or, with integer arithmetic:
pivot = (3 * low + high) / 4
In particular, the factors for low and high should sum up to 1.
I also think that your function has a logic error: You return the recursion depth, which has no meaning to the array. You should return the pivot, i.e. the array index at which the item is found. That also means that you can't return 0 on failure, because that's a valid array index. Return an illegal index like -1 to indicate that the item wasn't found.

Categories