python3 binary search not working [duplicate] - python

I am trying to implement the binary search in python and have written it as follows. However, I can't make it stop whenever needle_element is larger than the largest element in the array.
Can you help? Thanks.
def binary_search(array, needle_element):
mid = (len(array)) / 2
if not len(array):
raise "Error"
if needle_element == array[mid]:
return mid
elif needle_element > array[mid]:
return mid + binary_search(array[mid:],needle_element)
elif needle_element < array[mid]:
return binary_search(array[:mid],needle_element)
else:
raise "Error"

It would be much better to work with a lower and upper indexes as Lasse V. Karlsen was suggesting in a comment to the question.
This would be the code:
def binary_search(array, target):
lower = 0
upper = len(array)
while lower < upper: # use < instead of <=
x = lower + (upper - lower) // 2
val = array[x]
if target == val:
return x
elif target > val:
if lower == x: # these two are the actual lines
break # you're looking for
lower = x
elif target < val:
upper = x
lower < upper will stop once you have reached the smaller number (from the left side)
if lower == x: break will stop once you've reached the higher number (from the right side)
Example:
>>> binary_search([1,5,8,10], 5) # return 1
1
>>> binary_search([1,5,8,10], 0) # return None
>>> binary_search([1,5,8,10], 15) # return None

Why not use the bisect module? It should do the job you need---less code for you to maintain and test.

array[mid:] creates a new sub-copy everytime you call it = slow. Also you use recursion, which in Python is slow, too.
Try this:
def binarysearch(sequence, value):
lo, hi = 0, len(sequence) - 1
while lo <= hi:
mid = (lo + hi) // 2
if sequence[mid] < value:
lo = mid + 1
elif value < sequence[mid]:
hi = mid - 1
else:
return mid
return None

In the case that needle_element > array[mid], you currently pass array[mid:] to the recursive call. But you know that array[mid] is too small, so you can pass array[mid+1:] instead (and adjust the returned index accordingly).
If the needle is larger than all the elements in the array, doing it this way will eventually give you an empty array, and an error will be raised as expected.
Note: Creating a sub-array each time will result in bad performance for large arrays. It's better to pass in the bounds of the array instead.

You can improve your algorithm as the others suggested, but let's first look at why it doesn't work:
You're getting stuck in a loop because if needle_element > array[mid], you're including element mid in the bisected array you search next. So if needle is not in the array, you'll eventually be searching an array of length one forever. Pass array[mid+1:] instead (it's legal even if mid+1 is not a valid index), and you'll eventually call your function with an array of length zero. So len(array) == 0 means "not found", not an error. Handle it appropriately.

This is a tail recursive solution, I think this is cleaner than copying partial arrays and then keeping track of the indexes for returning:
def binarySearch(elem, arr):
# return the index at which elem lies, or return false
# if elem is not found
# pre: array must be sorted
return binarySearchHelper(elem, arr, 0, len(arr) - 1)
def binarySearchHelper(elem, arr, start, end):
if start > end:
return False
mid = (start + end)//2
if arr[mid] == elem:
return mid
elif arr[mid] > elem:
# recurse to the left of mid
return binarySearchHelper(elem, arr, start, mid - 1)
else:
# recurse to the right of mid
return binarySearchHelper(elem, arr, mid + 1, end)

def binary_search(array, target):
low = 0
mid = len(array) / 2
upper = len(array)
if len(array) == 1:
if array[0] == target:
print target
return array[0]
else:
return False
if target == array[mid]:
print array[mid]
return mid
else:
if mid > low:
arrayl = array[0:mid]
binary_search(arrayl, target)
if upper > mid:
arrayu = array[mid:len(array)]
binary_search(arrayu, target)
if __name__ == "__main__":
a = [3,2,9,8,4,1,9,6,5,9,7]
binary_search(a,9)

Using Recursion:
def binarySearch(arr,item):
c = len(arr)//2
if item > arr[c]:
ans = binarySearch(arr[c+1:],item)
if ans:
return binarySearch(arr[c+1],item)+c+1
elif item < arr[c]:
return binarySearch(arr[:c],item)
else:
return c
binarySearch([1,5,8,10,20,50,60],10)

All the answers above are true , but I think it would help to share my code
def binary_search(number):
numbers_list = range(20, 100)
i = 0
j = len(numbers_list)
while i < j:
middle = int((i + j) / 2)
if number > numbers_list[middle]:
i = middle + 1
else:
j = middle
return 'the index is '+str(i)

If you're doing a binary search, I'm guessing the array is sorted. If that is true you should be able to compare the last element in the array to the needle_element. As octopus says, this can be done before the search begins.

You can just check to see that needle_element is in the bounds of the array before starting at all. This will make it more efficient also, since you won't have to do several steps to get to the end.
if needle_element < array[0] or needle_element > array[-1]:
# do something, raise error perhaps?

It returns the index of key in array by using recursive.
round() is a function convert float to integer and make code fast and goes to expected case[O(logn)].
A=[1,2,3,4,5,6,7,8,9,10]
low = 0
hi = len(A)
v=3
def BS(A,low,hi,v):
mid = round((hi+low)/2.0)
if v == mid:
print ("You have found dude!" + " " + "Index of v is ", A.index(v))
elif v < mid:
print ("Item is smaller than mid")
hi = mid-1
BS(A,low,hi,v)
else :
print ("Item is greater than mid")
low = mid + 1
BS(A,low,hi,v)
BS(A,low,hi,v)

Without the lower/upper indexes this should also do:
def exists_element(element, array):
if not array:
yield False
mid = len(array) // 2
if element == array[mid]:
yield True
elif element < array[mid]:
yield from exists_element(element, array[:mid])
else:
yield from exists_element(element, array[mid + 1:])

Returning a boolean if the value is in the list.
Capture the first and last index of the list, loop and divide the list capturing the mid value.
In each loop will do the same, then compare if value input is equal to mid value.
def binarySearch(array, value):
array = sorted(array)
first = 0
last = len(array) - 1
while first <= last:
midIndex = (first + last) // 2
midValue = array[midIndex]
if value == midValue:
return True
if value < midValue:
last = midIndex - 1
if value > midValue:
first = midIndex + 1
return False

Related

How can I return the index of mid in my recursive binary search program?

class Solution:
def binarysearch(self, arr, n, k):
# code here
first = 0
last = n - 1
mid = (first + last)//2
if(arr[mid]==k):
//Here
elif(k > arr[mid]):
self.binarysearch(arr[mid+1:n], len(arr[mid+1:n]), k)
elif(k < arr[mid]):
self.binarysearch(arr[0:mid], len(arr[0: mid]), k)
else:
return -1
I can't figure out how to get the index of mid as I am manipulating the array in every call. The parameters of the function are fixed, I can't give the first and last parameters to the function. I tried using the iterative approach and I got it but I am not able to do it like this. I just want the logic to get the index of mid in the code.
To get index of middle element in array you can pass an additional parameter(eg. start_index in below code) to function that keeps track of the starting index of current subarray
class Solution:
def binarysearch(self, arr, n, k, start_index):
# code here
first = 0
last = n - 1
mid = (first + last)//2
if(arr[mid]==k):
return start_index + mid # Return the index of mid in the original array
elif(k > arr[mid]):
return self.binarysearch(arr[mid+1:n], len(arr[mid+1:n]), k, start_index + mid + 1)
elif(k < arr[mid]):
return self.binarysearch(arr[0:mid], len(arr[0: mid]), k, start_index)
else:
return -1

return value was give as return -1, but output is displayed as none

this is Binary search program```
when the number doesn't exist a return value -1 is what expected followed by output statement will be displayed.but here I don't no why return value is none.can anyone please explain why this is happening.
def binary_serch(arr,element,r,l):
if r>l:
mid=l+(r-l)//2
if arr[mid]==element:
return mid
if arr[mid]>element:
return binary_serch(arr,element,mid+1,l)
elif arr[mid]<element:
return binary_serch(arr,element,mid-1,l)
else:
return -1
arr=[1,2,3,4,5]
element=6
result=binary_serch(arr,element,len(arr)-1,0)
num=len(arr)
print(result)
if result<num-1:
print("Element is present at index ",result)
else:
print("not found")
In binary_search method there is a condition: if r > l that need else command.
Also it's better change if arr[mid]>element to elif arr[mid]>element.
I suggest you see this code and compare it with your code:
def binary_search(arr, x):
low = 0
high = len(arr) - 1
mid = 0
while low <= high:
mid = (high + low) // 2
# If x is greater, ignore left half
if arr[mid] < x:
low = mid + 1
# If x is smaller, ignore right half
elif arr[mid] > x:
high = mid - 1
# means x is present at mid
else:
return mid
# If we reach here, then the element was not present
return -1
# Test array
arr = [ 2, 3, 4, 10, 40 ]
x = 10
# Function call
result = binary_search(arr, x)
if result != -1:
print("Element is present at index", str(result))
else:
print("Element is not present in array")

Finding Pivot element in a sorted array, How do I optimize this code?

Is there a way to optimize this code?
I'm trying to find an element in the list where the sorted list is rotated. [8,9,10,11,12,6,7] Here the element I'm trying to find is 6 with an index 5.
class Solution:
def search(self, nums: List[int]) -> int:
lenn = len(nums)-1
pivot, mid = 0, (0 + lenn)//2
if(nums[0] > nums[lenn]):
while True:
if(nums[mid] > nums[mid+1]):
pivot = mid+1
break
if(nums[mid] < nums[mid-1]):
pivot = mid
break
if(nums[mid] > nums[lenn]):
mid += 1
if(nums[mid] < nums[lenn]):
mid -= 1
return pivot
Your solution is fast for small lists but not for big ones as it perform a linear-time search. To be faster, you can use a binary search. To do that, you need to use a keep a [start;end] range of values that contains the "pivot". You just need to take the middle item and find which part (left or right) contains increasing items.
Moreover, you can micro-optimize the code. The default implementation of Python is CPython which is a slow interpreter. It optimize almost nothing compared to compilers (of languages like C, Java, Rust) so you should do the optimizations yourself if you want a fast code. The idea is to use a linear search for few items and a binary search for many ones, and to store values in temporary variables not to compute them twice.
Here is the resulting implementation:
def search(nums):
first, last = nums[0], nums[-1]
# Trivial case
if first <= last:
return 0
lenn = len(nums)-1
# Micro-optimized fast linear search for few items (optional)
if lenn < 10:
pivot, mid = 0, lenn//2
while True:
middle = nums[mid]
if middle > nums[mid+1]:
return mid+1
elif middle < nums[mid-1]:
return mid
elif middle > last:
mid += 1
elif middle < last:
mid -= 1
# Binary search for many items
start, end = 0, lenn
while start+1 < end:
mid = (start + end) // 2
midVal = nums[mid]
if midVal < nums[start]:
end = mid
elif midVal > nums[end]:
start = mid
else:
assert False
return start+1

Heapsort in Python does not sorting

I am new to python and I am trying to implement heapsort from my C++ implementation. This code is unable to sort input.
I have written another program to test function build_max_heap and this function is unable to build max heap.
def max_heapify(thelist, lst_size, idx):
largest = idx
left_child = (2 * idx) + 1
right_child = (2 * idx) + 2
if left_child < lst_size and thelist[left_child] > thelist[largest]:
largest = left_child
elif right_child < len(thelist) and thelist[right_child] > thelist[largest]:
largest = right_child
if largest != idx:
thelist[idx], thelist[largest] = thelist[largest], thelist[idx]
max_heapify(thelist, lst_size, largest)
def build_max_heap(thelist, lst_size):
for curr_idx in range(lst_size // 2 - 1, -1, -1):
max_heapify(thelist, lst_size, curr_idx)
def heap_sort(thelist):
if len(thelist) == 0:
print("Empty list!!")
elif len(thelist) == 1:
print("Only one element!!")
else:
build_max_heap(thelist, len(thelist))
for curr_idx in range(len(thelist) -1, 0, -1):
thelist[curr_idx], thelist[0] = thelist[0], thelist[curr_idx]
max_heapify(thelist, curr_idx, 0)
There are two errors in your heapify function:
The second branch should not be an elif, but an if, since you'll want to select the right child, even if the left child is greater than its parent, but when the right child is even greater.
You don't want to use len(thelist) there, as your function should rely on the argument lst_size. This is needed because in the heap_sort function calls are made that pass a value for this argument that is (and must be) less than the actual list length.
So change:
elif right_child < len(thelist) and thelist[right_child] > thelist[largest]:
to:
if right_child < lst_size and thelist[right_child] > thelist[largest]:

Get index of closest value with binary search

I want to do a binary search in python:
def binarySearch(data, val):
Where data is a sorted array and value is the value being searched for. If the value is found, I want to return the index (such that data[index] = val). If the value is not found, I want to return the index of the item that is closest to that value.
Here is what I've got:
def binarySearch(data, val):
high = len(data)-1
low = 0
while True:
index = (high + low) / 2
if data[index] == val:
return index
if data[index] < val:
low = index
if data[index] > val:
high = index
Here is the code that will return the index if the value is found, otherwise the index of the item that is closest to that value, hope it helps.
def binarySearch(data, val):
lo, hi = 0, len(data) - 1
best_ind = lo
while lo <= hi:
mid = lo + (hi - lo) // 2
if data[mid] < val:
lo = mid + 1
elif data[mid] > val:
hi = mid - 1
else:
best_ind = mid
break
# check if data[mid] is closer to val than data[best_ind]
if abs(data[mid] - val) < abs(data[best_ind] - val):
best_ind = mid
return best_ind
def main():
data = [1, 2, 3, 4, 5, 6, 7]
val = 6.1
ind = binarySearch(data, val)
print 'data[%d]=%d' % (ind, data[ind])
if __name__ == '__main__':
main()
Something like this should work. It returns an array with two indexes. If val is found, both values in the return array are the same. Otherwise, it returns the indexes of the two items closest to val.
def binarySearch(data, val):
highIndex = len(data)-1
lowIndex = 0
while highIndex > lowIndex:
index = (highIndex + lowIndex) / 2
sub = data[index]
if data[lowIndex] == val:
return [lowIndex, lowIndex]
elif sub == val:
return [index, index]
elif data[highIndex] == val:
return [highIndex, highIndex]
elif sub > val:
if highIndex == index:
return sorted([highIndex, lowIndex])
highIndex = index
else:
if lowIndex == index:
return sorted([highIndex, lowIndex])
lowIndex = index
return sorted([highIndex, lowIndex])
I know this is an old question, but it's high on Google's results and I had the same issue. There's a built-in to do this which uses binary search and allows you to feed in a reference array and a comparison array.
numpy.searchsorted(a, v, side='left', sorter=None)
a is the reference array (data in original question), v is the array to compare (val from the question). This returns an array of size v with int values for the index the nth element of v would need to be inserted into a to preserve the sort order in a' The side keyword determines whether you want the elements of v to be placed to the 'left' (before) or the 'right' (after) the appropriate value in a.
[documentation link as of July 2017]
https://docs.scipy.org/doc/numpy/reference/generated/numpy.searchsorted.html#numpy.searchsorted
Here's a sample implementation of binary search. I won't do all the (home?)work for you, I am sure you can figure out how to store and return the index of the closest value yourself.
# BINARY SEARCH: O(log n), search space halfed each step
def biSearch(lst, find): # expects sorted lst
lowIndex = 0
highIndex = len(lst) - 1
midIndex = (lowIndex + highIndex)//2
lastMid = None
steps = 0
while midIndex != lastMid:
steps += 1
if lst[midIndex] == find:
return (midIndex, steps)
if lst[midIndex] < find:
lowIndex = midIndex + 1
else:
highIndex = midIndex - 1
lastMid = midIndex
midIndex = (lowIndex + highIndex)//2
return (-1, steps)
Not the answer to this question. But I landed here trying to figure out how to get the two surrounding values for a given target item in a sorted list.
If anyone else is looking, this is what I came up with based on some of the other answers here.
import random
def get_nearest(items, target):
print(f'looking for {target}')
high_index = len(items) - 1
low_index = 0
if not items[low_index] <= target <= items[high_index]:
raise ValueError(f'The target {target} is not in the range of'
f' provided items {items[low_index]}:{items[high_index]}')
if target in items:
return target, target
while high_index > low_index:
index = int((high_index + low_index) / 2)
sub = items[index]
if sub > target:
if high_index == index:
return tuple(sorted([items[high_index], items[low_index]]))
high_index = index
else:
if low_index == index:
return tuple(sorted([items[high_index], items[low_index]]))
low_index = index
return tuple(sorted([items[high_index], items[low_index]]))
if __name__ == '__main__':
my_randoms = sorted(random.sample(range(10000000), 100000))
x = 340000
print(get_nearest(my_randoms, x))
x = 0
my_randoms = [x] + my_randoms
print(get_nearest(my_randoms, x))
x = 10000000
my_randoms.append(x)
print(get_nearest(my_randoms, x))
idx = random.randint(0, 100000)
x = my_randoms[idx]
print(get_nearest(my_randoms, x))

Categories