Implementing Median of Median Selection Algorithm in Python - python

Okay. I give up. I've been trying to implement the median of medians algorithm but I am continually given the wrong result. I know there is a lot of code below, but I can't find my error, and each chunk of code has a fairly process design. Quicksort is what I use to sort the medians I get from the median of medians pivot selection. Should be a straightforward quicksort implementation. getMean simply returns the mean of a given list.
getPivot is what I use to select the pivot. It runs through the list, taking 5 integers at a time, finding the mean of those integers, placing that mean into a list c, then finding the median of c. That is the pivot I use for the dSelect algorithm.
The dSelect algorithm is simple in theory. The base case returns an item when the list is 1 item long. Otherwise, much like in quickSort, I iterate over the list. If the number I am currently on, j, is less than the pivot, I move it to the left of the list, i, and increment i. If it is larger, I move it to the right of the list, i + 1, and do not increment i. After this loops through the entire list, I should have the pivot in its proper index, and print statements indicate that I do. At this point, I recurse to the left or the right depending on whether the pivot is greater than or less than the position I am trying to find.
I am not sure what other print statements to test at this point, so I'm turning to anyone dedicated enough to take a stab at this code. I know there are related topics, I know I could do more print statements, but believe me, I've tried. What should be a simple algo has got me quite stumped.
def quickSort(m, left, right):
if right - left <= 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
elif m[j] == pivot:
m[j], m[i] = m[i], m[j]
print m
m[left], m[i-1] = m[i-1], m[left]
m = quickSort(m, left, i-1)
m = quickSort(m, i, right)
print m
return m
def getMedian(list):
length = len(list)
if length <= 1:
return list[0]
elif length % 2 == 0:
i = length/2
return list[i]
else:
i = (length + 1)/2
return list[i]
def getPivot(m):
c = []
i = 0
while i <= len(m) - 1:
tempList = []
j = 0
while j < 5 and i <= len(m) - 1:
tempList.append(m[i])
i = i + 1
j = j + 1
tempList = quickSort(tempList, 0, len(tempList) - 1)
c.append(getMedian(tempList))
c = quickSort(c, 0, len(c) - 1)
medianOfMedians = getMedian(c)
return medianOfMedians
def dSelect(m, position):
pivot = getPivot(m)
i = 0
j = 0
if len(m) <= 1:
return m[0]
for j in range(0, len(m)):
if m[j] < pivot:
m[j], m[i] = m[i], m[j]
i += 1
elif m[j] == pivot:
m[j], m[i] = m[i], m[j]
print "i: " + str(i)
print "j: " + str(j)
print "index of pivot: " + str(m.index(pivot))
print "pivot: " + str(pivot) + " list: " + str(m)
if m.index(pivot) == position:
return pivot
elif m.index(pivot) > position:
return dSelect(m[0:i], position)
else:
return dSelect(m[i:], position - i)

The biggest issue is with this line here:
i = (length + 1)/2
if list = [1, 2, 3, 4, 5, 6, 7] the answer should be 4 which is list[3]. Your version looks like the following:
i = (7 + 1) / 2
and so i is equal to 4 instead of 3. Similar problem with the even length list part although that shouldn't be as big of an issue.

Related

merge, heap, and quick sort counts are not coming out properly

import random, timeit
#Qucik sort
def quick_sort(A,first,last):
global Qs,Qc
if first>=last: return
left, right= first+1, last
pivot = A[first]
while left <= right:
while left <=last and A[left]<pivot:
Qc= Qc+1
left= left + 1
while right > first and A[right] >= pivot:
Qc=Qc+1
right = right -1
if left <= right:
A[left],A[right]=A[right],A[left]
Qs = Qs+1
left= left +1
right= right-1
A[first],A[right]=A[right],A[first]
Qs=Qs+1
quick_sort(A,first,right-1)
quick_sort(A,right+1,last)
#Merge sort
def merge_sort(A, first, last): # merge sort A[first] ~ A[last]
global Ms,Mc
if first >= last: return
middle = (first+last)//2
merge_sort(A, first, middle)
merge_sort(A, middle+1, last)
B = []
i = first
j = middle+1
while i <= middle and j <= last:
Mc=Mc+1
if A[i] <= A[j]:
B.append(A[i])
i += 1
else:
B.append(A[j])
j += 1
for i in range(i, middle+1):
B.append(A[i])
Ms=Ms+1
for j in range(j, last+1):
B.append(A[j])
for k in range(first, last+1): A[k] = B[k-first]
#Heap sort
def heap_sort(A):
global Hs, Hc
n = len(A)
for i in range(n - 1, -1, -1):
while 2 * i + 1 < n:
left, right = 2 * i + 1, 2 * i + 2
if left < n and A[left] > A[i]:
m = left
Hc += 1
else:
m = i
Hc += 1
if right < n and A[right] > A[m]:
m = right
Hc += 1
if m != i:
A[i], A[m] = A[m], A[i]
i = m
Hs += 1
else:
break
for i in range(n - 1, -1, -1):
A[0], A[i] = A[i], A[0]
n -= 1
k = 0
while 2 * k + 1 < n:
left, right = 2 * k + 1, 2 * k + 2
if left < n and A[left] > A[k]:
m = left
Hc += 1
else:
m = k
Hc += 1
if right < n and A[right] > A[m]:
m = right
Hc += 1
if m != k:
A[k], A[m] = A[m], A[k]
k = m
Hs += 1
else:
break
#
def check_sorted(A):
for i in range(n-1):
if A[i] > A[i+1]: return False
return True
#
#
Qc, Qs, Mc, Ms, Hc, Hs = 0, 0, 0, 0, 0, 0
n = int(input())
random.seed()
A = []
for i in range(n):
A.append(random.randint(-1000,1000))
B = A[:]
C = A[:]
print("")
print("Quick sort:")
print("time =", timeit.timeit("quick_sort(A, 0, n-1)", globals=globals(), number=1))
print(" comparisons = {:10d}, swaps = {:10d}\n".format(Qc, Qs))
print("Merge sort:")
print("time =", timeit.timeit("merge_sort(B, 0, n-1)", globals=globals(), number=1))
print(" comparisons = {:10d}, swaps = {:10d}\n".format(Mc, Ms))
print("Heap sort:")
print("time =", timeit.timeit("heap_sort(C)", globals=globals(), number=1))
print(" comparisons = {:10d}, swaps = {:10d}\n".format(Hc, Hs))
assert(check_sorted(A))
assert(check_sorted(B))
assert(check_sorted(C))
I made the code that tells how much time it takes to sort list size n(number input) with 3 ways of sorts. However, I found that my result is quite unexpected.
Quick sort:
time = 0.0001289689971599728
comparisons = 474, swaps = 168
Merge sort:
time = 0.00027709499408956617
comparisons = 541, swaps = 80
Heap sort:
time = 0.0002578190033091232
comparisons = 744, swaps = 478
Quick sort:
time = 1.1767549149953993
comparisons = 3489112, swaps = 352047
Merge sort:
time = 0.9040642600011779
comparisons = 1536584, swaps = 77011
Heap sort:
time = 1.665754442990874
comparisons = 2227949, swaps = 1474542
Quick sort:
time = 4.749891302999458
comparisons = 11884246, swaps = 709221
Merge sort:
time = 3.1966246420051903
comparisons = 3272492, swaps = 154723
Heap sort:
time = 6.2041203819972
comparisons = 4754829, swaps = 3148479
as you see, my results are very different from what I learned. Can you please tell me why quick sort is not the fastest in my code? and why merge is the fastest one.
I can see that you are choosing the first element of the array as the pivot in quicksort. Now, consider the order of the elements of the unsorted array. Is it random? How do you generate the input array?
You see, if the pivot was either the min or max value of the aray, or somewhere close to the mind/max value, the running time of quicksort in that case (worst case) will be in the order of O(n^2). That is because on each iteration, you are partitioning the arry by breaking off only one element.
For optimal quicksort performance of O(n log n), your pivot should be as close to the median value as possible. In order to increase the likelihood of that being the case, consider initially picking 3 values at random in from the array, and use the median value as the pivot. Obviously, the more values you choose the median from initially the better the probability that your pivot is more efficient, but you are adding extra moves by choosing those values to begin with, so it's a trade off. I imagine one would even be able to calculate exactly how many elements should be selected in relation to the size of the array for optimal performance.
Merge sort on the other hand, always has the complexity in the order of O(n log n) irrespective of input, which is why you got consistent results with it over different samples.
TL:DR my guess is that the input array's first element is very close to being the smallest or largest value of that array, and it ends up being the pivot of your quicksort algorithm.

List index out of range in lists and matrix

I am a beginner to programming. I always encounter this problem for matrices. Please help me to correct this code and understand the concept behind this. Thank you.
def new_matrix(matrix_size, center_num):
c = matrix_size / 2
if matrix_size % 2 != 0:
p = matrix_size
print("enter a valid size for matrix")
else:
matrix = [[0] * matrix_size for z in range(matrix_size)]
for counting in range(center_num, (matrix_size ** 2) + center_num):
for i in range(2, matrix_size+1):
row = int(c)
column = int(c)
if (i % 2 == 0):
for k in range(1, i + 1): # moving right
column += 1
matrix[column][row] = counting
for k in range(1, i + 1): # moving up
row += 1
matrix[column][row] = counting
else:
for k in range(1, i + 1): # moving left
column -= 1
matrix[column][row] = counting
for k in range(1, i + 1): # moving down
row -= 1
matrix[column][row] = counting
print(matrix)
new_matrix(6, 2)
This issue here seems to be that your indexing is off. The end value in range(n) is n-1, which means that in your for loop when i = matrix_size and then you try to increment the column beyond this, you're trying to assign a value to an index in the matrix that doesn't exist.
You can either try and fix your loop, i.e. reduce it by one, or you can do something like the following,
try:
<your code goes here>
except IndexError:
pass
And then anytime the loop hits an index error it will skip that index and try the next. It's better to fix the code though!

QuickSort with Pivot Function issue?

I have been looking at this for the past couple hours and can still not understand where I have messed up. I keep getting Index out of bounds errors like below:
Each small edit or change i have done, has run me into another error, then i end back up here after trying to simplify my code.
def quickSort(alist):
firstList = []
secondList = []
thirdList = []
if(len(alist) > 1):
#pivot = pivot_leftmost(alist)
#pivot = pivot_best_of_three(alist)
pivot = pivot_ninther(alist)
#pivot = pivot_random(alist)
for item in alist:
if(item < pivot):
firstList.append(item)
if(item == pivot):
secondList.append(item)
if(item > pivot):
thirdList.append(item)
sortedList = quickSort(firstList) + secondList + quickSort(thirdList)
return sortedList
else:
print("list:", alist)
print("sorted, nothing to do") #debug
print("") #debug
return alist
def pivot_ninther(alist):
listLength = int(len(alist))
third = int(listLength / 3)
leftList = alist[:third]
midlist = alist[third:(third * 2)]
lastlist = alist[(third * 2):(third * 3)]
leftBest = pivot_best_of_three(leftList)
midBest = pivot_best_of_three(midlist)
lastBest = pivot_best_of_three(lastlist)
pivots = [leftBest, midBest, lastBest]
return pivot_best_of_three(pivots)
I am pretty sure a fresh pair of eyes can easily find it, but i have been lookig at it for hours. Thanks!
UPDATE: (My Best_of_three function)
def pivot_best_of_three(alist):
leftmost = 0
middle = int(len(alist) / 2)
rightmost = len(alist) - 1
if (alist[leftmost] <= alist[middle] <= alist[rightmost] or alist[rightmost] <= alist[middle] <= alist[leftmost]):
return alist[middle]
elif (alist[middle] <= alist[leftmost] <= alist[rightmost] or alist[rightmost] <= alist[leftmost] <= alist[middle]):
return alist[leftmost]
else:
return alist[rightmost]
The IndexError occurs when pivot_best_of_three tries to find the rightmost member of a list of zero items. The simple way to fix that is to simply not pass it such lists. :)
Here are slightly modified versions of those functions. I've tested these versions with lists of various lengths, down to length zero, and they appear to function correctly.
def pivot_ninther(alist):
listLength = len(alist)
if listLength < 3:
return alist[0]
third = listLength // 3
leftList = alist[:third]
midlist = alist[third:-third]
lastlist = alist[-third:]
leftBest = pivot_best_of_three(leftList)
midBest = pivot_best_of_three(midlist)
lastBest = pivot_best_of_three(lastlist)
pivots = [leftBest, midBest, lastBest]
return pivot_best_of_three(pivots)
def pivot_best_of_three(alist):
leftmost = alist[0]
middle = alist[len(alist) // 2]
rightmost = alist[-1]
if (leftmost <= middle <= rightmost) or (rightmost <= middle <= leftmost):
return middle
elif (middle <= leftmost <= rightmost) or (rightmost <= leftmost <= middle):
return leftmost
else:
return rightmost
As you can see, I've simplified pivot_best_of_three so it doesn't index alist multiple times for the same value.
But it can be simplified further by using a simple sorting network:
def sort3(a, b, c):
if c < b: b, c = c, b
if b < a: a, b = b, a
if c < b: b, c = c, b
return a, b, c
def pivot_best_of_three(alist):
leftmost = alist[0]
middle = alist[len(alist) // 2]
rightmost = alist[-1]
return sort3(leftmost, middle, rightmost)[1]
Try a smaller count (<= 20) and check to see what happens in pivot_ninther() when third == 0 (at the deepest level of recursion)? Seems like it would create empty arrays and then try to index them.
The code should check to make sure length >= 9 before calling pivot_ninther(), then >= 3 if calling ...best_of_three(). If just one or two items, pick one.
Suggestion, after you get the quicksort to work, back up the source code and rather than make new arrays, the pivot function should work with the original array and first / middle / last indexes.
You can use swaps to simplify finding the median of 3. This will help in cases like starting with a reversed order array.
// median of 3
i = lo, j = (lo + hi)/2, k = hi;
if (a[k] < a[i])
swap(a[k], a[i]);
if (a[j] < a[i])
swap(a[j], a[i]);
if (a[k] < a[j])
swap(a[k], a[j]);
pivot = a[j];
wiki article:
http://en.wikipedia.org/wiki/Quicksort#Choice_of_pivot

What will be the big-O notation for this code?

Will it be O(n) or greater?
n = length of list
a is a list of integers that is very long
final_count=0
while(n>1):
i=0
j=1
while(a[i+1]==a[i]):
i=i+1
j=j+1
if i==n-1:
break
for k in range(j):
a.pop(0)
final_count=final_count+j*(n-j)
n=n-j
The way I see it, your code would be O(n) if it wasn’t for the a.pop(0) part. Since lists are implemented as arrays in memory, removing an element at the top means that all elements in the array need to moved as well. So removing from a list is O(n). You do that in a loop over j and as far as I can tell, in the end, the sum of all js will be the same as n, so you are removing the item n times from the list, making this part quadratic (O(n²)).
You can avoid this though by not modifying your list, and just keeping track of the initial index. This not only removes the need for the pop, but also the loop over j, making the complexity calculation a bit more straight-forward:
final_count = 0
offset = 0
while n > 1:
i = 0
j = 1
while a[offset + i + 1] == a[offset + i]:
i += 1
j += 1
if i == n - 1:
break
offset += j
final_count += j * (n - j)
n = n - j
Btw. it’s not exactly clear to me why you keep track of j, since j = i + 1 at every time, so you can get rid of that, making the code a bit simpler. And at the very end j * (n - j) is exactly j * n if you first adjust n:
final_count = 0
offset = 0
while n > 1:
i = 0
while a[offset + i + 1] == a[offset + i]:
i += 1
if i == n - 1:
break
offset += i + 1
n = n - (i + 1)
final_count += (i + 1) * n
One final note: As you may notice, offset is now counting from zero to the length of your list, while n is doing the reverse, counting from the length of your list to zero. You could probably combine this, so you don’t need both.

Quicksort with Python

I am totally new to python and I am trying to implement quicksort in it.
Could someone please help me complete my code?
I do not know how to concatenate the three arrays and print them.
def sort(array=[12,4,5,6,7,3,1,15]):
less = []
equal = []
greater = []
if len(array) > 1:
pivot = array[0]
for x in array:
if x < pivot:
less.append(x)
if x == pivot:
equal.append(x)
if x > pivot:
greater.append(x)
sort(less)
sort(pivot)
sort(greater)
def sort(array):
"""Sort the array by using quicksort."""
less = []
equal = []
greater = []
if len(array) > 1:
pivot = array[0]
for x in array:
if x < pivot:
less.append(x)
elif x == pivot:
equal.append(x)
elif x > pivot:
greater.append(x)
# Don't forget to return something!
return sort(less)+equal+sort(greater) # Just use the + operator to join lists
# Note that you want equal ^^^^^ not pivot
else: # You need to handle the part at the end of the recursion - when you only have one element in your array, just return the array.
return array
Quick sort without additional memory (in place)
Usage:
array = [97, 200, 100, 101, 211, 107]
quicksort(array)
print(array)
# array -> [97, 100, 101, 107, 200, 211]
def partition(array, begin, end):
pivot = begin
for i in range(begin+1, end+1):
if array[i] <= array[begin]:
pivot += 1
array[i], array[pivot] = array[pivot], array[i]
array[pivot], array[begin] = array[begin], array[pivot]
return pivot
def quicksort(array, begin=0, end=None):
if end is None:
end = len(array) - 1
def _quicksort(array, begin, end):
if begin >= end:
return
pivot = partition(array, begin, end)
_quicksort(array, begin, pivot-1)
_quicksort(array, pivot+1, end)
return _quicksort(array, begin, end)
There is another concise and beautiful version
def qsort(arr):
if len(arr) <= 1:
return arr
else:
return qsort([x for x in arr[1:] if x < arr[0]])
+ [arr[0]]
+ qsort([x for x in arr[1:] if x >= arr[0]])
Let me explain the above codes for details
pick the first element of array arr[0] as pivot
[arr[0]]
qsort those elements of array which are less than pivot with List Comprehension
qsort([x for x in arr[1:] if x < arr[0]])
qsort those elements of array which are larger than pivot with List Comprehension
qsort([x for x in arr[1:] if x >= arr[0]])
This answer is an in-place QuickSort for Python 2.x. My answer is an interpretation of the in-place solution from Rosetta Code which works for Python 3 too:
import random
def qsort(xs, fst, lst):
'''
Sort the range xs[fst, lst] in-place with vanilla QuickSort
:param xs: the list of numbers to sort
:param fst: the first index from xs to begin sorting from,
must be in the range [0, len(xs))
:param lst: the last index from xs to stop sorting at
must be in the range [fst, len(xs))
:return: nothing, the side effect is that xs[fst, lst] is sorted
'''
if fst >= lst:
return
i, j = fst, lst
pivot = xs[random.randint(fst, lst)]
while i <= j:
while xs[i] < pivot:
i += 1
while xs[j] > pivot:
j -= 1
if i <= j:
xs[i], xs[j] = xs[j], xs[i]
i, j = i + 1, j - 1
qsort(xs, fst, j)
qsort(xs, i, lst)
And if you are willing to forgo the in-place property, below is yet another version which better illustrates the basic ideas behind quicksort. Apart from readability, its other advantage is that it is stable (equal elements appear in the sorted list in the same order that they used to have in the unsorted list). This stability property does not hold with the less memory-hungry in-place implementation presented above.
def qsort(xs):
if not xs: return xs # empty sequence case
pivot = xs[random.choice(range(0, len(xs)))]
head = qsort([x for x in xs if x < pivot])
tail = qsort([x for x in xs if x > pivot])
return head + [x for x in xs if x == pivot] + tail
Quicksort with Python
In real life, we should always use the builtin sort provided by Python. However, understanding the quicksort algorithm is instructive.
My goal here is to break down the subject such that it is easily understood and replicable by the reader without having to return to reference materials.
The quicksort algorithm is essentially the following:
Select a pivot data point.
Move all data points less than (below) the pivot to a position below the pivot - move those greater than or equal to (above) the pivot to a position above it.
Apply the algorithm to the areas above and below the pivot
If the data are randomly distributed, selecting the first data point as the pivot is equivalent to a random selection.
Readable example:
First, let's look at a readable example that uses comments and variable names to point to intermediate values:
def quicksort(xs):
"""Given indexable and slicable iterable, return a sorted list"""
if xs: # if given list (or tuple) with one ordered item or more:
pivot = xs[0]
# below will be less than:
below = [i for i in xs[1:] if i < pivot]
# above will be greater than or equal to:
above = [i for i in xs[1:] if i >= pivot]
return quicksort(below) + [pivot] + quicksort(above)
else:
return xs # empty list
To restate the algorithm and code demonstrated here - we move values above the pivot to the right, and values below the pivot to the left, and then pass those partitions to same function to be further sorted.
Golfed:
This can be golfed to 88 characters:
q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])
To see how we get there, first take our readable example, remove comments and docstrings, and find the pivot in-place:
def quicksort(xs):
if xs:
below = [i for i in xs[1:] if i < xs[0]]
above = [i for i in xs[1:] if i >= xs[0]]
return quicksort(below) + [xs[0]] + quicksort(above)
else:
return xs
Now find below and above, in-place:
def quicksort(xs):
if xs:
return (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
else:
return xs
Now, knowing that and returns the prior element if false, else if it is true, it evaluates and returns the following element, we have:
def quicksort(xs):
return xs and (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
Since lambdas return a single epression, and we have simplified to a single expression (even though it is getting more unreadable) we can now use a lambda:
quicksort = lambda xs: (quicksort([i for i in xs[1:] if i < xs[0]] )
+ [xs[0]]
+ quicksort([i for i in xs[1:] if i >= xs[0]]))
And to reduce to our example, shorten the function and variable names to one letter, and eliminate the whitespace that isn't required.
q=lambda x:x and q([i for i in x[1:]if i<=x[0]])+[x[0]]+q([i for i in x[1:]if i>x[0]])
Note that this lambda, like most code golfing, is rather bad style.
In-place Quicksort, using the Hoare Partitioning scheme
The prior implementation creates a lot of unnecessary extra lists. If we can do this in-place, we'll avoid wasting space.
The below implementation uses the Hoare partitioning scheme, which you can read more about on wikipedia (but we have apparently removed up to 4 redundant calculations per partition() call by using while-loop semantics instead of do-while and moving the narrowing steps to the end of the outer while loop.).
def quicksort(a_list):
"""Hoare partition scheme, see https://en.wikipedia.org/wiki/Quicksort"""
def _quicksort(a_list, low, high):
# must run partition on sections with 2 elements or more
if low < high:
p = partition(a_list, low, high)
_quicksort(a_list, low, p)
_quicksort(a_list, p+1, high)
def partition(a_list, low, high):
pivot = a_list[low]
while True:
while a_list[low] < pivot:
low += 1
while a_list[high] > pivot:
high -= 1
if low >= high:
return high
a_list[low], a_list[high] = a_list[high], a_list[low]
low += 1
high -= 1
_quicksort(a_list, 0, len(a_list)-1)
return a_list
Not sure if I tested it thoroughly enough:
def main():
assert quicksort([1]) == [1]
assert quicksort([1,2]) == [1,2]
assert quicksort([1,2,3]) == [1,2,3]
assert quicksort([1,2,3,4]) == [1,2,3,4]
assert quicksort([2,1,3,4]) == [1,2,3,4]
assert quicksort([1,3,2,4]) == [1,2,3,4]
assert quicksort([1,2,4,3]) == [1,2,3,4]
assert quicksort([2,1,1,1]) == [1,1,1,2]
assert quicksort([1,2,1,1]) == [1,1,1,2]
assert quicksort([1,1,2,1]) == [1,1,1,2]
assert quicksort([1,1,1,2]) == [1,1,1,2]
Conclusion
This algorithm is frequently taught in computer science courses and asked for on job interviews. It helps us think about recursion and divide-and-conquer.
Quicksort is not very practical in Python since our builtin timsort algorithm is quite efficient, and we have recursion limits. We would expect to sort lists in-place with list.sort or create new sorted lists with sorted - both of which take a key and reverse argument.
There are many answers to this already, but I think this approach is the most clean implementation:
def quicksort(arr):
""" Quicksort a list
:type arr: list
:param arr: List to sort
:returns: list -- Sorted list
"""
if not arr:
return []
pivots = [x for x in arr if x == arr[0]]
lesser = quicksort([x for x in arr if x < arr[0]])
greater = quicksort([x for x in arr if x > arr[0]])
return lesser + pivots + greater
You can of course skip storing everything in variables and return them straight away like this:
def quicksort(arr):
""" Quicksort a list
:type arr: list
:param arr: List to sort
:returns: list -- Sorted list
"""
if not arr:
return []
return quicksort([x for x in arr if x < arr[0]]) \
+ [x for x in arr if x == arr[0]] \
+ quicksort([x for x in arr if x > arr[0]])
functional approach:
def qsort(lst):
if len(lst) < 2:
return lst
pivot = lst[0]
left = list(filter(lambda x: x <= pivot, lst[1:]))
right = list(filter(lambda x: x > pivot, lst[1:]))
return qsort(left) + [pivot] + qsort(right)
Easy implementation from grokking algorithms
def quicksort(arr):
if len(arr) < 2:
return arr #base case
else:
pivot = arr[0]
less = [i for i in arr[1:] if i <= pivot]
more = [i for i in arr[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(more)
This is a version of the quicksort using Hoare partition scheme and with fewer swaps and local variables
def quicksort(array):
qsort(array, 0, len(array)-1)
def qsort(A, lo, hi):
if lo < hi:
p = partition(A, lo, hi)
qsort(A, lo, p)
qsort(A, p + 1, hi)
def partition(A, lo, hi):
pivot = A[lo]
i, j = lo-1, hi+1
while True:
i += 1
j -= 1
while(A[i] < pivot): i+= 1
while(A[j] > pivot ): j-= 1
if i >= j:
return j
A[i], A[j] = A[j], A[i]
test = [21, 4, 1, 3, 9, 20, 25, 6, 21, 14]
print quicksort(test)
functional programming aproach
smaller = lambda xs, y: filter(lambda x: x <= y, xs)
larger = lambda xs, y: filter(lambda x: x > y, xs)
qsort = lambda xs: qsort(smaller(xs[1:],xs[0])) + [xs[0]] + qsort(larger(xs[1:],xs[0])) if xs != [] else []
print qsort([3,1,4,2,5]) == [1,2,3,4,5]
Partition - Split an array by a pivot that smaller elements move to the left and greater elemets move to the right or vice versa. A pivot can be an random element from an array. To make this algorith we need to know what is begin and end index of an array and where is a pivot. Then set two auxiliary pointers L, R.
So we have an array user[...,begin,...,end,...]
The start position of L and R pointers
[...,begin,next,...,end,...]
R L
while L < end
1. If a user[pivot] > user[L] then move R by one and swap user[R] with user[L]
2. move L by one
After while swap user[R] with user[pivot]
Quick sort - Use the partition algorithm until every next part of the split by a pivot will have begin index greater or equals than end index.
def qsort(user, begin, end):
if begin >= end:
return
# partition
# pivot = begin
L = begin+1
R = begin
while L < end:
if user[begin] > user[L]:
R+=1
user[R], user[L] = user[L], user[R]
L+= 1
user[R], user[begin] = user[begin], user[R]
qsort(user, 0, R)
qsort(user, R+1, end)
tests = [
{'sample':[1],'answer':[1]},
{'sample':[3,9],'answer':[3,9]},
{'sample':[1,8,1],'answer':[1,1,8]},
{'sample':[7,5,5,1],'answer':[1,5,5,7]},
{'sample':[4,10,5,9,3],'answer':[3,4,5,9,10]},
{'sample':[6,6,3,8,7,7],'answer':[3,6,6,7,7,8]},
{'sample':[3,6,7,2,4,5,4],'answer':[2,3,4,4,5,6,7]},
{'sample':[1,5,6,1,9,0,7,4],'answer':[0,1,1,4,5,6,7,9]},
{'sample':[0,9,5,2,2,5,8,3,8],'answer':[0,2,2,3,5,5,8,8,9]},
{'sample':[2,5,3,3,2,0,9,0,0,7],'answer':[0,0,0,2,2,3,3,5,7,9]}
]
for test in tests:
sample = test['sample'][:]
answer = test['answer']
qsort(sample,0,len(sample))
print(sample == answer)
I think both answers here works ok for the list provided (which answer the original question), but would breaks if an array containing non unique values is passed. So for completeness, I would just point out the small error in each and explain how to fix them.
For example trying to sort the following array [12,4,5,6,7,3,1,15,1] (Note that 1 appears twice) with Brionius algorithm .. at some point will end up with the less array empty and the equal array with a pair of values (1,1) that can not be separated in the next iteration and the len() > 1...hence you'll end up with an infinite loop
You can fix it by either returning array if less is empty or better by not calling sort in your equal array, as in zangw answer
def sort(array=[12,4,5,6,7,3,1,15]):
less = []
equal = []
greater = []
if len(array) > 1:
pivot = array[0]
for x in array:
if x < pivot:
less.append(x)
elif x == pivot:
equal.append(x)
else: # if x > pivot
greater.append(x)
# Don't forget to return something!
return sort(less) + equal + sort(greater) # Just use the + operator to join lists
# Note that you want equal ^^^^^ not pivot
else: # You need to hande the part at the end of the recursion - when you only have one element in your array, just return the array.
return array
The fancier solution also breaks, but for a different cause, it is missing the return clause in the recursion line, which will cause at some point to return None and try to append it to a list ....
To fix it just add a return to that line
def qsort(arr):
if len(arr) <= 1:
return arr
else:
return qsort([x for x in arr[1:] if x<arr[0]]) + [arr[0]] + qsort([x for x in arr[1:] if x>=arr[0]])
Or if you prefer a one-liner that also illustrates the Python equivalent of C/C++ varags, lambda expressions, and if expressions:
qsort = lambda x=None, *xs: [] if x is None else qsort(*[a for a in xs if a<x]) + [x] + qsort(*[a for a in xs if a>=x])
A "true" in-place implementation [Algorithms 8.9, 8.11 from the Algorithm Design and Applications Book by Michael T. Goodrich and Roberto Tamassia]:
from random import randint
def partition (A, a, b):
p = randint(a,b)
# or mid point
# p = (a + b) / 2
piv = A[p]
# swap the pivot with the end of the array
A[p] = A[b]
A[b] = piv
i = a # left index (right movement ->)
j = b - 1 # right index (left movement <-)
while i <= j:
# move right if smaller/eq than/to piv
while A[i] <= piv and i <= j:
i += 1
# move left if greater/eq than/to piv
while A[j] >= piv and j >= i:
j -= 1
# indices stopped moving:
if i < j:
# swap
t = A[i]
A[i] = A[j]
A[j] = t
# place pivot back in the right place
# all values < pivot are to its left and
# all values > pivot are to its right
A[b] = A[i]
A[i] = piv
return i
def IpQuickSort (A, a, b):
while a < b:
p = partition(A, a, b) # p is pivot's location
#sort the smaller partition
if p - a < b - p:
IpQuickSort(A,a,p-1)
a = p + 1 # partition less than p is sorted
else:
IpQuickSort(A,p+1,b)
b = p - 1 # partition greater than p is sorted
def main():
A = [12,3,5,4,7,3,1,3]
print A
IpQuickSort(A,0,len(A)-1)
print A
if __name__ == "__main__": main()
def quick_sort(self, nums):
def helper(arr):
if len(arr) <= 1: return arr
#lwall is the index of the first element euqal to pivot
#rwall is the index of the first element greater than pivot
#so arr[lwall:rwall] is exactly the middle part equal to pivot after one round
lwall, rwall, pivot = 0, 0, 0
#choose rightmost as pivot
pivot = arr[-1]
for i, e in enumerate(arr):
if e < pivot:
#when element is less than pivot, shift the whole middle part to the right by 1
arr[i], arr[lwall] = arr[lwall], arr[i]
lwall += 1
arr[i], arr[rwall] = arr[rwall], arr[i]
rwall += 1
elif e == pivot:
#when element equals to pivot, middle part should increase by 1
arr[i], arr[rwall] = arr[rwall], arr[i]
rwall += 1
elif e > pivot: continue
return helper(arr[:lwall]) + arr[lwall:rwall] + helper(arr[rwall:])
return helper(nums)
I know many people have answered this question correctly and I enjoyed reading them. My answer is almost the same as zangw but I think the previous contributors did not do a good job of explaining visually how things actually work...so here is my attempt to help others that might visit this question/answers in the future about a simple solution for quicksort implementation.
How does it work ?
We basically select the first item as our pivot from our list and then we create two sub lists.
Our first sublist contains the items that are less than pivot
Our second sublist contains our items that are great than or equal to pivot
We then quick sort each of those and we combine them the first group + pivot + the second group to get the final result.
Here is an example along with visual to go with it ...
(pivot)9,11,2,0
average: n log of n
worse case: n^2
The code:
def quicksort(data):
if (len(data) < 2):
return data
else:
pivot = data[0] # pivot
#starting from element 1 to the end
rest = data[1:]
low = [each for each in rest if each < pivot]
high = [each for each in rest if each >= pivot]
return quicksort(low) + [pivot] + quicksort(high)
items=[9,11,2,0]
print(quicksort(items))
The algorithm contains two boundaries, one having elements less than the pivot (tracked by index "j") and the other having elements greater than the pivot (tracked by index "i").
In each iteration, a new element is processed by incrementing j.
Invariant:-
all elements between pivot and i are less than the pivot, and
all elements between i and j are greater than the pivot.
If the invariant is violated, ith and jth elements are swapped, and i
is incremented.
After all elements have been processed, and everything after the pivot
has been partitioned, the pivot element is swapped with the last element
smaller than it.
The pivot element will now be in its correct place in the sequence. The
elements before it will be less than it and the ones after it will be
greater than it, and they will be unsorted.
def quicksort(sequence, low, high):
if low < high:
pivot = partition(sequence, low, high)
quicksort(sequence, low, pivot - 1)
quicksort(sequence, pivot + 1, high)
def partition(sequence, low, high):
pivot = sequence[low]
i = low + 1
for j in range(low + 1, high + 1):
if sequence[j] < pivot:
sequence[j], sequence[i] = sequence[i], sequence[j]
i += 1
sequence[i-1], sequence[low] = sequence[low], sequence[i-1]
return i - 1
def main(sequence):
quicksort(sequence, 0, len(sequence) - 1)
return sequence
if __name__ == '__main__':
sequence = [-2, 0, 32, 1, 56, 99, -4]
print(main(sequence))
Selecting a pivot
A "good" pivot will result in two sub-sequences of roughly the same
size. Deterministically, a pivot element can either be selected in a
naive manner or by computing the median of the sequence.
A naive implementation of selecting a pivot will be the first or last
element. The worst-case runtime in this case will be when the input
sequence is already sorted or reverse sorted, as one of the subsequences
will be empty which will cause only one element to be removed per
recursive call.
A perfectly balanced split is achieved when the pivot is the median
element of the sequence. There are an equal number of elements greater
than it and less than it. This approach guarantees a better overall
running time, but is much more time-consuming.
A non-deterministic/random way of selecting the pivot would be to pick
an element uniformly at random. This is a simple and lightweight
approach that will minimize worst-case scenario and also lead to a
roughly balanced split. This will also provide a balance between the naive approach and the median approach of selecting the pivot.
def quicksort(array):
if len(array) < 2:
return array
else:
pivot = array[0]
less = [i for i in array[1:] if i <= pivot]
greater = [i for i in array[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)
def quick_sort(array):
return quick_sort([x for x in array[1:] if x < array[0]]) + [array[0]] \
+ quick_sort([x for x in array[1:] if x >= array[0]]) if array else []
def Partition(A,p,q):
i=p
x=A[i]
for j in range(p+1,q+1):
if A[j]<=x:
i=i+1
tmp=A[j]
A[j]=A[i]
A[i]=tmp
l=A[p]
A[p]=A[i]
A[i]=l
return i
def quickSort(A,p,q):
if p<q:
r=Partition(A,p,q)
quickSort(A,p,r-1)
quickSort(A,r+1,q)
return A
The algorithm has 4 simple steps:
Divide the array into 3 different parts: left, pivot and right, where pivot will have only one element. Let us choose this pivot element as the first element of array
Append elements to the respective part by comparing them to pivot element. (explanation in comments)
Recurse this algorithm till all elements in the array have been sorted
Finally, join left+pivot+right parts
Code for the algorithm in python:
def my_sort(A):
p=A[0] #determine pivot element.
left=[] #create left array
right=[] #create right array
for i in range(1,len(A)):
#if cur elem is less than pivot, add elem in left array
if A[i]< p:
left.append(A[i])
#the recurssion will occur only if the left array is atleast half the size of original array
if len(left)>1 and len(left)>=len(A)//2:
left=my_sort(left) #recursive call
elif A[i]>p:
right.append(A[i]) #if elem is greater than pivot, append it to right array
if len(right)>1 and len(right)>=len(A)//2: # recurssion will occur only if length of right array is atleast the size of original array
right=my_sort(right)
A=left+[p]+right #append all three part of the array into one and return it
return A
my_sort([12,4,5,6,7,3,1,15])
Carry on with this algorithm recursively with the left and right parts.
Another quicksort implementation:
# A = Array
# s = start index
# e = end index
# p = pivot index
# g = greater than pivot boundary index
def swap(A,i1,i2):
A[i1], A[i2] = A[i2], A[i1]
def partition(A,g,p):
# O(n) - just one for loop that visits each element once
for j in range(g,p):
if A[j] <= A[p]:
swap(A,j,g)
g += 1
swap(A,p,g)
return g
def _quicksort(A,s,e):
# Base case - we are sorting an array of size 1
if s >= e:
return
# Partition current array
p = partition(A,s,e)
_quicksort(A,s,p-1) # Left side of pivot
_quicksort(A,p+1,e) # Right side of pivot
# Wrapper function for the recursive one
def quicksort(A):
_quicksort(A,0,len(A)-1)
A = [3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,-1]
print(A)
quicksort(A)
print(A)
For Version Python 3.x: a functional-style using operator module, primarily to improve readability.
from operator import ge as greater, lt as lesser
def qsort(L):
if len(L) <= 1: return L
pivot = L[0]
sublist = lambda op: [*filter(lambda num: op(num, pivot), L[1:])]
return qsort(sublist(lesser))+ [pivot] + qsort(sublist(greater))
and is tested as
print (qsort([3,1,4,2,5]) == [1,2,3,4,5])
Here's an easy implementation:-
def quicksort(array):
if len(array) < 2:
return array
else:
pivot= array[0]
less = [i for i in array[1:] if i <= pivot]
greater = [i for i in array[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)
print(quicksort([10, 5, 2, 3]))
My answer is very similar to the great one from #alisianoi . However, I believe there is a slight inefficiency in his code (see my comment), which I removed. Moreover, I added more explanation and was a bit more specific about the problem of duplicate (pivot) values.
def quicksort(nums, begin=0, end=None):
# Only at the beginning end=None. In this case set to len(nums)-1
if end is None: end = len(nums) - 1
# If list part is invalid or has only 1 element, do nothing
if begin>=end: return
# Pick random pivot
pivot = nums[random.randint(begin, end)]
# Initialize left and right pointers
left, right = begin, end
while left < right:
# Find first "wrong" value from left hand side, i.e. first value >= pivot
# Find first "wrong" value from right hand side, i.e. first value <= pivot
# Note: In the LAST while loop, both left and right will point to pivot!
while nums[left] < pivot: left += 1
while nums[right] > pivot: right -= 1
# Swap the "wrong" values
if left != right:
nums[left], nums[right] = nums[right], nums[left]
# Problem: loop can get stuck if pivot value exists more than once. Simply solve with...
if nums[left] == nums[right]:
assert nums[left]==pivot
left += 1
# Now, left and right both point to a pivot value.
# All values to its left are smaller (or equal in case of duplicate pivot values)
# All values to its right are larger.
assert left == right and nums[left] == pivot
quicksort(nums, begin, left - 1)
quicksort(nums, left + 1, end)
return
Without recursion:
def quicksort(nums, ranges=None):
if ranges is None:
ranges = [[0, len(nums) - 1]]
while ranges != []:
[start, end] = ranges[0]
ranges = ranges[1:]
if start >= end:
continue
pivot = nums[randint(start, end)]
left = start
right = end
while left < right:
while nums[left] < pivot:
left += 1
while nums[right] > pivot:
right -= 1
if left != right:
nums[left], nums[right] = nums[right], nums[left]
if nums[left] == nums[right]:
left += 1
ranges = [[start, left - 1], [left + 1, end]] + ranges
First we declare the first value in the array to be the
pivot_value and we also set the left and right marks
We create the first while loop, this while loop is there to tell
the partition process to run again if it doesn't satisfy the
necessary condition
then we apply the partition process
after both partition processes have ran, we check to see if it
satisfies the proper condition. If it does, we mark it as done,
if not we switch the left and right values and apply it again
Once its done switch the left and right values and return the
split_point
I am attaching the code below! This quicksort is a great learning tool because of the Location of the pivot value. Since it is in a constant place, you can walk through it multiple times and really get a hang of how it all works. In practice it is best to randomize the pivot to avoid O(N^2) runtime.
def quicksort10(alist):
quicksort_helper10(alist, 0, len(alist)-1)
def quicksort_helper10(alist, first, last):
""" """
if first < last:
split_point = partition10(alist, first, last)
quicksort_helper10(alist, first, split_point - 1)
quicksort_helper10(alist, split_point + 1, last)
def partition10(alist, first, last):
done = False
pivot_value = alist[first]
leftmark = first + 1
rightmark = last
while not done:
while leftmark <= rightmark and alist[leftmark] <= pivot_value:
leftmark = leftmark + 1
while leftmark <= rightmark and alist[rightmark] >= pivot_value:
rightmark = rightmark - 1
if leftmark > rightmark:
done = True
else:
temp = alist[leftmark]
alist[leftmark] = alist[rightmark]
alist[rightmark] = temp
temp = alist[first]
alist[first] = alist[rightmark]
alist[rightmark] = temp
return rightmark
def quick_sort(l):
if len(l) == 0:
return l
pivot = l[0]
pivots = [x for x in l if x == pivot]
smaller = quick_sort([x for x in l if x < pivot])
larger = quick_sort([x for x in l if x > pivot])
return smaller + pivots + larger
Full example with printed variables at partition step:
def partition(data, p, right):
print("\n==> Enter partition: p={}, right={}".format(p, right))
pivot = data[right]
print("pivot = data[{}] = {}".format(right, pivot))
i = p - 1 # this is a dangerous line
for j in range(p, right):
print("j: {}".format(j))
if data[j] <= pivot:
i = i + 1
print("new i: {}".format(i))
print("swap: {} <-> {}".format(data[i], data[j]))
data[i], data[j] = data[j], data[i]
print("swap2: {} <-> {}".format(data[i + 1], data[right]))
data[i + 1], data[right] = data[right], data[i + 1]
return i + 1
def quick_sort(data, left, right):
if left < right:
pivot = partition(data, left, right)
quick_sort(data, left, pivot - 1)
quick_sort(data, pivot + 1, right)
data = [2, 8, 7, 1, 3, 5, 6, 4]
print("Input array: {}".format(data))
quick_sort(data, 0, len(data) - 1)
print("Output array: {}".format(data))
def is_sorted(arr): #check if array is sorted
for i in range(len(arr) - 2):
if arr[i] > arr[i + 1]:
return False
return True
def qsort_in_place(arr, left, right): #arr - given array, #left - first element index, #right - last element index
if right - left < 1: #if we have empty or one element array - nothing to do
return
else:
left_point = left #set left pointer that points on element that is candidate to swap with element under right pointer or pivot element
right_point = right - 1 #set right pointer that is candidate to swap with element under left pointer
while left_point <= right_point: #while we have not checked all elements in the given array
swap_left = arr[left_point] >= arr[right] #True if we have to move that element after pivot
swap_right = arr[right_point] < arr[right] #True if we have to move that element before pivot
if swap_left and swap_right: #if both True we can swap elements under left and right pointers
arr[right_point], arr[left_point] = arr[left_point], arr[right_point]
left_point += 1
right_point -= 1
else: #if only one True we don`t have place for to swap it
if not swap_left: #if we dont need to swap it we move to next element
left_point += 1
if not swap_right: #if we dont need to swap it we move to prev element
right_point -= 1
arr[left_point], arr[right] = arr[right], arr[left_point] #swap left element with pivot
qsort_in_place(arr, left, left_point - 1) #execute qsort for left part of array (elements less than pivot)
qsort_in_place(arr, left_point + 1, right) #execute qsort for right part of array (elements most than pivot)
def main():
import random
arr = random.sample(range(1, 4000), 10) #generate random array
print(arr)
print(is_sorted(arr))
qsort_in_place(arr, 0, len(arr) - 1)
print(arr)
print(is_sorted(arr))
if __name__ == "__main__":
main()
This algorithm doesn't use recursive functions.
Let N be any list of numbers with len(N) > 0. Set K = [N] and execute the following program.
Note: This is a stable sorting algorithm.
def BinaryRip2Singletons(K, S):
K_L = []
K_P = [ [K[0][0]] ]
K_R = []
for i in range(1, len(K[0])):
if K[0][i] < K[0][0]:
K_L.append(K[0][i])
elif K[0][i] > K[0][0]:
K_R.append(K[0][i])
else:
K_P.append( [K[0][i]] )
K_new = [K_L]*bool(len(K_L)) + K_P + [K_R]*bool(len(K_R)) + K[1:]
while len(K_new) > 0:
if len(K_new[0]) == 1:
S.append(K_new[0][0])
K_new = K_new[1:]
else:
break
return K_new, S
N = [16, 19, 11, 15, 16, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1]
K = [ N ]
S = []
print('K =', K, 'S =', S)
while len(K) > 0:
K, S = BinaryRip2Singletons(K, S)
print('K =', K, 'S =', S)
PROGRAM OUTPUT:
K = [[16, 19, 11, 15, 16, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1]] S = []
K = [[11, 15, 10, 12, 14, 4, 10, 5, 2, 3, 4, 7, 1], [16], [16], [19]] S = []
K = [[10, 4, 10, 5, 2, 3, 4, 7, 1], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[4, 5, 2, 3, 4, 7, 1], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[2, 3, 1], [4], [4], [5, 7], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = []
K = [[5, 7], [10], [10], [11], [15, 12, 14], [16], [16], [19]] S = [1, 2, 3, 4, 4]
K = [[15, 12, 14], [16], [16], [19]] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11]
K = [[12, 14], [15], [16], [16], [19]] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11]
K = [] S = [1, 2, 3, 4, 4, 5, 7, 10, 10, 11, 12, 14, 15, 16, 16, 19]

Categories