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.
Related
I have a question about the quickSort algorithm on the GeeksForGeeks website here: https://www.geeksforgeeks.org/python-program-for-quicksort/
The quickSort consists of the partition function shown on GeeksForGeeks as follows:
def partition(arr, low, high):
i = (low-1) # index of smaller element
pivot = arr[high] # pivot
for j in range(low, high):
# If current element is smaller than or
# equal to pivot
if arr[j] <= pivot:
# increment index of smaller element
i = i+1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return (i+1)
I am wondering why i is set to i = low - 1.
Why can't the function be rewritten like this (Notice all the i's):
def partition(arr, low, high):
i = low
pivot = arr[high]
for j in range(low, high):
if arr[j] <= pivot:
arr[i], arr[j] = arr[j], arr[i]
i += 1
arr[i], arr[high] = arr[high], arr[i]
return i
Your implementation of quicksort works. There are multiple correct implementations of quicksort out there. My guess is that GeeksForGeeks chose this implementation, but as to why they chose it?
You'd have to ask the writer of the article.
I think your question brings up a good point, that is, algorithms can be implemented in different, but similar ways.
I quickly wrote a script to test your version of the quicksort implementation. See it below.
def partition(arr, low, high):
i = low # index of smaller element
pivot = arr[high] # pivot
for j in range(low, high):
# If current element is smaller than or
# equal to pivot
if arr[j] <= pivot:
# increment index of smaller element
arr[i], arr[j] = arr[j], arr[i]
i = i+1
arr[i], arr[high] = arr[high], arr[i]
return i
def quickSort(arr, low, high):
if len(arr) == 1:
return arr
if low < high:
# pi is partitioning index, arr[p] is now
# at right place
pi = partition(arr, low, high)
# Separately sort elements before
# partition and after partition
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
# Driver code to test above
arr = []
for i in range(0, 10000):
import random
r = random.randint(-100000,100000)
arr.append(r)
n = len(arr)
quickSort(arr, 0, n-1)
# using naive method to
# check sorted list
flag = 0
i = 1
while i < len(arr):
if(arr[i] < arr[i - 1]):
flag = 1
i += 1
# printing result
if (not flag) :
print ("Sorted")
else :
print ("Not sorted")
print(arr)
Its all about efficiency, by starting i at low and starting j at low, you are testing a value against itself in the first run of the loop, which does absolutely nothing in the scope of sorting an array.
To change that and keep the efficiency, you would have to change the implementation of the quickSort method (as in the one that calls partition) but by doing that you end up touching the same index multiple times between the two recursive calls, which decreases efficiency again.
Quicksort is all about speed (hence the name). The changes you made, definitely don't break the algorithm, however when you scale up the input it does decrease it's speed.
I recently came across the QuickSort algorithm and found an example for it in Python on Geeksforgeeks here: https://www.geeksforgeeks.org/python-program-for-quicksort/
My question is this: the variable arr is defined outside of the function Quicksort.. so how is the variable known both in and out of the function without global or return?
Sorry if this was basically posted elsewhere or if posting other people's code is a no-no. Not trying to claim this as mine, I just haven't seen Python code do this before. It's not the algorithm itself nor the use of recursive functions.. it's the lack of global or return that confuses me.
def Partition(arr, low, high):
i= low - 1
pivot= arr[high]
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return i+1
def QuickSort(arr, low, high):
if len(arr) == 1:
return arr
if low < high:
pi= Partition(arr, low, high)
QuickSort(arr, low, pi-1)
QuickSort(arr, pi+1, high)
arr = [10, 7, 8, 9, 1, 5]
n= len(arr)
QuickSort(arr, 0, n-1)
print("Sorted array is:")
for i in range(n):
print("%d" % arr[i])
arr is being passed in as an argument here. It is misleading because the variable name in the function is the same as the global one, though it doesn't need to be. For instance, the following code is the same as what you posted:
def Partition(mylst, low, high):
i= low - 1
pivot= mylst[high]
for j in range(low, high):
if mylst[j] <= pivot:
i += 1
mylst[i], mylst[j] = mylst[j], mylst[i]
mylst[i+1], mylst[high] = mylst[high], mylst[i+1]
return i+1
def QuickSort(mylst, low, high):
if len(mylst) == 1:
return mylst
if low < high:
pi= Partition(mylst, low, high)
QuickSort(mylst, low, pi-1)
QuickSort(mylst, pi+1, high)
arr = [10, 7, 8, 9, 1, 5]
n= len(arr)
QuickSort(arr, 0, n-1)
print("Sorted array is:")
for i in range(n):
print("%d" % arr[i])
Even aside from that, as long as you are not changing what a variable is, you can still modify global values in Python. The distinction here is that you're not changing the value of arr, but the value of the actual array it is pointing to, so even if you wanted to modify the global array you would not need the global keyword.
Stuff like this is usually not done often in practice so don't worry too much about it. In this scenario at least, the array is being passed into the function, and the Quicksort function directly modifies the array that is passed into it (and it just so happens to be arr in this example).
it has been several days of trying to implement a functional recursive merge sort in Python. Aside from that, I want to be able to print out each step of the sorting algorithm. Is there any way to make this Python code run in a functional-paradigm way? This is what I have so far...
def merge_sort(arr, low, high):
# If low is less than high, proceed. Else, array is sorted
if low < high:
mid = (low + high) // 2 # Get the midpoint
merge_sort (arr, low, mid) # Recursively split the left half
merge_sort (arr, mid+1, high) # Recursively split the right half
return merge(arr, low, mid, high) # merge halves together
def merge (arr, low, mid, high):
temp = []
# Copy all the values into a temporary array for displaying
for index, elem in enumerate(arr):
temp.append(elem)
left_p, right_p, i = low, mid+1, low
# While left and right pointer still have elements to read
while left_p <= mid and right_p <= high:
if temp[left_p] <= temp[right_p]: # If the left value is less than the right value. Shift left pointer by 1 unit to the right
arr[i] = temp[left_p]
left_p += 1
else: # Else, place the right value into target array. Shift right pointer by 1 unit to the right
arr[i] = temp[right_p]
right_p += 1
i += 1 # Increment target array pointer
# Copy the rest of the left side of the array into the target array
while left_p <= mid:
arr[i] = temp[left_p]
i += 1
left_p += 1
print(*arr) # Display the current form of the array
return arr
def main():
# Get input from user
arr = [int(input()) for x in range(int(input("Input the number of elements: ")))]
print("Sorting...")
sorted_arr = merge_sort(arr.copy(), 0, len(arr)-1)
print("\nSorted Array")
print(*sorted_arr)
if __name__ == "__main__":
main()
Any help would be appreciated! Thank you.
In a purely functional merge sort, we don't want to mutate any values.
We can define a nice recursive version with zero mutation like so:
def merge(a1, a2):
if len(a1) == 0:
return a2
if len(a2) == 0:
return a1
if a1[0] <= a2[0]:
rec = merge(a1[1:], a2)
return [a1[0]] + rec
rec = merge(a1, a2[1:])
return [a2[0]] + rec
def merge_sort(arr):
if len(arr) <= 1:
return arr
halfway = len(arr) // 2
left = merge_sort(arr[:halfway])
right = merge_sort(arr[halfway:])
return merge(left, right)
You can add a print(arr) to the top of merge_sort to print step-by-step, however technically side-effects will make it impure (although still referentially transparent, in this case). In python, however, you can't separate out the side effects from the pure computation using monads, so if you want to really avoid this print, you'll have to return the layers, and print them at the end :)
Also, this version is technically doing a lot of copies of the list, so it's relatively slow. This can be fixed by using a linked list, and consing / unconsing it. However that's out of scope.
I'm trying to test the computation time of multiple sorting algorithms on increasingly larger sets of data. Quicksort has a memory error when it reaches 7000.
I have set the recursion limit to 10**6 in order to combat the maximum recursion depth when using timsort
from random import randint
import time
import math
start = 1000
finish = 10000
inc = 1000
import sys
sys.setrecursionlimit(10**6)
def generateNew(z):
listt = []
for o in range(z):
listt.append(randint(0, z))
return listt
...
def partition(arr,low,high):
i = ( low-1 )
pivot = arr[high]
for j in range(low , high):
if arr[j] <= pivot:
i = i+1
arr[i],arr[j] = arr[j],arr[i]
arr[i+1],arr[high] = arr[high],arr[i+1]
return ( i+1 )
def quickSort(arr,low,high):
if low < high:
pi = partition(arr,low,high)
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
def quickSortTimer(unsorted):
start = time.time()
array = unsorted
quickSort(array,0,len(array)-1)
end = time.time()
print("Quick Sort: "+str(end - start))
...
def runAll():
for h in range(start, finish+1, inc):
print("\n\nCurrent: "+str(h))
unsorted = generateNew(h)
oddEvenSortTimer(unsorted)
#stoogeSortTimer(unsorted)
gnomeSortTimer(unsorted)
#bogoSortTimer(unsorted)
binaryInserionSortTimer(unsorted)
pancakeSortTimer(unsorted)
bitonicSortTimer(unsorted)
cocktailSortTimer(unsorted)
cycleSortTimer(unsorted)
pigeonholeSortTimer(unsorted)
combSortTimer(unsorted)
timSortTimer(unsorted)
shellSortTimer(unsorted)
bucketSortTimer(unsorted)
radixSortTimer(unsorted)
heapSortTimer(unsorted)
quickSortTimer(unsorted)
mergeSortTimer(unsorted)
selectionSortTimer(unsorted)
insertionSortTimer(unsorted)
bubbleSortTimer(unsorted)
I expect the program to continue running but instead I get the error: MemoryError: Stack overflow. This question got marked as a duplicate of another question explaining how to increase the recursion depth, but this is another error. I want to maintain the recursion depth but avoid the stack overflow error.
To avoid stack overflow, use recursion on the smaller (or equal) part, then iterate back to handle the larger part.
def quicksort(a, lo, hi):
while(hi - lo > 0):
pivot = a[hi]
i = lo
for j in xrange(lo, hi):
if a[j] <= pivot:
a[i],a[j] = a[j],a[i]
i += 1
a[i],a[hi] = a[hi],a[i]
if(i - lo <= hi - i):
quicksort(a, lo, i-1)
lo = i+1
else:
quicksort(a, i+1, hi)
hi = i-1
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