I am trying to solve a binary search problem, however, I keep getting an infinite loop, since the values of lo hi and mid do not change as they suppose to. I am trying to search for a minimum mid value where function count_lines(mid,k) >= n. I have spent hours debugging this code but I still couldn't figure out what is wrong with the values of lo hi and mid. Could someone please look at my code and show me why this happen? Many thanks!
Failed testing case where: n=9 k=2
Here is my code:
def count_lines(v,k):
...
...
return count_lines #count_lines is an integer`
def binarySearch_v(n, k):
lo = 0
hi = n
mid = (lo + hi)//2
while (lo <= hi):
if (count_lines(mid, k) < n):
lo = mid
elif (count_lines(mid, k) > n):
hi = mid
elif (count_lines(mid, k) >= n and count_lines(mid-1, k) < n):
return mid
mid = (lo + hi) // 2
def main():
print(binarySearch_v(n,k)) # Failed at n=9 & k=2
main()
Here's your termination condition:
while (lo <= hi):
And here's where lo and hi can possibly change values:
lo = mid
...
hi = mid
The logical conclusion is that, if this code loops enough times, you'll end up with lo == hi. mid will then be set to the average of the two, which is equal to both of them, and that's where all three variables will stay. And as such, the loop will not terminate. There are two possible solutions: either change the way you're reassigning lo or hi, or change the termination condition to < instead of <=.
Related
I wrote a recursive quicksort algorithm in Python to sort a list of 10,000 ints. I am testing a worst case (pivot is always the low index, and the list is already sorted, so one of the recursive calls to quicksort is always empty, and the other always decreases by 1). I know that means I have to increase the recursion limit with
sys.setrecursionlimit(10000)
Now I am getting the error code
Process finished with exit code -1073741571 (0xC00000FD)
I read elsewhere that I should use
threading.stack_size()
But, I have no idea how to use this function. I tried passing in different values, and I will either get that the size is not valid, or the same error code. Can somebody please help me with this?
Stack space can be limited to O(log(n)) by recursing on smaller partition, and looping on larger partition. Worst case time complexity is still O(n^2).
def qsort(a, lo, hi):
while(lo < hi):
p = a[(lo + hi) // 2] # pivot, any a[] except a[hi]
i = lo - 1
j = hi + 1
while(1): # Hoare partition
while(1): # while(a[++i] < p)
i += 1
if(a[i] >= p):
break
while(1): # while(a[--j] < p)
j -= 1
if(a[j] <= p):
break
if(i >= j): # if indexes met or crossed, break
break
a[i],a[j] = a[j],a[i] # else swap elements and loop
if((j - lo) < (hi - j)): # recurse on smaller partition
qsort(a, lo, j) # loop on larger partition
lo = j+1
else:
qsort(a, j+1, hi)
hi = j
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
I'm trying to implement a recursive quicksort algorithm using two methods (swap, partition) while running the main algorithm using recursion in a lambda expression. I'm getting an infinite recursion error and honestly I can't find the syntax error. Can someone help me out? Thanks :)
def swap(array, a, b):
array[a], array[b] = array[b], array[a]
def partition(array, high, low):
pivot = array[high]
i = low
for x in range(low, high-1):
if array[x] < pivot:
i+=1
swap(array, array[x], array[high])
return i
g = lambda array, low, high: g(array,low,partition(array,high,low)-1)+g(array,partition(array,high,low)+1,high) if low < high else print("not sorted")
There are several issues in partition:
The call to swap is passing values from your list, instead of indices.
Even when the previous mistake is corrected, it will either move the pivot value to the low+1 index, or it will not move at all.
The returned index i, should be the one where the pivot was moved. In a correct implementation that means i is the last index to which a value was moved, which was the value at index high. This is not what is happening, as already with the first swap the pivot value is moved.
The swap should be of the current value with the value at i, so that all values up to the one at index i are less or equal to the pivot value.
Here is the corrected partition function:
def partition(array, high, low):
pivot = array[high]
i = low - 1
for x in range(low, high+1):
if array[x] <= pivot:
i+=1
swap(array, x, i)
return i
These are the issues in the function g:
It is supposed to perform the sort in-place, so the + operator for lists should not occur here, as that would create a new list. Moreover, the base case (in else) does not return anything, so the + operator will fail with an error
partition(array,high,low) is called twice, which is not only a waste, but the second call will in most cases return a different result, because the pivot can be different. This means the second call of g will potentially not work with an adjacent partition, but will either leave an (unsorted) gap, or work on an overlapping partition.
Here is a correction for the function g:
def g(array, low, high):
if low < high:
i = partition(array, high, low)
g(array, low, i-1)
g(array, i+1, high)
You should also consider using a better name than g, and change the order of the high/low parameters for partition: that reversed order is a good way to confuse the readers of your code.
Here is Hoare's quicksort algorithm implemented in Python -
def quicksort(A, lo, hi):
if lo >= 0 and hi >= 0 and lo < hi:
p = partition(A, lo, hi)
quicksort(A, lo, p)
quicksort(A, p + 1, hi)
def partition(A, lo, hi):
pivot = A[(hi + lo) // 2]
i = lo
j = hi
while True:
while A[i] < pivot:
i += 1
while A[j] > pivot:
j -= 1
if i >= j:
return j
swap(A, i, j)
def swap(A, i, j):
A[i], A[j] = A[j], A[i]
You can write g using lambda if you wish, but I would recommend to define an ordinary function instead -
g = lambda a: quicksort(a, 0, len(a) - 1)
Given a sample input, x -
x = [5,0,9,7,4,2,8,3,1,6]
g(x)
print(x)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
See this related Q&A if you would like to count the number of comparisons and swaps used.
I'm quite new to python and algorithm and I encountered a question which is defined as follows:
Suppose that you are given a python list l of size n which contains only numbers. We index l from 0 to n-1. Further, we suppose that there exists an index k ∈ {1, ..., n-2} such that
for all i ∈ {0, ..., k-1}, l[i] < l[i+1]
for all i ∈ {k, ..., n-2}, l[i] > l[i+1]
In other words, l is unimodal. An example with k=3 is given below:
l = [-5, 8, 12, 15, 13, 12, 10, 5, 1, 0, -2]
I can easily implement it using an iterative approach:
def findK(l):
k = 0
while l[k] < l[k + 1]:
k += 1
return k
But how can I do it using a recursive way which is O(logn)?
The maximum/minimum of a unimodal function can be obtained by using the concept of Ternary Search
def ternarySearch(f, left, right, absolutePrecision):
'''
left and right are the current bounds;
the maximum is between them
'''
if abs(right - left) < absolutePrecision:
return (left + right)/2
leftThird = (2*left + right)/3
rightThird = (left + 2*right)/3
if f(leftThird) < f(rightThird):
return ternarySearch(f, leftThird, right, absolutePrecision)
else:
return ternarySearch(f, left, rightThird, absolutePrecision)
The overall complexity of the solution is O(log3N). You can learn more about it from https://www.hackerearth.com/practice/algorithms/searching/ternary-search/tutorial/ or https://en.wikipedia.org/wiki/Ternary_search
Algorithm
You can use binary search to do this(if you wish to).
If we come across a pattern of b-1 < b < b+1 where b was our middle element, then surely the highest element is to the right side of the array.
If we come across a pattern of b-1 > b > b+1 where b was our middle element, then surely the highest element is to the left side of the array.
If we come across a pattern of b-1 < b > b+1 where b was our middle element, then this b is our answer.
CODE:
mid = 0,low=0,high = arr.size-1
while low <= high:
mid = low + (high - low) / 2
if arr[mid] > arr[mid-1] && arr[mid] > arr[mid + 1]:
return arr[mid]
else if arr[mid] < arr[mid-1] && arr[mid] > arr[mid + 1]:
high = mid - 1
else
low = mid + 1
Time complexity is O(log2n). But, as mentioned by #nellex in his answer, ternary search provides a better performance.
The recursive version of your code would be
def max_modal(list, start=0):
If start < len(list):
If list[start]>list[start+1]:
Return list[start]
Else:
Return max_modal(list,start=start+1)
Else:
Return list[start]
However in a interpreter language this Schild be a lot slower than the iterative way
def rwSteps(start, low, hi):
n=0
while low <= start <= hi:
print (start-low-1)*" " + "#" + (hi-start)*" ", n
start+=random.choice((-1,1))
n+=1
return "%d steps" % (n-1)
print rwSteps(10, 5, 15)
The above function is the function that I need to rewrite in a recursive fashion. The function takes in a starting point integer, and a low and a high point. From the starting point, the function should either do +1 or -1 from the starting point randomly until either the high limit or the low limit is reached. Here is what I have so far.
def RandomWalkSteps(start, low, hi):
count = 0
count = count + 1
if(low <= start <= hi):
count = count + 1
start+=random.choice((-1,1))
newStart = start
RandomWalkSteps(newStart, low, hi)
return count
I feel like I'm pretty close, but I'm running into trouble of where to put the "count" statement so that it increments properly at every instance of recursion. Any help would be appreciated and feel free to yell at me if I left out any crucial piece of information.
def RandomWalkSteps(start, low, hi):
if low < start < hi:
return 1 + RandomWalkSteps(random.choice((-1,1)), low, hi)
return 0
def RandomWalkSteps(start, low, hi, count=0):
if low < start < hi:
return RandomWalkSteps(start+random.choice((-1,1)), low, hi, count+1)
return count
print RandomWalkSteps(10, 5, 15)
I believe this is what you are looking for
def RandomWalkSteps(count, start, low, hi):
if low <= start <= hi:
start+=random.choice((-1,1))
newStart = start
return RandomWalkSteps(count+1, newStart, low, hi)
else:
return count
call RandomWalkSteps(0, x, y, z) instead of RandomWalkStep(x, y, z)