I've copied code from the book "grokking algorithms" for finding an item in the list using the binary search algorithm. The code launches great and returns no errors. However, sometimes it just doesn't work for certain numbers, for example if i call it to find "1". What's wrong?
def binary_search(list, item):
low = 0
high = len(list) - 1
while low <= high:
mid = (low + high)/2
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid + 1
else:
low = mid + 1
return None
list1 = []
for i in range (1, 101):
list1.append(i)
print(list1)
print(binary_search(list1, 1))
Two issues:
Use integer division (so it will work in Python 3): mid = (low + high)//2
When guess > item you want to exclude the guessed value from the next range, but with high = mid + 1 it still is in the range. Correct to high = mid - 1
Related
So I understand conceptually how binary search works, but I always have problems with implementing it when trying to find an index in an array. For instance, for the Search Insert Position on LC, this is what I wrote:
def searchInsert(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if target > nums[-1]:
return len(nums)
low = 0
high = len(nums) - 1
while high > low:
mid = (low + high) // 2
if nums[mid] == target:
return mid
elif nums[mid] > target:
high = mid
else:
low = mid + 1
return low
It works, but I don't understand why I have to update low as mid + 1 instead of updating low as mid. Similarly, why am I updating high as mid instead of mid - 1. I've tried updating low/high as every combination of mid, mid - 1, and mid + 1 and the above is the only one that works but I have no idea why.
When implementing binary search for these kinds of problems, is there a way to reason through how you update the low/high values?
This is personal favorite:
while high >= low:
mid = (low + high) // 2
if nums[mid] >= target:
high = mid - 1
else:
low = mid + 1
return low
# or return nums[low] == target for boolean
It has difference in the case that has same values.
for example, Let's assume the array is [1,1,2,2,3,3,3,3,4].
with your function, search(arr, 1) returned 1 BUT search(arr, 2) returned 2.
why does it returned most RIGHT index on the interval 1s, and returned most LEFT index on 2s?
As i think, the key is at if nums[mid] >= target:.
when it finds the target exactly same one, the range changes by high = mid - 1. it means high might not be answer because the answer we found is mid. [1]
At the last step of binary search, the range is going to close to zero. and finally loop breaks by they crossed. thus, the answer must be low or high. but we know high is not an answer at [1].
Is giving me an error in this code: c is a list or an array // k = is the number we want to check
def binary_search(c,k):
low = 0
high = len(c) - 1
while low <= high:
mid = floor((low + high) / 2)
if c[mid] == k:
return True
elif c[mid] > k:
high = mid - 1
else:
low = mid - 1
return False
And this was the error: File "C:/Users/JJ/OneDrive - ISCTE-IUL/EDA/Aula3.py", line 108, in binary_search
mid = floor((low + high) / 2)
KeyboardInterrupt
I don't why is this happening, so i need all the help I can get. Thanks for your time
There is a bug in your algorithm. Whenever you search for a number that does not exist in the list, your algorithm will keep running an infinite loop.
The python code below seems to work fine. If I change the code as per the comments, it still seems to work. Are there conditions where the algorithm will fail if I use high = middle instead of high = middle - 1 and low = middle instead of low = middle + 1?
haystack = [3,5,7,8,9,23,65,89,100]
needle = 89
low = 0
high = len(haystack)
while (low < high):
middle = (high + low)//2
if (needle < haystack[middle] ):
high = middle - 1 # what about just high = middle?
elif (needle > haystack[middle]):
low = middle + 1 # what about just low = middle?
elif (needle == haystack[middle]):
print ("The needle was found at index " + str (middle))
break
That's because you are considering only on cases where the value is in the list (Maybe there is a case where the list contains the element and these lines are needed but i couldn't think of one)
think of the following simple case:
haystack = [1,2]
needle = 3
the rest of the code...
you will be stuck in an infinite loop without the current version of the code.
Note: as #vivek_23 mentioned you are already checking middle so there is no need for that extra iteration.
In order for the loop to complete, each iteration must decrease the range by at least 1. If it doesn't you get an infinite loop.
If you ever reach a point where low is an even number and high = low+1, the calculation for middle will always return low. You will continually test the same location.
The explanation as to why you would enter an infinite loop has to do with your condition:
while (low < high):
Not updating the conditions (low or high) would mean that your loop condition does not change and low (if it begins lower than high) will forever be less than high.
Another note is, it would help (you) to break code up into functions. It will make the debugging process easier for you in the future. Here's a not-so-pythonic implementation:
def binary_search(sorted_list, target_value, low, high):
if (low > high):
return False
mid = (low + high) // 2
if (mid == 0 and sorted_list[mid] != target_value):
return False
if sorted_list[mid] == target_value:
return True
elif sorted_list[mid] < target_value:
return binary_search(sorted_list, target_value, mid + 1, high)
else:
return binary_search(sorted_list, target_value, low, mid)
If you want to ensure you can reach every item in a list, try testing if your algorithm finds everything in the list:
sorted_list = [1, 2, 3, 4, 5, 6, 7, 8, 8, 9]
not_in_list = 42
def test_binary_search(sorted_list):
for each_item in sorted_list:
found = binary_search(sorted_list, each_item, 0, len(sorted_list) - 1)
if not found:
print("{} not found by binary_search".format(each_item))
else:
print("found {} ".format(each_item))
test_binary_search(sorted_list)
Similarly, you'd like your algorithm to behave correctly when given an item that is not in your list.
def test_item_not_found(sorted_list, item):
expected = False
actual = binary_search(sorted_list, item, 0, len(sorted_list) - 1)
status = "Passed" if actual == expected else "Failed"
print("status {} expected: {}, actual: {}".format(status, expected, actual))
test_item_not_found(sorted_list, not_in_list)
I am trying to implement a solution using binary search. I have a list of numbers
list = [1, 2, 3, 4, 6]
value to be searched = 2
I have written something like this
def searchBinary(list, sval):
low = 0
high = len(list)
while low < high:
mid = low + math.floor((high - low) / 2)
if list[mid] == sval:
print("found : ", sval)
elif l2s[mid] > sval:
high = mid - 1
else:
low = mid + 1
but when I am trying to implement this, I am getting an error like: index out of range. Please help in identifying the issue.
A few things.
Your naming is inconsistent. Also, do not use list as a variable name, you're shadowing the global builtin.
The stopping condition is while low <= high. This is important.
You do not break when you find a value. This will result in infinite recursion.
def searchBinary(l2s, sval): # do not use 'list' as a variable
low = 0
high = len(l2s)
while low <= high: # this is the main issue. As long as low is not greater than high, the while loop must run
mid = (high + low) // 2
if l2s[mid] == sval:
print("found : ", sval)
return
elif l2s[mid] > sval:
high = mid - 1
else:
low = mid + 1
And now,
list_ = [1, 2, 3, 4, 6]
searchBinary(list_, 2)
Output:
found : 2
UPDATE high = len(lst) - 1 per comments below.
Three issues:
You used l2s instead of list (the actual name of the parameter).
Your while condition should be low <= high, not low < high.
You should presumably return the index when the value is found, or None (or perhaps -1?) if it's not found.
A couple other small changes I made:
It's a bad idea to hide the built-in list. I renamed the parameter to lst, which is commonly used in Python in this situation.
mid = (low + high) // 2 is a simpler form of finding the midpoint.
Python convention is to use snake_case, not camelCase, so I renamed the function.
Fixed code:
def binary_search(lst, sval):
low = 0
high = len(lst) - 1
while low <= high:
mid = (low + high) // 2
if lst[mid] == sval:
return mid
elif lst[mid] > sval:
high = mid - 1
else:
low = mid + 1
return None
print(binary_search([1, 2, 3, 4, 6], 2)) # 1
I am trying to change my code so instead of finding a specific value of the array it will output the value of an interval if found, example being 60-70. Any help is appreciated.
def binary (array, value):
while len(array)!= 0:
mid = len(array) // 2
if value == array[mid]:
return value
elif value > array[mid]:
array = array[mid+1:]
elif value < array [mid]:
array = array[0:mid]
sequence = [1,2,5,9,13,42,69,123,256]
print( "found", binary(sequence,70) )
I have this so far and want it to find an specified interval, so if i specify 60-70 it will find what is in between.
Actually this is pretty simple:
While searching for the elements in the interval (lower, upper), perform a binary search on the array arr for the index of the smallest element arr[n], such that arr[n] >= lower and the index of the largest element arr[m], such that arr[m] <= upper.
Now there are several possibilities:
n < m: there exist multiple solutions in the array. All of the are in the subarray starting at index n up to index m inclusively
n = m: there exists precisely one solution: arr[n]
n > m: no solutions exist
Searching for values beyond a certain threshold can be done using binary search like this:
def lowestGreaterThan(arr, threshold):
low = 0
high = len(arr)
while low < high:
mid = math.floor((low + high) / 2)
print("low = ", low, " mid = ", mid, " high = ", high)
if arr[mid] == threshold:
return mid
elif arr[mid] < threshold and mid != low:
low = mid
elif arr[mid] > threshold and mid != high:
high = mid
else:
# terminate with index pointing to the first element greater than low
high = low = low + 1
return low
Sorry bout the looks of the code, my python is far from perfect. Anyways, this ought to show the basic idea behind the approach. The algorithm basically searches for the index ind of the first element in the array with the property arr[ind] >= threshold.