Binary Search...Is something wrong with this code - c

I have only included the function. I am trying to implement binary search but for some reason it is not working. What I am really trying to determine is whether the algorithm is correct. It appears fine to me....but may be I am wrong. I know that the algorithm has to be sorted first but that will be taken care of in another function. Is the algorithm wrong or right? For some unknown reason the programme hangs...I have taken care of all the headers etc....i am off target or what? Thanks. Function is in C.
bool search(int value, int values[], int n)
{
int i;
int begin = 0;
int end = n-1;
int middle = (begin + end)/2;
for ( i = 0; middle <=end; i++)
if (value == values[middle])
{
return true;
break;
}
else if (value > values[middle])
{
begin = values[middle +1];
}
else
{
end = values[middle -1];
}
return false;
}

You do not need the loop on i.
Your loop would be on begin and end, such as while( begin < end )
Depending on how values[middle] compare with values[begin] and values[end], you have to adjust begin (begin = middle + 1;) or end (end = middle - 1;). Beware of the boundary cases!

(begin + end) / 2 may caused integer overflow in C. Try to use begin + (end - begin) / 2 instead.

What is the algorithm you have in mind ? I would suggest to go through the algorithm first .
http://en.wikipedia.org/wiki/Binary_search

Your middle is calculating to the end. It should be middle = (end - begin) / 2

Related

How to determine why a piece of code produces an infinite loop?

This is my binary search function. I can't seem to find the error but every time I try to run the code it gives me a segmentation fault 11. I feel like my mistake has to do with my last else if statement.
void binary(struct list *A[], char search[15], int start, int
end) {
if(start <= end) {
int middle = (start + end)/2;
if(strcmp(search, A[middle]->name) == 0){
printf("found");
exit(0);
} else if (strcmp(search, A[middle]->name) > 0){
int start = middle + 1;
int end = end;
binary(A, search, start, end);
} else if (strcmp(search, A[middle]->name) < 0){
int start = start;
int end = middle - 1;
binary(A, search, start, end);
} else if (start == (end - 1)) {
printf("%s was not found in the list", search);
exit(0);
}
}
}
These statements
int end = end;
int start = start;
do not make sense because the variables are initialized by themselves while they have indeterminate values.
There is no need to declare local variables end and start. Use the parameters.
This statement
} else if (start == (end - 1)) {
printf("%s was not found in the list", search);
exit(0);
}
also does not make sense because initially the variables start and end satisfy the condition of enclosing if statement
if(start <= end) {
And at last it does not make sense to use standard function exit instead of the return statement..
First, as others already pointed out, the assignment like int end = end is asking for troubles. Do a simple test and print the start and end values at the beginning of the function to see what happens as your program works...
Next, you do not need recursion here! Shrinking the search area can be easily done in a simple loop:
void binary(struct list *A[], char search[15], int start, int end) {
while(start <= end) {
int middle = start + (end - start)/2;
int cmpresult = strcmp(search, A[middle]->name);
if (cmpresult > 0) {
start = middle + 1;
} else if (cmpresult < 0) {
end = middle - 1;
} else { // cmpresult == 0
printf("found at %d", middle);
return;
}
}
printf("%s was not found in the list", search);
}
Finally, please note the middle calculation – adding (start + end) is a common step to do that, however it may lead to error if the array is too long; specifically, if the array length exceeds a half of the maximum value representable by int type.

Divide and Conquer in C, may reach end of non-void function

I am trying to get this divide and conquer to work, but the compiler is giving me:
control may reach end of non-void function
I have read through similar scenarios, and understand the error implies the program might run forever without a return. I have reviewed some scenarios, which were resolved by using "else" instead of "if else" (as you should to begin with). However, that didn't help.
I am aware that using a do while loop and return in this scenario is redundant, I was fiddling with it in hopes to trick the compiler.
Where is the error?
bool search(int value, int values[], int n)
{
int sorted = 0;
int min = 0;
int max = n;
int mid = n / 2;
do
{
//mid is value
if (value == values[mid])
{
printf("value found!");
sorted = 1;
return 1;
}
//search right
else if (values[mid] < value)
{
min = mid + 1;
mid = (max - mid) / 2 ;
}
//search left
else if (values[mid] > value)
{
max = mid - 1;
mid = (max - mid) / 2;
}
// DNE
else
{
printf("value not found");
sorted = 1;
return 0;
}
}
while(sorted == 0);
}
Every branch of code should have a return if function suppose to return a value.
In your case after while you have to put a return. How ever, Your logic is also slightly wrong. In your code there is not any need of sorted variable and the terminating condition is also wrong as well as the way of calculating mid is also wrong. Don't worry here is your updated code:
bool search(int value, int values[], int n)
{
//int sorted = 0;
int min = 0;
int max = n;
int mid = n / 2; // S1
do
{
//mid=(max+min)/2; // S2
//mid is value
if (value == values[mid])
{
//printf("value found!");
//sorted = 1;
return 1;
}
//search right
else if (values[mid] < value)
{
min = mid + 1;
mid = (max + min) / 2 ; // S3
}
//search left
else if (values[mid] > value)
{
max = mid - 1;
mid = (max + min) / 2; //S4
}
// DNE
else
{
//printf("value not found");
//sorted = 1;
return 0;
}
}
while(min!=max);
return 0;
}
So if your function is returning 1 it mean value found otherwise value doesn't found.
you also can comment S1, S3 and S4 and uncomment S2 for minimum line of code.
And the way of finding mid will be mid=(max+min)/2.
Ignoring other problems in your code (the loop will never terminate under certain conditions although I didn't study it too closely), let's just look at why the compiler complains, because it's quite interesting.
Your compiler complains about that you don't have a return at the end of the function (pretty much what we can read from the warning message) after the while loop. You loop runs:
while(sorted == 0);
And everywhere you change sorted, you do this:
sorted = 1;
return X;
So it is pretty obvious to you and me that the while condition will always be true and you won't fall out of the while loop and need a return after it. Every time you make the while condition not true, you also return immediately. But the compiler doesn't know that. It probably could figure it out with a little bit more effort, but it can never be written to know in all cases and you wouldn't accept a compiler that slow anyway. The problem of fully analyzing any arbitrary bit of code to know what you and I know (that we'll never fall out of the loop) is pretty much the halting problem (if you don't know what it is, you should, google it).
In situations like this we need to work with the compiler and help it understand. I would add a return sorted; at the end of the function, change the loop condition to while (1) and replace sorted = 1; return X; with sorted = X; break; to break out of the loop and have the function return from just one place (it is much easier to read functions that have just one or very few return statements). Or just remove the sorted variable, do the returns properly and loop forever.
I think your compiler doesn't like your understanding of Control Flow. As a beginner this is one of the most cringing problems.
Your error explained: Control may reach end of non-void function. Whenever you branch your code (i.e. use loops, if, switch etc) you have to explicitly write what each branch will do. So in your code, you must return a bool by any means necessary.
I am unable to discern where the error might be, it would be great if someone can show me.
See we have two return x; statements, one in if block and second in else block. Now we think that control should return from either of these blocks conveniently. But your compiler is "concerned" about what will happen if control didn't enter either of these blocks and loop ends. Moreover, what will happen after the loop, how can control ever return to main? Hence the error.
Now what we can do to fix the problem is add a return 0; just before the end of the function. That will surely make the error disappear. However, there are better ways to solve this problem:
bool search(int value, int values[], int n) {
int min = 0, max = n;
int mid = n / 2;
bool is_found = 0;
while (min != max) {
if (value == values[mid]) {
is_found = 1;
break;
}
else if (value > values[mid]) {
min = mid + 1;
mid = (max + min) / 2;
}
else if (value < values[mid]) {
max = mid - 1;
mid = (max + min) / 2;
}
else break;
}
return is_found;
}

Binary Search Using Recursive Function in C

So I'm trying to write a binary search function that uses recursion and keep getting a segmentation fault if I go past two values in the array. I've looked at a bunch of other code doing what I'm trying to do and as far as I can see they appear to do the same thing. I'm a very novice programmer and feel like I'm banging my head against the wall with this. Any help would be appreciated.
int search(int value, int array[], int start, int end)
{
//Define new variables for use in recursion
int sizeArray, middleOfArray;
//Get size of array
sizeArray = (end - start) + 1;
//Find midpoint of array based off size
middleOfArray = sizeArray / 2;
//Base Case 1, if array unscannable, return -1
if (start > end) {
return -1;
}
//Recursive Cases
else
{
//If midpoint in array is > target value,
//Search from beginning of array->one below midpoint
if (array[middleOfArray] > value){
return search(value, array, start, middleOfArray - 1);
}
//If midpoint in array is < target value,
//search from one above midpoint->end of array
else if (array[middleOfArray] < value) {
return search(value, array, middleOfArray + 1, end);
}
//If none of the other cases are satisfied, value=midpoint
//Return midpoint
else {
return middleOfArray;
}
}
}
The problem is here:
middleOfArray = sizeArray / 2;
It should be like this:
middleOfArray = start + sizeArray / 2;
You can also get middle of array like this. Which will save you from one extra variable sizeofArray.
middleofArray=(start+end)/2;

Why is this binary search giving me an infinite loop?

I am trying to do a binary search. I really can't think of why I am getting an infinite loop? Is is because I ignored the null value somewhere? The value, values[], and n are being provided by a different file, and they are written by someone else, and are, for the purposes of this question, perfectly coded.
bool search(int value, int values[], int n)
{
int upper_bound = n - 1;
int lower_bound = 0;
int middle = (upper_bound + lower_bound) / 2;
while (lower_bound <= upper_bound)
{
if (values[middle] == value)
{
return true;
}
else if (values[middle] > value)
{
upper_bound = middle - 1;
}
else if (values[middle] < value)
{
lower_bound = middle + 1;
}
else
{
return false;
}
}
return false;
}
Thank you all so much.
You need to calculate the value of middle inside the while loop:
while (lower_bound <= upper_bound){
int middle = (upper_bound + lower_bound) / 2;
...
}
As the value of middle should change every time you are changing the value of either lower_bound or upper_bound.
the middle value is fixed. It is not changing as the values of upper_bound and lower_bound are changing.

Simplify/Neatify this two-way loop?

I've got my wires crossed somewhere (or I had not enough sleep). I need a two-way loop, and my current code is just plain ugly.
Problem: I am running along a linear datastructre using an index. I have an starting index, lets say 120. I want to run alternating into both directions.
Example:
120,121,119,122,118,123,117,...
I have a stopping criterion which needs to be met for each direction separately. If it is met for one direction, I only want to run into the other direction, if both are met I need to exit the loop. In addition I need to stop if the next index is invalid (end of data structure, say smaller than 0 or bigger than 200).
Example: Stopping execution at 116 backwards and 130 forward:
120,121,119,122,118,123,117,124,116,(break),125,126,127,128,129,130.
Running into one direction first, then the other one is unfortunately not an option.
My current code is plain ugly. It is a lot of lines without containing any "productive" code. Only iteration logic:
int start_idx = 120;
int forward_idx = start_idx;
int backward_idx = start_idx;
bool next_step_forward = true; //should next step be forward or backward?
int cur_idx;
while(backward_idx >= 0 || forward_idx >= 0)
{
if(next_step_forward //if we should step forward
&& forward_idx >= 0) //and we still can step forward
{
cur_idx = ++forward_idx;
if(forward_idx >= 200) //200 is fictive "max index"
{
next_step_forward = false;
forward_idx = -1; //end of data reached, no more stepping forward
continue;
}
if(backward_idx >= 0)
{
next_step_forward = false;
}
}
else if(!next_step_forward
&& backward_idx >= 0)
{
cur_idx = --backward_idx;
if(backward_idx < 0) //beginning of data reached, no more stepping backward
{
next_step_forward = true;
continue;
}
if(forward_idx >= 0)
{
next_step_forward = true;
}
}
else
{
next_step_forward = !next_step_forward; //ever hit?, just security case
continue;
}
//loop body
//do something with cur_idx here
if(stoppingCriterionMet())
{
if(cur_idx > start_idx)
{ //this was a forward step, stop forward stepping
forward_idx = -1;
}
else
{ //this was backward step, stop backward stepping
backward_idx = -1;
}
}
}
Am I missing anything? Any hints appreciated. Thanks.
Edit 1: There are lots of very nice answers, which put "do something with cur_idx" into a separate function. While this is a perfect idea for the way my question was asked, I prefer putting the iterating code somewhere else and leave the productive code there. I have a long algorithm and want to split it up after it is finished to minimize rearangement work.
How about this?
void do_loop(SomeType *arr, int start, int low, int high, int arr_max)
{
int downwardIndex, upwardIndex;
downwardIndex = upwardIndex = start;
while (downwardIndex > 0 && upwardIndex < arr_max) {
if (downwardIndex < low && upwardIndex > high) {
break;
}
if (downwardIndex > low) {
processElement(arr[downwardIndex]);
downwardIndex--;
}
if (upwardIndex < high) {
processElement(arr[upwardIndex]);
upwardIndex++;
}
}
}
It so happened that I coded almost this problem today. And I used a C# iterator function to do it. But I think you want a more generic solution.
If you use a language where you can build your own iterators (C++,Java,C#), it's easy. You just make a custom iterator that initially spits out numbers starting from the center. Then you give the iterator an extra function to tell it to stop running in the current direction.
If you're doing something like this in C (it looks C to me), you can mimic this with a struct containing the iterator state, and functions that you call to step it forward or stop it.
First pass at hacking this (assuming C - adaptations needed for other languages, but the concepts are basically language neutral):
void pass1(int start_x, int lo_limit, int hi_limit)
{
assert(start_x >= lo_limit && start_x <= hi_limit);
int lo_x = start_x - 1;
int hi_x = start_x + 1;
Process(start_x);
if (StopCriterion(start_x))
return; // Is that correct?
while (lo_x >= lo_limit && hi_x <= hi_limit)
{
Process(lo_x);
if (StopCriterion(lo_x))
lo_x = lo_limit - 1;
else
lo_x--;
Process(hi_x);
if (StopCriterion(hi_x))
hi_x = hi_limit + 1;
else
hi_x++;
}
while (lo_x >= lo_limit)
{
Process(lo_x);
if (StopCriterion(lo_x))
lo_x = lo_limit - 1;
else
lo_x--;
}
while (hi_x <= hi_limit)
{
Process(hi_x);
if (StopCriterion(hi_x))
hi_x = hi_limit + 1;
else
hi_x++;
}
}
It is not clear what should happen if the starting position matches the stop criterion. Should the search stop altogether, or should it continue upwards, or downwards, or both ways. I chose 'stop altogether', but a case could be made for any of the options listed. In the case of 'both', you would not even bother to run the stop criterion check.
I also chose to do the lower before the upper direction; it is clearly trivially reversed. The order of the final two loops doesn't matter because if both directions terminate in the same iteration, neither trailing loop is executed; if only one direction is terminated, the corresponding loop won't execute at all - only the other will.
Since there is still repeated code in there:
void pass2(int start_x, int lo_limit, int hi_limit)
{
assert(start_x >= lo_limit && start_x <= hi_limit);
int lo_x = start_x - 1;
int hi_x = start_x + 1;
Process(start_x);
if (StopCriterion(start_x))
return; // Is that correct?
while (lo_x >= lo_limit && hi_x <= hi_limit)
{
Process_lo(&lo_x, lo_limit);
Process_hi(&hi_x, hi_limit);
}
while (lo_x >= lo_limit)
Process_lo(&lo_x, lo_limit);
while (hi_x <= hi_limit)
Process_hi(&hi_x, hi_limit);
}
void Process_lo(int *lo_x, int lo_limit)
{
Process(*lo_x);
if (StopCriterion(*lo_x))
*lo_x = lo_limit - 1;
else
*lo_x--;
}
void Process_hi(int *hi_x, int hi_limit)
{
Process(*hi_x);
if (StopCriterion(*hi_x))
*hi_x = hi_limit + 1;
else
*hi_x++;
}
Visibility controls (static functions) etc left out as details of the implementation language.
This is how I'd approach it in C#:
const int UPPER_BOUND = 200;
const int LOWER_BOUND = 0;
const int START = 120;
bool foundlower = false, foundupper = false;
int upper, lower;
upper = lower = START;
while (!foundlower || !foundupper) {
if (!foundlower) {
if (--lower <= LOWER_BOUND) foundlower = true;
if (stoppingCriterionMet(lower)) foundlower = true;
}
if (!foundupper) {
if (++upper >= UPPER_BOUND) foundupper = true;
if (stoppingCriterionMet(upper)) foundupper = true;
}
}

Resources