recursion, finding max, why is it not stopping? - python

I tried to find the maximum value in a sorted list. but the recursion is not stopping. please, can somebody help me?
A = [5,16,28,43,0,1]
start = 0
end = len(A) - 1
mid = 0
print mid
def search(start, end, mid):
mid = int((start + end) / 2)
print mid
if A[mid] > [mid - 1] and A[mid] > A[mid + 1]:
return A[mid]
else:
if A[mid - 1] > A[mid + 1]:
search(start, mid, mid)
else:
search(mid, end, mid)
print search(start, end, mid)

You need to add a "basis case" (where the recursion stops).
A natural basis case for this problem: if start is equal to end, just return A[start]
EDIT:
I just looked at this and the more I look the more confused I get. Why are you using recursion to find a max? It would make more sense to use recursion to do a "binary search" to find a value inside a sorted list.
If you want to really find a max value, that's pretty easy. With recursion, we first want a "basis case" that gives us a trivial solution; then we want more code that will take us one step closer to that solution.
In this case, the basis case: we have only one value in the list; return it as the max. To be specific, if the start and end together specify just one value, return that one value. To make it proof against errors, might as well make this also handle the case where start is equal to or even greater than end.
Next remember the first value.
Next make a recursive call, but add one to start to reduce the size of the list we are considering. This is the part that takes us one step closer to a solution. Repeat this step enough times and we arrive at the basis case where there is only one value in the list to consider.
Finally compare the remembered first value with the result of the recursive call and return the larger of the two.
I'll lay it out in psuedocode for you:
BASIS CASE: start and end specify one value: return A[start]
save A[0] in a variable
save recursive_call_to_this_function(start+1, end) in a variable
compare two saved values and return the larger
Once you have tried to write the above in code, peek below this line for my working tested solution.
def recursive_max(start, end):
if start >= end - 1:
return A[start]
x0 = A[start]
x1 = recursive_max(start+1, end)
if x0 >= x1:
return x0
else:
return x1
print recursive_max(start, end)

I agree with most of steveha's answer, but rather than picking off one element at a time I'd suggest dividing the list into halves and finding the max in each half. You won't do any fewer comparisons, but the growth of the recursive stack will be O(log(len(A))) rather than O(len(A)). For large lists this would be the difference between getting a stack overflow or not.
My implementation (which takes the list as an argument rather than expecting it to be global) follows:
def recursive_max(value_list, start, end):
if start >= end:
return value_list[start]
mid = start + (end - start) // 2
lower_half_max = recursive_max(value_list, start, mid)
upper_half_max = recursive_max(value_list, mid+1, end)
if lower_half_max > upper_half_max:
return lower_half_max
else:
return upper_half_max

Related

How to do binary search in python (Recursion) , but not including max and min values of list?

I have been ,wanting to do binary search (by recursion) , and I dont know why my code is not working...
Can anyone pls, correct my code and tell the reason why it is not working??
As you can look in my code, i am slicing the string per recursion, and at last when the target is found i will return the position of the target..
def binary(n,target):
n.sort()
mid = (0 + (len(n)-1))//2
if target == n[mid]:
return mid
elif target < n[mid]:
return binary(n[:mid],target)
elif target > n[mid]:
return binary(n[mid:],target)
This is the error message i am recieving...
RecursionError: maximum recursion depth exceeded while calling a Python object.
There are two problems, both of them are on the last line:
def binary(n, target):
n.sort()
mid = (0 + (len(n) - 1)) // 2
if target == n[mid]:
return mid
elif target < n[mid]:
return binary(n[:mid], target)
elif target > n[mid]:
return mid + 1 + binary(n[mid + 1:],target)
^^^^^^^^ ^
since you're slicing, and in order to provide the index in the original sorted list, we need to keep track of the indexes we "lose" while recursing
The complete to n[:mid] is n[mid+1:] - not n[mid:] because you already checked the target (in mid) and want to remove it from future iterations - this is, by the way, what causes the infinite loop!
Since we slice the list at mid+1 we need to add mid+1 before calling recursively, in order to preserve the index of the item on the right-side of the list:
[1,2,3,4,5]
^ say we slice here and get [4,5]
we want to save the indexes so we'll add mid (2) + 1 since now in [4,5] the item 4 will get the index zero
Comment: by calling n.sort() upon every iteration we "lose" all the advantage of binary search since, even after the list is sorted, to re-sort it will take at least O(n). So if we need to sort first, we might as well just iterate the array until the item is found/not-found. Or, if we insist on sorting, do it only once and only then call recursively:
n.sort()
binary(n, 2)
where binary does't include the sorting anymore:
def binary(n, target):
mid = (0 + (len(n) - 1)) // 2
if target == n[mid]:
return mid
elif target < n[mid]:
return binary(n[:mid], target)
elif target > n[mid]:
return mid + 1 + binary(n[mid + 1:], target)
Your midpoint calculation is too aggressive:
Consider n = [1, 2, 3] and target = 3. mid (len(n)-1//2) will then be 0, when surely it should be 1 or 2. The issue stems from you subtracting 1 from the length, and then dividing the result by 2.
Since you anyways do integer division, there is no need for subtracting one:
def binary(n,target):
n.sort()
mid = len(n)//2
...
There is also the issue of sorting the list (segment) every time binary is entered, which is unnecessary. You could write a wrapper function that sorts the list once and then calls the inner recursive function:
def binary(n, target):
return _binary(sorted(n), target)
Then just rename your original function to _binary.
Finally, when cutting from the left, you'll need to make sure to keep track of the lost number that you need to add to the index:
elif target > n[mid]:
return mid + binary(n[mid:],target)

Binary search: weird middle point calculation

Regarding calculation of the list mid-point: why is there
i = (first +last) //2
and last is initialized to len(a_list) - 1? From my quick tests, this algorithm without -1 works correctly.
def binary_search(a_list, item):
"""Performs iterative binary search to find the position of an integer in a given, sorted, list.
a_list -- sorted list of integers
item -- integer you are searching for the position of
"""
first = 0
last = len(a_list) - 1
while first <= last:
i = (first + last) / 2
if a_list[i] == item:
return '{item} found at position {i}'.format(item=item, i=i)
elif a_list[i] > item:
last = i - 1
elif a_list[i] < item:
first = i + 1
else:
return '{item} not found in the list'.format(item=item)
The last legal index is len(a_list) - 1. The algorithm will work correctly, as first will always be no more than this, so that the truncated mean will never go out of bounds. However, without the -1, the midpoint computation will be one larger than optimum about half the time, resulting in a slight loss of speed.
Consider the case where the item you're searching for is greater than all the elements of the list. In that case the statement first = i + 1 gets executed repeatedly. Finally you get to the last iteration of the loop, where first == last. In that case i is also equal to last, but if last=len() then i is off the end of the list! The first if statement will fail with an index out of range.
See for yourself: https://ideone.com/yvdTzo
You have another error in that code too, but I'll let you find it for yourself.

Find index of list using binary recursive function

So, ive been instructed to create a function with 2 parameters, a list and a number, that uses a binary recursive search to see if a number is in a list. If the number is in the list i'm to return its index and if its not I am to return -1. So far i have
def findIndex(alist,num):
print(alist)
if len(alist) % 2 == 0:
mid = int((len(alist)/2)-1)
else:
mid = ((len(alist)//2))
if alist[mid] == num:
print(mid)
elif alist[mid] > num:
findIndex(alist[0:mid],num)
elif alist[mid] < num:
findIndex(alist[mid+1:],num)
I know how a binary search works. Do to the middle, if its not the number you're searching for compare that number to the number you're searching for. If its greater than the number youre searching for, search the front half of the list. If its lesser, search the back half of the list again. The problem is my code only works in the case that the number I'm searching for is less than the middle number in every case.
ANALYSIS
There are several problems with the logic.
The deleted post nailed your most glaring problem: your search works only when the search target appears in the middle of a series of left-only divisions. Otherwise, you print 0, the index when the list gets down to a single item.
If the target is not in the list, your program crashes on index out of range, when you try to find the midpoint of an empty list.
You never return anything. Printing a result is not the same as returning a value.
SOLUTION
There are two straightforward ways to handle this. The first is to use findIndex as a wrapper function, and write the function you want to be called by that. For instance:
def findIndex(alist,num):
return binaryFind(alist, 0, len(alist), num)
def binaryFind(alist, left, right, target):
# Here, you write a typical binary search function
# with left & right limits.
The second is to return the index you find, but adjust it for all of the times you cut off the left half of the list. Each level of call has to add that adjustment to the return value, passing the sum back to the previous level. The simple case looks like this, where you're recurring on the right half of the list:
elif alist[mid] < num:
return (mid+1) + findIndex(alist[mid+1:], num)
Does that get you moving toward a useful solution?

Quicksort in place Python

I am extremely new to Python and i am trying my hand in Algorithms. I was just trying my hands in the in place sorting of an array using quicksort algo.
Below is my code . When i run this there is an infinite loop as output. Can anyone kindly go through the code and let me know where i am going wrong logically :
def quicksort(arr,start,end):
if start < end:
pivot = arr[int(start + (end - start)/2)]
while end > start:
while arr[start] < pivot:
start = start + 1
while arr[end] > pivot:
end = end - 1
if start <= end:
arr[start],arr[end] = arr[end],arr[start]
start = start + 1
end = end - 1
quicksort(arr,0,end)
quicksort(arr,start,len(arr) - 1)
else:
return
arr = list(map(int,input().split(" ")))
quicksort(arr,0,len(arr) - 1)
print ("The final sorted array:",arr)
Thanks for any help in advance.
Your recursion is wrong. After doing Hoare Partition, you should do :
call quicksort from start (of data that being sorted) to index that run from the end, and
call quicksort from end (of data that being sorted) to index that run from the start
So you have to create new variables beside the argument to maintain start,end and indices that move toward each other in the partition.
In order to not change a lot of your code, i suggest you to change the argument
def quicksort(arr,left,right):
start = left
end = right
and do this in the recursion
quicksort(arr,left,end)
quicksort(arr,start,right)

Is my code's worse time complexity is log(n)?

The method foo gets as a parameter a sorted list with different numbers and returns the count of all the occurrences such that: i == list[i] (where i is the index 0 <= i <= len(list)).
def foo_helper(lst, start, end):
if start > end:
# end of recursion
return 0
if lst[end] < end or lst[start] > start:
# no point checking this part of the list
return 0
# all indexes must be equal to their values
if abs(end - start) == lst[end] - lst[start]:
return end - start + 1
middle = (end + start) // 2
print(lst[start:end+1], start, middle, end)
if lst[middle] == middle:
#print("lst[" , middle , "]=", lst[middle])
return 1 + foo_helper(lst, middle+1, end) + foo_helper(lst, start, middle-1)
elif lst[middle] < middle:
return foo_helper(lst, middle+1, end)
else:
return foo_helper(lst, start, middle-1)
def foo(lst):
return foo_helper(lst, 0, len(lst)-1)
My question is if this code's worst-case complexity = log(n)?
If not, What should I do different?
If you have a list of N numbers, all unique, and known to be sorted, then if list[0] == 0 and list[N-1] == N-1, then the uniqueness and ordering properties dictate that the entire list meets the property that list[i] == i. This can be determined in O(1) time - just check the first and last list entries.
The uniqueness and ordering properties force any list to have three separate regions - a possibly empty prefix region where list[i] < i, a possibly empty middle region where list[i] == i, and a possibly empty suffix region where list[i] > i]. In the general case, finding the middle region requires O(n) time - a scan from the front to find the first index where list[i] == i, and a scan from the back to find the last such index (or you could do both with one single forward scan). Once you find those, you are guaranteed by uniqueness and ordering that all the indexes in between will have the same property...
Edit: As pointed out by #tobias_k below, you could also do a binary search to find the two end points, which would be O(log n) instead of O(n). This would be the better option if your inputs are completely general.
To expand on my comment trying to think about this problem. Consider of the graph of the identity function, which represents the indices. We want to know where this sorted list (a strictly monotonic function) intersects the line representing the indices y = x, considering only integer locations. I think you should be able to find this in O(n) time (as commented it seems binary search for the intersection bounds should work), though I need to look at your code more closely to see what it's doing.
Because we have a sorted list with unique elements, we have i == list[i] either at no place
at one place
or if there are multiple places they must be consecutive (once you're above the line you can never come back down)
Code used:
import numpy as np
import matplotlib.pyplot as plt
a = np.unique(np.random.randint(-25, 50, 50))
indices = range(len(a))
plt.scatter(indices, indices, c='b')
plt.scatter(indices, a, c='r')
plt.show()

Categories