My bisection functions have a small error - python

My recursive and iterative bisection functions wont work with long lists of the same number.
def bisection_it(mylist, goal):
start, end = 0, (len(mylist) - 1)
while start <= end:
mid = (start + end) // 2
if goal == mylist[mid]:
return mid
if goal < mylist[mid]:
end = mid - 1
else:
start = mid + 1
return -1
def bisection_rec(mylist, goal, start = 0, end = None):
if end is None:
end = len(mylist) - 1
if start > end:
return -1
mid = (start + end) // 2
if goal == mylist[mid]:
return mid
if goal < mylist[mid]:
return bisection_rec(mylist, goal, start = 0, end = mid-1)
return bisection_rec(mylist, goal, mid+1, end)
with the list [2, 2, 2, 2, 2, 2, 2, 2, 2,] I expect the output to be 0, but the actual output is 4

Related

Using binary search to find the last position of an element

I'm trying to write a function that returns the last position of an element in a sorted list, however I'm not quite sure of how binary search works. Can someone help me fix the problem in my code?
def last_entry(L, target):
low = 0
high = len(L) - 1
while low <= high:
mid = (low + high) // 2
if L[mid] < target:
low = mid + 1
elif L[mid] > target:
high = mid - 1
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
return -1
The problem lies here:
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
It's incomplete. If you use the input list [0, 0, 1, 3, 3, 3, 4, 8] and target 3, it will loop infinitely (I haven't tested it though). So you need to keep searching in the upper half for the target:
else:
if mid == len(L) - 1:
return mid
if target < L[mid + 1]:
return mid
low = mid + 1
Edit: I tested my solution and I think it works
You can separate the problems. First we do the binary search, then we verify if it's the latest occurrence in list.
lst = [1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 7, 7, 8, 8, 9]
def search(lst, target):
min = 0
max = len(lst)-1
avg = (min+max)//2
while (min < max):
if (lst[avg] == target):
return avg
elif (lst[avg] < target):
return avg + 1 + search(lst[avg+1:], target)
else:
return search(lst[:avg], target)
return avg
def find_latest(lst, index):
current = lst[index]
index += 1
while lst[index] == current:
current = lst[index]
index += 1
return index - 1
find_latest(lst ,search(lst, 4))

Python Quicksort implementation

I tried to implement the recursive quicksort in Python, but it doesn't work. I know that there is the problem that the array doesn't get sorted because the pivot is always higher than i, which results in the problem that i is always equals to m.
def partition(array):
pivot = array[-1]
m = 0
for i in range(len(array) - 1):
if array[i] < pivot:
array[i], array[m] = array[m], array[i]
m += 1
else:
continue
array[m], array[len(array)-1] = array[len(array)-1], array[m]
return m
def quicksort(array):
if len(array) > 1:
m = partition(array)
quicksort(array[:m])
quicksort(array[m+1:])
return array
def main():
testarray = [3,6,2,4,5,1,9,8,7,10,14]
print(quicksort(testarray))
if __name__ == '__main__':
main()
Two things. Firstly, you forgot to return array when it's of length 1, and secondly you aren't actually modifying array before returning. This will work.
def quicksort(array):
if len(array) > 1:
m = partition(array)
# return the concatenation of the two sorted arrays
return quicksort(array[:m]) + quicksort(array[m:])
else:
return array
For those looking for an iterative/non-recursive version of Quicksort, here's an implementation I came up with in Python:
from random import randint
def default_comparator_fn(a, b):
return -1 if a < b else (1 if a > b else 0)
def reverse_comparator_fn(a, b):
return default_comparator_fn(b, a)
def quick_sort(A, comparator_fn=default_comparator_fn):
n = len(A)
if n < 2:
# The list has only 1 element or does not have any.
return A
# There are at least 2 elements.
partitions = [[0, n - 1]] # [[start, end]]
while len(partitions):
partition = partitions.pop()
start = partition[0]
end = partition[1]
pivot_index = randint(start, end)
pivot = A[pivot_index]
A[pivot_index], A[start] = A[start], A[pivot_index]
breakpoint_index = start
k = start + 1
m = end
while k <= m:
res = comparator_fn(A[k], pivot)
if res < 0:
breakpoint_index = k
else:
while m > k:
res = comparator_fn(A[m], pivot)
if res < 0:
breakpoint_index = k
A[m], A[k] = A[k], A[m]
m -= 1
break
m -= 1
k += 1
A[start], A[breakpoint_index] = A[breakpoint_index], A[start]
if start < breakpoint_index - 1:
partitions.append([start, breakpoint_index - 1])
if breakpoint_index + 1 < end:
partitions.append([breakpoint_index + 1, end])
return A
# Example:
A = [4, 2, 5, 1, 3]
quick_sort(A) # Sort in ascending order ([1, 2, 3, 4, 5]).
quick_sort(A, reverse_comparator_fn) # Sort in descending order ([5, 4, 3, 2, 1]).
This implementation of Quicksort accepts an optional custom comparator function which defaults to a comparator which compares the elements of the list in ascending order.

Circular range in Python

How can I implement a circular range object in Python?
e.g.
Let S is a circular space modulo 2^3 (range [0, 2^3)).
I want to generate a range object like this:
crange(3, 7, 2 ** 3) # => a range object [3, 4, 5, 6]
crange(7, 3, 2 ** 3) # => a range object [7, 0, 1, 2]
I tried this:
def crange(start, stop, modulo):
if start > stop:
return range(start, modulo) or range(stop)
else:
return range(start, stop)
But I can't input bigint to crange e.g. crange(8, 2, 2 ** 160).
OverflowError: Python int too large to convert to C ssize_t
Try this:
def crange(start, stop, modulo):
result = []
index = start
while index != stop:
result.append(index)
index = (index + 1) % modulo
return result
If you know that your list can be too long, you can use a generator instead that generates the necessage sequence:
def crange(start, stop, modulo):
index = start
while index != stop:
yield index
index = (index + 1) % modulo
I implemented crange which I want (in reference to #Ni and #J.F.Sebastian).
import math
class crange:
def __init__(self, start, stop, step=None, modulo=None):
if step == 0:
raise ValueError('crange() arg 3 must not be zero')
if step is None and modulo is None:
self.start = 0
self.stop = start
self.step = 1
self.modulo = stop
else:
self.start = start
self.stop = stop
if modulo is None:
self.step = 1
self.modulo = step
else:
self.step = step
self.modulo = modulo
def __iter__(self):
n = self.start
if n > self.stop:
while n < self.modulo:
yield n
n += 1
n = 0
while n < self.stop:
yield n
n += 1
def __contains__(self, n):
if self.start >= self.stop:
return self.start <= n < self.modulo or 0 <= n < self.stop
else:
return self.start <= n < self.stop
I got the following output:
>>> print(list(crange(start=7, stop=3, modulo=2 ** 4)))
[7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2]
>>> print(3 in crange(start=7, stop=3, modulo=2 ** 4))
False
>>> print(7 in crange(start=7, stop=3, modulo=2 ** 4))
True
>>> print(list(crange(start=3, stop=7, modulo=2 ** 4)))
[3, 4, 5, 6]
>>> print(3 in crange(start=3, stop=7, modulo=2 ** 4))
True
>>> print(7 in crange(start=3, stop=7, modulo=2 ** 4))
False
You can avoid the use of range and the storage of a huge list in memory by creating your own generator:
def crange(start, end, modulo):
if start > end:
while start < modulo:
yield start
start += 1
start = 0
while start < end:
yield start
start += 1
print list(crange(3, 7, 2 ** 3))
print list(crange(7, 3, 2 ** 3))
print next(crange(8, 2, 2 ** 160))
This code outputs:
[3, 4, 5, 6]
[7, 0, 1, 2]
8
An equivalent solution to that of #Fomalhaut but simpler:
def crange(start, end, modulo):
for i in range(start,end):
yield i % modulo
Added no reset to start and subscriptability. original
copied from answer by #sira
class Crange:
def __init__(self, start, stop, step=None, modulo=None, no_reset=True):
if step == 0:
raise ValueError('crange() arg 3 must not be zero')
self.no_reset = no_reset
if step is None and modulo is None:
self.start = 0
self.stop = start
self.step = 1
self.modulo = stop
else:
self.start = start
self.stop = stop
if modulo is None:
self.step = 1
self.modulo = step
else:
self.step = step
self.modulo = modulo
def __iter__(self):
n = self.start
if n > self.stop:
while n < self.modulo:
yield n
n += self.step
if n > self.modulo:
n = n-self.modulo if self.no_reset else 0
else: n=0
while n < self.stop:
yield n
n += self.step
def __getitem__(self, __name):
return [*self][__name]
def __contains__(self, n):
if self.start >= self.stop:
return self.start <= n < self.modulo or 0 <= n < self.stop
else:
return self.start <= n < self.stop
I made my own version that can slice forwards and backwards. What it can't do in this state is use negative integers and different step sizes than one though, so that would need to be implemented to cover the whole gamut of slicing options in python. I bet that's an easy addition though.
l = list(range(10))
def crange(l,a,b):
assert a >= 0
assert b >= 0
d = b-a
if d == 0:
return
elif d > 0:
increment = 1
elif d < 0:
increment = -1
start = a%len(l)
counter = 0
while counter <= abs(d):
index = abs(start + counter*increment)%len(l)
yield l[index]
counter += 1
print(list(l))
print(list(crange(l,0,6)))
print(list(crange(l,6,10)))
print(list(crange(l,10,25)))
print(list(crange(l,9,0)))
print(list(crange(l,10,5)))
Python 3.x's range objects provides more functionality than just iteration and membership testing: subscripting (indexing), reverse iteration, length testing (which implies boolean coercion), the .count and .index methods (all of these collectively being the Sequence protocol), plus __str__ and __repr__.
To emulate these, we can wrap a range object, delegating to its methods with modified arguments. We can also leverage collections.abc in order to avoid the need to delegate everything, relying on mixins from the Sequence ABC instead.
There's also an issue here in that the input specification is tricky to interpret in corner cases, and inflexible (for example, we can't represent an iterator over [3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6]). The simple way around this is to redefine: a crange(start, stop, step, modulo) iterates over the values start % modulo, (start + step) % modulo ... (start + n * step) % modulo, such that (start + n * step) is limited to stop. (That is: the same values as would be in the range, but applying the specified modulo value to each of them.)
This is fairly straightforward to implement, as we need only __getitem__ and __len__:
from collections.abc import Sequence
class crange(Sequence):
def __init__(self, start, stop, *, step=1, modulo=None):
self._modulo, self._impl = modulo, range(start, stop, step)
def __getitem__(self, index):
result = self._impl[index]
if self._modulo is not None:
result %= self._modulo
return result
def __len__(self):
return len(self._impl)
def __repr__(self):
return f'c{self._impl}'
Now we can do fun things like:
>>> list(reversed(crange(7, 19, step=3, modulo=8)))
[0, 5, 2, 7]

Binary Searching in Python

So I have this problem.
You are given a landscape in the form of a non-empty one-dimensional
array seq. The goal is to find an index i of a cell that is a pit. We
say seq[i] is a pit if seq[i] <= seq[i-1] and seq[i] <= seq[i+1]. For
example in the array [7, 6, 9, 7, 8], the indices 1 and 3 are pits.
The first or last elements are considered to be a pit if they are less
than or equal to their only neighbour. For example the last element of
[3, 2, 4, 4, 1] is a pit (and also index 1). Note that the definition
of a pit also includes equality; for example in [3, 2, 2, 2, 5, 6, 6,
8], the indices 1, 2, 3, and 6 are pits. As a special case, we also
define the only cell of an array of length one to be a pit as well.
I've formulated a solution using a binary search (kind of) to achieve O(logn) as the worst case time. But I've encountered an example which returns nothing or NONE.
def find_pit(seq):
first = 0
last = len(seq) - 1
origlast = last
mid = 0
if len(seq) == 1 :
return 0
else:
while first <= last & mid < last :
mid = (first + last) // 2
if seq[mid] <= seq[mid - 1] & seq[mid] <= seq[mid + 1]:
return mid
else:
if seq[mid] > seq[mid - 1]:
last = mid
else:
first = mid
if seq[0] <= seq[1]:
return 0
elif seq[origlast] <= seq[origlast-1]:
return (len(seq) - 1)
print(find_pit([0,1]))
print(find_pit([5, 4, 3, 6, 7]))
How do I fix this?
You need to change the
& (bitwise "and")
to
and (logical "and")
in your code:
def find_pit(seq):
first = 0
last = len(seq) - 1
origlast = last
mid = 0
if len(seq) == 1 :
return 0
else:
#change next line to use logical and
while first <= last and mid < last :
mid = (first + last) // 2
#change next line to use logical and
if seq[mid] <= seq[mid - 1] and seq[mid] <= seq[mid + 1]:
return mid
else:
if seq[mid] > seq[mid - 1]:
last = mid
else:
first = mid
if seq[0] <= seq[1]:
return 0
elif seq[origlast] <= seq[origlast-1]:
return (len(seq) - 1)
print(find_pit([0,1]))
print(find_pit([5, 4, 3, 6, 7]))
Running this with the above test cases will now give the result:
0 for the first list and 2 for the second.
seems to work at finding first pit in the given cases. I've tweaked the call to allow multiple functions to be checked.
#.... original find_pit left, but not pasted in
import sys
def find_pit2(seq):
left = sys.maxint
maxp = len(seq)
if maxp == 1 :
return 0
else:
for pos, current in enumerate(seq):
try:
right = seq[pos+1]
except IndexError:
#rightmost, count as right neighbor as bigger
right = sys.maxint
#pit - smaller or equal to neighbours
if left >= current and current <= right:
return pos
left = current
li_f = [find_pit, find_pit2]
for f in li_f:
print f.__name__
print(" ",f([0,1]))
print(" ",f([5, 4, 3, 6, 7]))
print(" ",f([7, 6, 9, 7, 8]))
print(" ",f([3, 2, 2, 2, 5, 6, 6, 8]))
giving
find_pit
(' ', 0)
(' ', 2)
(' ', None)
(' ', 3)
find_pit2
(' ', 0)
(' ', 2)
(' ', 1)
(' ', 1)

Recursion binary search in Python [duplicate]

This question already has answers here:
What is the difference between '/' and '//' when used for division?
(16 answers)
Closed 7 months ago.
I have a list with numbers from 0-9:
mylist = list(range(10))
I am getting an error with the division command to get mid:
def binary_search(mylist, element, low, high):
low=0
high= len(mylist)
mid=low + (high- mymin)/2
if mid==len(mylist):
return False
elif mylist[mid]==element:
return mid
elif high==low:
return False
elif mylist[mid]<element:
return binary_search(mylist, element, mymin, mid-1)
elif mylist[mid]<element:
return binary_search(mylist, element, mid+1, mymax)
else:
return mid
and if I wanted to return True how would I write that on top of return binary_search(mylist, element, mymin, mid-1)?
Recursive solution:
def binary_search_recursive(arr, elem, start=0, end=None):
if end is None:
end = len(arr) - 1
if start > end:
return False
mid = (start + end) // 2
if elem == arr[mid]:
return mid
if elem < arr[mid]:
return binary_search_recursive(arr, elem, start, mid-1)
# elem > arr[mid]
return binary_search_recursive(arr, elem, mid+1, end)
Iterative solution:
def binary_search_iterative(arr, elem):
start, end = 0, (len(arr) - 1)
while start <= end:
mid = (start + end) // 2
if elem == arr[mid]:
return mid
if elem < arr[mid]:
end = mid - 1
else: # elem > arr[mid]
start = mid + 1
return False
Here's an elegant recursive solution if you're:
1) Trying to return the INDEX of the target in the original list being passed in, before it is halved. Getting the target is the easy part.
2) Only want to have to pass in 2 arguments: (list, target) instead of 4 arguments including the upper/lower (right/left) bounds of each array being passed in recursively.
3) Don't want out of bounds, maximum recursion depth, or target not found errors.
# Base Case: one item (target) in array.
# Recursive Case: cut array by half each recursive call.
def recursive_binary_search(arr, target):
mid = len(arr) // 2
if len(arr) == 1:
return mid if arr[mid] == target else None
elif arr[mid] == target:
return mid
else:
if arr[mid] < target:
callback_response = recursive_binary_search((arr[mid:]), target)
return mid + callback_response if callback_response is not None else None
else:
return recursive_binary_search((arr[:mid]), target)
please correct me, if the code presents any bugs
def binary_recursive(array, val):
if val < array[0] or val > array[-1]:
return False
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
if val == array[mid]:
return True
elif array[mid] > val:
return binary_recursive(left, val)
elif array[mid] < val:
return binary_recursive(right, val)
else:
return False
`Please correct me if I am wrong as I am a new programmer but here is my solution:
def binary_search(test_cases, item_to_be_found):
try:
if item_to_be_found == test_cases[len(test_cases) // 2]:
return True
elif item_to_be_found < test_cases[len(test_cases) // 2]:
test_cases = test_cases[:len(test_cases) // 2]
else:
test_cases = test_cases[len(test_cases) // 2:]
return binary_search(test_cases, item_to_be_found)
except RecursionError:
return False
The first solution looks wrong because it doesn't index the list.
This problem tripped me up too the first time I wrote a solution so be sure to test your algorithm well.
Here's what I ended up with:
def binary_search(value, items, low=0, high=None):
"""
Binary search function.
Assumes 'items' is a sorted list.
The search range is [low, high)
"""
high = len(items) if high is None else high
pos = low + (high - low) / len(items)
if pos == len(items):
return False
elif items[pos] == value:
return pos
elif high == low:
return False
elif items[pos] < value:
return binary_search(value, items, pos + 1, high)
else:
assert items[pos] > value
return binary_search(value, items, low, pos)
And when I test it, the answers look correct:
In [9]: for val in range(7):
...: print val, binary_search(val, [1, 2, 3, 5])
...:
0 False
1 0
2 1
3 2
4 False
5 3
6 False
Btw, Python has a library module for just this kind of thing named bisect.
Though it's too late, it might help someone else :-
def bsearch_helper(arr, key, low, high):
if low > high:
return False
mid = (low + high)//2
if arr[mid] == key:
return True
elif arr[mid] < key:
return bsearch_helper(arr, key, mid + 1, high)
else:
return bsearch_helper(arr, key, low, mid - 1)
return False
def bsearch(arr, key):
return bsearch_helper(arr, key, 0, len(arr) - 1)
if __name__ == '__main__':
arr = [8, 3, 9, 2, 6, 5, 1, 7, 4]
print (bsearch(sorted(arr), 5))
You can make use of list slicing too.
def binary_search_recursive(list1, element):
if len(list1) == 0:
return False
else:
mid = len(list1)//2
if (element == list1[mid]):
return True
else:
if element > list1[mid]:
return binary_search_recursive(list1[mid+1:],element)
else:
return binary_search_recursive(list1[:mid],element)
However, note that list slicing introduces additional complexity.
There are a lot of solutions here already. Below is one more solution without slicing and that just requires element and list as arguments:
def binary_search(item, arr):
def _binary_search(item, first, last, arr):
if last < first:
return False
if last == first:
return arr[last] == item
mid = (first + last) // 2
if arr[mid] > item:
last = mid
return _binary_search(item, first, last, arr)
elif arr[mid] < item:
first = mid + 1
return _binary_search(item, first, last, arr)
else:
return arr[mid] == item
return _binary_search(item, 0, len(arr) - 1, arr)
print(binary_search(-1, [0, 1, 2, 3, 4, 5]))
Recursion Binary Search for sorted list.
The print statements are helpful to see how it all works.
# n = number we are searching for
# lst = the sorted list we are searching in
# sp = list start position
# ep = list end postion
def binary_search_recursion(n: int, lst: list, sp: int = 0, ep: int = None) -> bool:
# first time searching, start position will be 0
# and end position will be None
if ep is None:
# End position equals total length minus 1 since 0 indexed
ep = len(lst) - 1
# get the midpoint of the list (lst)
mid = (sp + ep) // 2
mid_item = lst[mid]
print(f"Start: lst[{sp}] = {lst[sp]}\nMid: lst[{mid}] = {mid_item}\nEnd: lst[{ep}] = {lst[ep]}")
# If mid item matches the searching number then success
if mid_item == n:
print(f"Success!!! Number {n} found in lst[{mid}]")
return True
# Else if mid item is greater than number, we eliminate everything to the left and move right
elif mid_item > n:
new_ep = mid - 1
if new_ep < 0:
print(f"Number {n} is too low. Lowest number found is {lst[sp]}")
return False
else:
print(f"Number {n} is less than mid item {mid_item}, change ep {ep} to {new_ep}.\n")
binary_search_recursion(n, lst, sp, new_ep)
# Else if mid item is lower than number, we eliminate everything to the right and move left
elif mid_item < n:
new_sp = mid + 1
if new_sp > ep:
print(f"Number {n} is too High. Highest number found is {lst[ep]}")
return False
else:
print(f"Number {n} is greater than mid item {mid_item}, change sp {sp} to {new_sp}.\n")
binary_search_recursion(n, lst, new_sp, ep)
Testing out the function:
# A sorted list
num_list = [10,20,30,40,50,60,70,80,90,100,110]
# Search for 10 in num_list
binary_search_recursion(10, num_list)
Start: lst[0] = 10
Mid: lst[5] = 60
End: lst[10] = 110
Number 10 is less than mid item 60, change ep 10 to 4.
Start: lst[0] = 10
Mid: lst[2] = 30
End: lst[4] = 50
Number 10 is less than mid item 30, change ep 4 to 1.
Start: lst[0] = 10
Mid: lst[0] = 10
End: lst[1] = 20
Success!!! Number 10 found in lst[0]
# Search for 110 in num_list
binary_search_recursion(110, num_list)
Start: lst[0] = 10
Mid: lst[5] = 60
End: lst[10] = 110
Number 110 is greater than mid item 60, change sp 0 to 6.
Start: lst[6] = 70
Mid: lst[8] = 90
End: lst[10] = 110
Number 110 is greater than mid item 90, change sp 6 to 9.
Start: lst[9] = 100
Mid: lst[9] = 100
End: lst[10] = 110
Number 110 is greater than mid item 100, change sp 9 to 10.
Start: lst[10] = 110
Mid: lst[10] = 110
End: lst[10] = 110
Success!!! Number 110 found in lst[10]
# Search for 6 in num_list
binary_search_recursion(6, num_list)
Start: lst[0] = 10
Mid: lst[5] = 60
End: lst[10] = 110
Number 6 is less than mid item 60, change ep 10 to 4.
Start: lst[0] = 10
Mid: lst[2] = 30
End: lst[4] = 50
Number 6 is less than mid item 30, change ep 4 to 1.
Start: lst[0] = 10
Mid: lst[0] = 10
End: lst[1] = 20
Number 6 is too low. Lowest number found is 10
# Search for 1111 in num_list
binary_search_recursion(1111, num_list)
Start: lst[0] = 10
Mid: lst[5] = 60
End: lst[10] = 110
Number 1111 is greater than mid item 60, change sp 0 to 6.
Start: lst[6] = 70
Mid: lst[8] = 90
End: lst[10] = 110
Number 1111 is greater than mid item 90, change sp 6 to 9.
Start: lst[9] = 100
Mid: lst[9] = 100
End: lst[10] = 110
Number 1111 is greater than mid item 100, change sp 9 to 10.
Start: lst[10] = 110
Mid: lst[10] = 110
End: lst[10] = 110
Number 1111 is too High. Highest number found is 110
Your first one won't even get started, because list(mid) will immediately raise a TypeError: 'list' object is not callable.
If you fix that (by using list[mid]), your next problem is that you ignore the min and max arguments you receive, and instead set them to 0 and len(list)-1 each time. So, you will infinitely recurse (until you eventually get a RecursionError for hitting the stack limit).
Think about it: the first call to binarySearch(l, 5, 0, 10) recursively calls binarySearch(l, 5, 0, 4). But that call ignores that 4 and acts as if you'd passed 10, so it recursively calls binarySearch(l, 5, 0, 4). Which calls binarySearch(l, 5, 0, 4). And so on.
If you fix that (by removing those two lines), you've got another problem. When you give it the number 10, binarySearch(l, 10, 0, 10) will call binarySearch(l, 10, 6, 10), which will callbinarySearch(l, 10, 8, 10), then binarySearch(l, 10, 9, 10), thenbinarySearch(l, 10, 10, 10). And that last one will check list[10] > element. But list[10] is going to raise an IndexError, because there aren't 11 elements in the list.
And once you fix that off-by-one error, there are no problems left. The problem you asked about cannot possibly ever occur. Here's an example run:
>>> a = range(10)
>>> for i in -3, -1, 0, 1, 4, 5, 9, 10, 11:
... print i, binarySearch(a, i, 0, 10)
-3 False
-1 False
0 0
1 1
4 4
5 5
9 9
10 False
11 False
Your second version isn't recursive. bSearch never calls bSearch, and that's the whole definition of recursion.
There's nothing wrong with writing an iterative algorithm instead of a recursive algorithm (unless you're doing a homework problem and recursion is the whole point), but your function isn't iterative either—there are no loops anywhere.
(This version also ignores the start and end arguments, but that's not as much of a problem in this case, because, again, you're not doing any recursion.)
Anyway, the only return False in the function is in that first if len(list) == 0. So, for any non-empty list, it's either going to return True, or a number. With your input, it will return 4 for anything less than the value at the midpoint of the list (5), and True for anything else.
Your problem here is that you're redeclaring min and max in each loop, so although it should be recursive, passing in a new min or max each time, this isn't in fact happening.
You can solve this by using defaults in the arguments:
def binary_search(list, element, min=0, max=None):
max = max or len(list)-1
if max<min:
return False
else:
mid= min + (max-min)/2
if mid>element:
return binary_search(list, element, min, mid-1)
elif mid<element:
return binary_search(list, element, mid+1, max)
else:
return mid
If you're not familiar with the syntax on line 2,
max = max or len(list)-1
max will be set to len(list)-1 only if max is not passed in to the method.
So you can call the method simply using:
binary_search(range(10), 7)
# Returns 7
binary_search(range(10), 11)
# Returns False
Just another answer to the same question:
def binary_search(array, element, mini=0, maxi=None):
"""recursive binary search."""
maxi = len(array) - 1 if maxi is None else maxi
if mini == maxi:
return array[mini] == element
else:
median = (maxi + mini)/2
if array[median] == element:
return True
elif array[median] > element:
return binary_search(array, element, mini, median)
else:
return binary_search(array, element, median+1, maxi)
print binary_search([1,2,3],2)
I made this one. Correct me if there's any bug.
import math
def insert_rec(A,v,fi,li):
mi = int(math.floor((li+fi)/2))
if A[mi] == v:
print("Yes found at: ",mi)
return
if fi==li or fi>li:
print("Not found")
return
if A[mi] < v:
fi = mi+1
insert_rec(A,v,fi,li)
if A[mi] > v:
li = mi-1
insert_rec(A,v,fi,li)
def bs(list,num): #presume that the list is a sorted list
#base case
mid=int(len(list)/2) # to divide the list into two parts
if num==list[mid]:
return True
if len(list)==1:
return False
#recursion
elif num<list[mid]: #if the num is less than mid
return bs(list[0:mid],num) #then omit all the nums after the mid
elif num>list[mid]: #if the num is greater than mid
return bs(list[mid:],num) # then omit all the nums before the mid
#return False
list=[1,2,3,4,5,6,7,8,9,10]
print(bs(list,20))
<<< False
print(bs(list,4))
<<< True
You can implement binary search in python in the following way.
def binary_search_recursive(list_of_numbers, number, start=0, end=None):
# The end of our search is initialized to None. First we set the end to the length of the sequence.
if end is None:
end = len(list_of_numbers) - 1
if start > end:
# This will happen if the list is empty of the number is not found in the list.
raise ValueError('Number not in list')
mid = (start + end) // 2 # This is the mid value of our binary search.
if number == list_of_numbers[mid]:
# We have found the number in our list. Let's return the index.
return mid
if number < list_of_numbers[mid]:
# Number lies in the lower half. So we call the function again changing the end value to 'mid - 1' Here we are entering the recursive mode.
return binary_search_recursive(list_of_numbers, number, start, mid - 1)
# number > list_of_numbers[mid]
# Number lies in the upper half. So we call the function again changing the start value to 'mid + 1' Here we are entering the recursive mode.
return binary_search_recursive(list_of_numbers, number, mid + 1, end)
We should check our code with good unittest to find loop holes in our code.
Hope this helps you.
Here is My Recursion Solution of Binary Search
def recBinarySearch(arr,ele):
if len(arr) == 0:
return False
else:
mid = len(arr)/2
if arr[mid] == ele:
return True
else:
if ele < arr[mid]:
return recBinarySearch(arr[:mid], ele)
else:
return recBinarySearch(arr[mid+1], ele)
You can do it this way as well:
def recursive_binary_search(list, item):
"""find the index of the given item in the list recursively"""
low = 0
high = len(list) - 1
if low <= high:
mid = low + high
guess = list[mid]
if guess == item:
return mid
if guess > item: # item must be in the first split
return recursive_binary_search(list[low:mid], item)
else: # check in second split otherwise
return recursive_binary_search(list[mid:high], item)
return None
def main():
print(recursive_binary_search([2,3,4,5,6,7,8], 3)) # will print 1
if __name__ == "__main__":
main()

Categories

Resources