Binary search 1/4 modification - java

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.

Related

Binary-search with duplicate elements in array

I want find if there is a single element in a list of duplicate elements.
For this code
private static int findDuplicate(int array[]) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = array[mid];
if (midVal == mid)
low = mid + 1;
else
high = mid - 1;
}
return high;
}
It find the duplicate number but I want to find only the single number
in the duplicate and sorted array.
For example, given this int[] input:
[1,1,2,2,3,3,4,5,5]
Output would be '4'.
Or this int[] input:
[1,1,2,2,3,4,4,5,5,6,6]
Output would be '3'.
In this int[] input:
[1,1,2,7,7,9,9]
Output would be '2'.
I'm working in Java now but any language or psuedo-code is fine.
I know the obvious traversal at O(n) linear time, but I'm trying to see if this is possible via binary search at O(log n) time.
The elements are sorted and only duplicate twice!
I know the way with simple loop but I want to do it by binary search.
Consider each pair of 2 consecutive elements: (the pairs with 2 elements different are highlighted) (note that there's a stray element at the end)
(1 1) (2 2) (3 3) (4 5) (5 6) (6 7) (7 8) (8)
Observe that the only non-duplicated element will make the corresponding pair and all the later pairs have different values, the pairs before that have the same value.
So just binary search for the index of the different pair.
This algorithm also don't require that the list is sorted, only that there's exactly one element which appears once, and all other elements appear twice, in consecutive indices.
Special case: if the last element is the unique one, then all the pairs will have equal values.
Every pair of same values will be like below in terms of indices:
(0,1),
(2,3),
(4,5)
(6,7)
etc. You can clearly see that if index is even, check with next element for similarity. If index is odd, you can check with previous value for similarity.
If this symmetry is broken, you can move towards left side or if everything is ok, keep moving right.
Pseudocode(not tested):
low = 0,high = arr.length - 1
while low <= high:
mid = (low + high) / 2
if mid == 0 || mid == arr.length - 1 || arr[mid] != arr[mid-1] and arr[mid] != arr[mid + 1]: // if they are corner values or both left and right values are different, you are done
return arr[mid]
if(mid % 2 == 0):
if arr[mid + 1] != arr[mid]: // check with next index since even for symmetry
high = mid
else:
low = mid + 2
else:
if arr[mid - 1] != arr[mid]: // check with prev index since odd for symmetry
high = mid
else:
low = mid + 1
return -1

How to implement a lower_bound binary search algorithm in 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);
}
}

Binary search recursive number of calls?

So I was wondering, in my book, recursive binary search is implemented as follows:
private static int bin(int[] arr, int low, int high, int target){
counter++; //ignore this, it was to count how many calls this function invocated
if(low > high) return -1;
else{
int mid = (low+high) / 2;
if(target == arr[mid]) return mid;
else if(target < arr[mid]) return bin(arr, low, mid-1, target);
else return bin(arr, mid + 1, high, target);
}
}
And it says that "If n, the number of elements, is a power of 2, express n as a power of 2... Case 3: The key is not in the array, and its value lies between a[0] and a[n-1]. Here the number of comparisons to determine that the key is not in the array is equal to the exponent. There will be one fewer comparison than in the worst case."
But when I sat down and found the number of function calls using an array {1,2,3,4,5,6,7,9} and key of 8, the number of calls was 4. The book says the number of COMPARISONS is 3 (which is excluding the 3rd line I am guessing?), but I'm pretty sure the number of function calls is 4. I also generalized this to an iterative implementation of binary search and generalized that the number of iterations, OR recursive function calls, is always floor(log base 2 ( n ) ) + 1.
Can explain what's going on here?
Only 3 target == arr[mid] comparisons are made. On the fourth iteration the base case if(low > high) is reached so the comparison is never made. As you stated: "Here the number of comparisons to determine that the key is not in the array is equal to the exponent." You are correct in that we are not dealing with the comparison statement on line 3. We are only concerned with the comparison statement for our target value.
Let's look at the iterations until either of the 2 base cases are reached.
Binary search for 8 in array {1,2,3,4,5,6,7,9}
First iteration:
low = 0
high = 7
mid = 3
arr[mid] = 4
(target == arr[mid]) == false
Second iteration:
low = 4
high = 7
mid = 5
arr[mid] = 6
(target == arr[mid]) == false
Third iteration:
low = 7
high = 7
mid = 7
arr[mid] = 7
(target == arr[mid]) == false
Forth iteration:
low = 8
high = 7
low > high == true
Also, the Big O notation is O(log n). The + 1 is considered insignificant in Big O and therefore not counted. Please see this list on Wikipedia for order of Big O functions from fastest to slowest.

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

Convert algorithm from o(n) to o(1)

Basically what I wanted to is if a number n is divisible by b for a(count) times, then find the a(count), and divide n by b for a(count) times.
That is,
count = 0;
while(n%b == 0)
n=n/b;
count = count + 1;
How to optimize this, so that everything can be obtained in one step
You can do it in O(log(a)) by applying binary search, on a sorted "list" to find the last element that equals 1.
The list is metaphoric, and each element in it is calculated on the fly when queried by a simple calculation:
list[i] = 1 n % a^i == 0
0 otherwise
You can first find the range of possible a's using exponention:
curr = b
tempA = 1
while n % curr == 0:
curr = curr * curr
tempA = tempA *2
And then, run the binary search on the range [tempA/2, tempA]. This range is of size (a/2), so finding the last "element" that the symbolic list holds 1 - is done in O(loga) multiplications.
Code + Demo:
private static int specialBinarySearch(int n, int b, int aLow, int aHigh) {
if (aHigh == aLow) return aHigh;
int mid = (aHigh - aLow)/2 + aLow;
//pow method can be optimized to remember pre-calculated values and use them
int curr = (int)Math.round(Math.pow(b, mid));
if (n % curr == 0) { //2nd half, or found it:
if (n % (curr*b) != 0) return mid; //found it
return specialBinarySearch(n, b, mid+1, aHigh); //2nd half
}
else return specialBinarySearch(n, b, aLow, mid); //first half
}
public static int findA(int n, int b) {
int curr = b;
int tempA = 1;
while (n % curr == 0) {
curr = curr * curr;
tempA = tempA *2;
}
return specialBinarySearch(n, b, tempA/2, tempA);
}
public static void main(String args[]) {
System.out.println(findA(62,2)); //1
System.out.println(findA(1024,2)); //10
System.out.println(findA(1,2)); //0
System.out.println(findA(100,2)); //2
System.out.println(findA(6804,3)); //5
}
You cannot solve this in O(1) but there is a different kind of approach to this problem if you start using a numeric system where b is the base.
For example, if we have a number like 154200, and b is 10, we know the answer is 2 here immediately because we can simply count how many zeros there are on the right hand side.
Similarly, in binary, if b is 2, you simply count how many zeros there are on the right side with a binary representation.
If b is 5, we have to use the odd base 5 representation where a number like 8 is represented as 13. Again we know that the answer for a is zero is n=8 and b=5 because there are no zeros on the right hand side.
This won't necessarily give you speed gains except possibly in cases where b is a power of two where you can use bitwise logic to deduce the answer, but it gives you a different kind of way of looking at the problem lexically by digits instead of through arithmetic.

Categories