How can I put this Binary Search to work? - python

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.

Related

Find max element using recursion

I am new to recursion and the task is to find the POSITION of largest element in the array using recursion. This is my code:
def calc(low , high):
print(low, high)
if low == high:
return low
max_1 = calc(low , low +high//2)
max_2 = calc(low + high//2 , high)
if a[max_1] > a[max_2]:
return max_1
a = [4,3,6,1,9]
print(calc(0 , len(a)))
What am I doing wrong?
While google gives me solutions for finding the max element in array none of them have solutions for finding position of max element. Thanks in advance.
You are almost there. Two tiny mistakes are:
Base case should be low + 1 == high
Mid point should be (low + high) // 2
def calc(low , high):
if low + 1 == high:
return low
max_1 = calc(low , (low + high) // 2)
max_2 = calc((low + high) // 2 , high)
if a[max_1] > a[max_2]:
return max_1
else:
return max_2
a = [4,3,6,1,9]
print(calc(0 , len(a)))
## 4
Your solution generates infinite recursion due to the wrong base case and the mid-point.
When low == 0 and high == 1, since low != high you trigger two calls
max_1 = calc(low , low + high // 2)
max_2 = calc(low + high // 2 , high)
which are evaluated to
max_1 = calc(0, 0) ## This got returned to 0, because low == high
max_2 = calc(0, 1) ## Notice here again low == 0 and high == 1
The second call max_2 = calc(0, 1) triggers again another two calls one of which is again max_2 = calc(0, 1). This triggers infinite recursions that never returns back to max_2 and max_2 will never get evaluated and thus neither the lines after it (if a[max_1] > a[max_2]: ... ).
That is why you should check for base case low + 1 == high instead of low == high. Now you could test yourself and guess if the following code will generate infinite recursion or not. Will this time max_2 gets returned value assigned to it and the lines after it get evaluated?
def calc(low , high):
if low + 1 == high: # Here is changed from your solution
return low
max_1 = calc(low , low + high // 2) # Here is same to your solution
max_2 = calc(low + high // 2 , high) # Here is same as well
if a[max_1] > a[max_2]:
return max_1
else:
return max_2
If you get the answer right, you are half way in understanding your mistake. Then you can play with different mid-point and print at each level of recursion to see how that affects results and get a full understanding.
I think this is what you are trying to do. You should pass list slices to the function - this makes it much simpler than trying to pass low and high indices, and avoids accessing the list as a global variable - and add the midpoint to the resulting index that comes from the right hand side of the list.
def idxmax(l):
if len(l) == 1:
return 0
midpoint = len(l) // 2
a = idxmax(l[:midpoint])
b = idxmax(l[midpoint:]) + midpoint
if l[a] >= l[b]:
return a
else:
return b
print(idxmax([4,3,6,1,9]))
This returns the index of the first occurrence of the maximum, e.g. idxmax([4,9,3,6,1,9]) == 1
If you want to implement it by passing indices instead of slices (possibly more efficient by not making multiple copies of the list), you could do it like this:
def idxmax(l, start=0, end=None):
if end is None:
end = len(l) - 1
if end == start:
return start
midpoint = (start + end) // 2
a = idxmax(l, start, midpoint)
b = idxmax(l, midpoint + 1, end)
if l[a] >= l[b]:
return a
else:
return b
print(idxmax([4,3,6,1,9]))
I believe the task was to find the POSITION of the max number only (and not the value itself).
So, the function starts by comparing the last value with the max value of the list and returns the length of the list (minus one) as position if True. then it is recursively called to a shorter list and compared again until it left with only one value in the list
def calc(lst):
if lst[len(lst) - 1] == max(lst):
return len(lst) - 1
if len(lst) > 1:
return calc(lst[:-1])
print(calc([0, 1, 2, 3, 4, 5, 6])) # 6
print(calc([7, 1, 2, 3, 4, 5, 6])) # 0
print(calc([0, 1, 8, 3, 4, 5, 6])) # 2

Why does binary search algorithm not work?

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

I am attempting to create a binary search program for a school project, but certain numbers cause infinite recursion

This is my code:
list = []
line = 50
def genList(list):
i = 0
while i < 1000:
list.append(i)
i += 1
return list
def displayList(list, line):
i = 0
while i < 1000:
if i != 0 and i % line == 0:
print('\n')
print('{0:03}'.format(list[i]), end = ' ')
i += 1
print('\n')
def binarySearch(min, max, list, goal):
mid = min + (max - 1) // 2
if list[mid] == goal:
return mid
elif list[mid] > goal:
return binarySearch(min, mid - 1, list, goal)
else:
return binarySearch(mid + 1, max, list, goal)
genList(list)
displayList(list, line)
goal = int(input('What number do you want to find, from 0 to 999?\n'))
result = binarySearch(0, len(list) - 1, list, goal)
print(result)
...and like I said, certain numbers work but others don't, for example 999 will return 999 but 998 will return:
RecursionError: maximum recursion depth exceeded in comparison
Anyone know what's wrong with it? I'm at a bit of a loss...
mid = min + (max - 1) // 2 probably should be (min + max) // 2. Also, your code should have a base case that returns a value, say -1 if the element does not exist within the list. Something like if min > max: return -1 right at the beginning of your search function.
This line is definitely wrong:
mid = min + (max - 1) // 2
Perhaps you meant ( as #Nikolaos Chatzis pointed out)
mid = min + (max - min) // 2
but following works as well and needs one less operation:
mid = (min + max) // 2
Additionally you should abort recursion if minval > maxval (if the value you search for is not in the list)
I didn't look if you can do better. but this should work
def binarySearch(minval, maxval, list_, goal):
mid = (minval + maxval) // 2
# print(minval, maxval, mid, goal) # uncomment for debugging
if minval > maxval: # couldn't find search value
return None
if list_[mid] == goal:
return mid
elif list_[mid] > goal:
return binarySearch(minval, mid - 1, list_, goal)
else:
return binarySearch(mid + 1, maxval, list_, goal)
Also don't hesitate to add print statements (or logs) for debugging.
It makes it very often obvious what is going wrong.
What you could also do is run some simple test code to show that it works for all cases:
for v in range(1000):
assert binarySearch(0, len(list_) -1, list_, v) == v
Also as general recommendation to not stumble into funny or strange bugs.
min, max and list are all python keywords.
You can avoid funny surprises by not using variables with these names. It makes your code also look better with many basic syntax highlighters.
Using them as variable names is not forbidden, but I think it's not a good idea.

Binary Search Algorithm Testing Repeated Values

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)

Binary Search Python Implementation [duplicate]

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

Categories

Resources