pythonic quicksort. call stack exceeded - python

This is my attempted implementation of quicksort in python
However, I notice that I have a max call stack exceeded error, likely because the length of my collection never goes down.
When printing, I notice that my recursive call does not update the "end" variable
def swap(collection, index1, index2):
temp = collection[index1]
collection[index1] = collection[index2]
collection[index2] = temp
def partition(collection, idx1, idx2):
left = idx1
right = idx2
pivot = len(collection)//2
while(left < right):
while(collection[left] < collection[pivot]):
left += 1
while(collection[right] > collection[pivot]):
right -= 1
if left < right:
swap(collection, left, right)
left +=1
right -=1
return left
def quickSort(collection, start, end):
#Check for base case
if len(collection) > 1:
index = partition(collection, start, end)
newIdx = index - 1
if start < newIdx:
quickSort(collection, start, newIdx)
if index < right:
quickSort(collection, index, end)
return collection
array = [7,5, 10, 12, 3,2,9]
quickSort(array, 0, len(array) - 1)
print(array)

Related

Trying to find my error in my QuickSort Algorithm

Heres my QuickSort Algorithm that I came up with. The first function is the Partitioning, and the second function is the QuickSorting of an array.
def Partition(A ,start, end):
pivot = A[end]
p_index = start
for i in range(start,end-1):
if A[i] <= pivot:
#swap
A[i], A[i-1] = A[p_index], A[i]
p_index = p_index +1
# this is swaping the pivot point between the sorted values
A[p_index], A[end] = A[end], A[p_index]
return p_index
def QuickSort(A, start, end):
if start < end:
return
p_index = Partition(A, start, end)
# recursive Calls here:
QuickSort(A, start, p_index-1)
QuickSort(A,p_index +1, end)
The original array is returned unsorted
array = [2,1,3,6,8,5,7,4]
n = len(array)
QuickSort(array,0,n-1)
print(array)
returned: [2,1,3,6,8,5,7,4]
Your base case logic is incorrect.
You want to return when 'start' is greater or equal to 'end':
def QuickSort(A, start, end):
if start >= end:
return
p_index = Partition(A, start, end)
# recursive Calls here:
QuickSort(A, start, p_index-1)
QuickSort(A,p_index +1, end)

Python recursive Merge Sort by index

I have a question about the Python version of recursive Merge Sort. I completed the basic version, which is only referred by the array, and am now working on the index version. I will sink into the endless loop, but I am not sure where I did wrong. Would you mind sharing some ideas? Thank you in advance.
The successful and non-index version:
def mergesort(x):
# The base case is when the array contains less than 1 observation.
length = len(x)
if length < 2:
return x
else:
# Recursive case:merge sort on the lower subarray, and the upper subarray.
mid = (length) // 2
lower = mergesort(x[:mid])
upper = mergesort(x[mid:])
# merge two subarrays.
x_sorted = merge(lower, upper)
return (x_sorted)
def merge(lower, upper):
nlower = len(lower)
nupper = len(upper)
i, j, k = 0, 0, 0
# create a temp array to store the sorted results
temp = [0] * (nlower + nupper)
# as the lower and upper are sorted, since the base case is the single observation.
# now we compare the smallest element in each sorted array, and store the smaller one to the temp array
# repeat this process until one array is completed moved to the temp array
# store the other array to the end of the temp array
while i < nlower and j < nupper:
if lower[i] <= upper[j]:
temp[k] = lower[i]
i += 1
k += 1
else:
temp[k] = upper[j]
j += 1
k += 1
if i == nlower:
temp[i+j:] = upper[j:]
else:
temp[i+j:] = lower[i:]
return temp
With expected results:
x = random.sample(range(0, 30), 15)
mergesort(x)
[0, 1, 3, 6, 9, 10, 11, 13, 14, 17, 18, 20, 25, 27, 29]
But I will stuck into the endless loop with the index version:
def ms(x, left, right):
# the base case: right == left as a single-element array
if left < right:
mid = (left + right) // 2
ms(x, left, mid)
ms(x, mid, right + 1)
m(x, left, mid, right)
return m
def m(x, left, mid, right):
nlower = mid - left
nupper = right - mid + 1
temp = [0] * (nlower + nupper)
ilower, iupper, k = left, mid, 0
while ilower < mid and iupper < right + 1:
if x[ilower] <= x[iupper]:
temp[k] = x[ilower]
ilower += 1
k += 1
else:
temp[k] = x[iupper]
iupper += 1
k += 1
if ilower == mid:
temp[k:] = x[iupper:right+1]
else:
temp[k:] = x[ilower:mid]
x[left:right+1] = temp
return x
The test data as:
x = random.sample(range(0, 30), 15)
ms(x, 0, 14)
---------------------------------------------------------------------------
RecursionError Traceback (most recent call last)
<ipython-input-59-39859c9eae4a> in <module>
1 x = random.sample(range(0, 30), 15)
----> 2 ms(x, 0, 14)
... last 2 frames repeated, from the frame below ...
<ipython-input-57-854342dcdefb> in ms(x, left, right)
3 if left < right:
4 mid = (left + right)//2
----> 5 ms(x, left, mid)
6 ms(x, mid, right+1)
7 m(x, left, mid, right)
RecursionError: maximum recursion depth exceeded in comparison
Would you mind providing some insights? Thanks.
Your index version uses a confusing convention whereby left is the index of the first element in the slice and right is the index of the last element. This convention requires error-prone +1/-1 adjustments. Your problem is this: mid as computed is the index of the last element in the left half, but you consider mid to be the first element of the right half: a slice of 2 elements is split into one with 0 and one with 2, hence the infinite recursion. You can fix this problem by changing ms(x, mid, right+1) to ms(x, mid+1, right), etc.
Furthermore, retuning m from function ms makes no sense. You should return x if anything at all.
It is much less error prone for right to be the index one past the last element, just like range specifiers in Python. This way there are no confusing +1/-1 adjustments.
Here is modified version:
def ms(x, left, right):
# the base case: right - left as a single-element array
if right - left >= 2:
mid = (left + right) // 2 # index of the first element of the right half
ms(x, left, mid)
ms(x, mid, right)
m(x, left, mid, right)
return x
def m(x, left, mid, right):
nlower = mid - left
nupper = right - mid
temp = [0] * (nlower + nupper)
ilower, iupper, k = left, mid, 0
while ilower < mid and iupper < right:
if x[ilower] <= x[iupper]:
temp[k] = x[ilower]
ilower += 1
k += 1
else:
temp[k] = x[iupper]
iupper += 1
k += 1
if ilower == mid:
temp[k:] = x[iupper:right]
else:
temp[k:] = x[ilower:mid]
x[left:right] = temp
return x
You would invoke as:
x = random.sample(range(0, 30), 15)
ms(x, 0, len(x))

Quick Sort Algorithm using python

I took the first element as starting and pivot value.
Incrementing start position when pivot is less than end value.
Decrementing end position when pivot is greater than end value.
Also, swapping starting and ending position when each pass completes.
If starting position cross the end position then I'm swapping pivot and end elements.
def partition(arr, lb, ub):
pivot = arr[lb]
start = lb
print('start', start)
end = ub
print('end', end)
while start <= end:
while arr[start] <= pivot:
start += 1
while arr[end] >= pivot:
end -= 1
if start <= end:
arr[start], arr[end] = arr[end], arr[start]
else:
arr[end], arr[lb] = arr[lb], arr[end]
return end
def quickSort(arr, lb, ub):
if lb >= ub:
return 0
loc = partition(arr, lb, ub)
quickSort(arr, lb, loc-1)
quickSort(arr, loc+1, ub)
arr = [10, 4, 7, 3, 8, 6, 9, 1, 5, 2]
n = len(arr)
print(n)
quickSort(arr, 0, len(arr) -1)
print ("Sorted array is:")
for i in range(n):
print("%d" % arr[i])
I am getting the following error:
IndexError
---> quickSort(arr, 0, len(arr) -1)
Error in partition(arr, lb, ub)
---> while arr[start] <= pivot:
IndexError: list index out of range
Can someone please tell whats wrong in this code?
This is the direct cause of the error:
while arr[start] <= pivot:
start += 1
while arr[end] >= pivot:
end -= 1
You don't check whether start isn't larger than ub (and whether end isn't smaller than lb). This is what you should do:
while start < ub and arr[start] <= pivot:
start += 1
while end > lb and arr[end] >= pivot:
end -= 1
However, there's another thing that is wrong:
while start <= end:
should be
while start < end:
because for the weak inequality the loop never ends - it reaches the state of start == end and loops there endlessly. The inequality here:
if start <= end:
also should be a strong one, otherwise for start == end you're swapping arr[end] with arr[start] (ie. itself) instead of arr[lb] (ie. the pivot).
And finally, formatting of your code posted here was botched, so I took the liberty of fixing it.
Not sure if you still don't have a solution yet, but here is a revised partition function.
def partition(arr, lb, ub):
pivot = arr[lb]
start = lb + 1
print('start', start)
end = ub
print('end', end)
while start <= end:
while start <= end and arr[start] <= pivot:
start += 1
while end >= start and arr[end] > pivot:
end -= 1
if start < end:
arr[start], arr[end] = arr[end], arr[start]
if start >= end:
start -= 1
arr[start], arr[lb] = arr[lb], arr[start]
return start
You need to check whether start does not increments more than the end such as following,
while arr[start] <= pivot:
if start<end:
start += 1

quick sort implementation using python, can't figure out where I'm wrong in the implementation

Below is the code that I'm implementing for quick sort in python. But I'm not able to get where I'm going wrong with the implementation. Any leads?
def quicksort(arr, size):
partition(arr, size)
def partition(arr, size):
if size <= 1:
return
left = 0
right = size - 1
pivot = arr[size/2]
while left < right:
while arr[left] < pivot:
left += 1
while arr[right] > pivot:
right -= 1
temp = arr[left]
arr[left] = arr[right]
arr[right] = temp
partition(arr, left)
partition(arr[left:], len(arr[left:]))
arr = [1,2,3,4,5,45,3,5,4,6]
quicksort(arr, len(arr))
You are sorting new slices:
partition(arr[left:], len(arr[left:]))
Those slices are new lists, not the original list object. Anything you do to those won't be visible in the original value of arr.
If you want to do the sorting in-place, you'll need to pass along start and stop values of what section of the array to sort, not pass along slices:
def quicksort(arr):
partition(arr, 0, len(arr) - 1)
def partition(arr, start, stop):
if stop - start < 1:
return
left = start
right = stop
pivot = arr[start + ((stop - start) / 2)]
while left <= right:
while arr[left] < pivot:
left += 1
while arr[right] > pivot:
right -= 1
if left <= right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
partition(arr, start, right)
partition(arr, left, stop)
I've made a few more tweaks, adjusting the boundary tests and using tuple assignment to swap the two elements.
This version sorts correctly:
>>> def quicksort(arr):
... partition(arr, 0, len(arr) - 1)
...
>>> def partition(arr, start, stop):
... if stop - start < 1:
... return
... left = start
... right = stop
... pivot = arr[start + ((stop - start) / 2)]
... while left <= right:
... while arr[left] < pivot:
... left += 1
... while arr[right] > pivot:
... right -= 1
... if left <= right:
... arr[left], arr[right] = arr[right], arr[left]
... left += 1
... right -= 1
... partition(arr, start, right)
... partition(arr, left, stop)
...
>>> arr = [1,2,3,4,5,45,3,5,4,6]
>>> quicksort(arr)
>>> arr
[1, 2, 3, 3, 4, 4, 5, 5, 6, 45]

QuickSort Returning Recursion Depth Error

I have what should be a simple quicksort implementation, but it's returning a recursion depth exceeded error, and I'm testing it on a list of less than 30 elements. Moreover, my implementation was working on a list of 10,000 a few days ago, and the only thing I changed was moving it from a Class to a global function. Anyone see what may be causing this?
def quickSort(m, left, right):
if len(m[left:right]) <= 1:
return m
pivot = m[left]
i = left + 1
j = left + 1
for j in range(j, right):
if m[j] <= pivot:
m[j], m[i] = m[i], m[j]
i += 1
m[left], m[i-1] = m[i-1], m[left]
m = quickSort(m, left, i)
m = quickSort(m, i, right)
return m
one of your recursive calls is causing the exception(as you may have guessed :-), also note that you sort the list in place so returning the list is not necessary
def quickSort(m, left, right):
if right - left <= 1:
return
pivot = m[left]
i = left + 1
j = left + 1
for j in range(j, right):
if m[j] <= pivot:
m[j], m[i] = m[i], m[j]
i += 1
m[left], m[i-1] = m[i-1], m[left]
quickSort(m, left, i-1)
quickSort(m, i, right)

Categories

Resources