QuickSort in Python. Increment in array trouble - python

I am trying to implement quicksort in python. Problem is how to increment/decrement value of i/j in array a. I know that I should write i=i+1 and there are no such thing like i++ in python, but I don't understand in which way I should do this.
I am newbie, here is my code.
def quicksort(a,lo,hi):
if(hi<=lo):
return
i = lo - 1
j = hi
v = a[hi]
while True:
while(a[++i] < v):
pass
while(v < a[--j]):
if(j==lo):
break
if(i>=j):
break
t = a[i]
a[i] = a[j]
a[j] = t
t = a[i]
a[i] = a[hi]
a[hi] = t
quicksort(a, lo, i - 1)
quicksort(a, i + 1, hi)

in python, you cannot assign and get the value, that's an intentional limitation to avoid issues with typos, find the proper sequence point...
You have no choice than to "emulate" the C-ported code:
while(a[++i] < v):
pass
while(v < a[--j]):
if(j==lo):
break
(note that both constructs generate an infinite loop because:
++i == i
and
--j == j
(applying unary plus any number of times or unary minus an even number of times gives the same number, see Why Don't Two Plus Operators Throw an Error (e.g., 1 + + 2))
So change to:
i += 1
while(a[i] < v):
i += 1
j -= 1
while(v < a[j]):
if(j==lo):
break
j -= 1

The following construct does not work the same way in Python as it does in C++:
while(a[++i] < v):
as well as this one:
while(v < a[--j]):
The way you change the code is the following:
def quicksort(a,lo,hi):
if(hi<=lo):
return
i = lo - 1
j = hi
v = a[hi]
while True:
i += 1
while(a[i] < v):
i += 1
pass
j -= 1
while(v < a[j]):
j -= 1
if(j==lo):
break
if(i>=j):
break
t = a[i]
a[i] = a[j]
a[j] = t
t = a[i]
a[i] = a[hi]
a[hi] = t
quicksort(a, lo, i - 1)
quicksort(a, i + 1, hi)

Related

Implementing quick sort in Python - list index/infinite loop bugs

I'm trying to implement quicksort in Python. I used the pseudocode found on Wikipedia here as a basis. That had do...while loops in it, so I used the
while True:
# code
if condition:
break
construct to simulate it.
I have tried a lot of things like incrementing/decrementing differently or adding checks for indexes out of bounds (which the pseudocode does not have because it is not supposed to be necessary using the Hoare algorithm, at least I think so...). I always get either an IndexError or an infinite loop.
Could someone show me where I have gone wrong in implementing the algo please?
class QuickSort:
def sort(self, data):
if data is None:
raise TypeError()
n = len(data)
if n < 2:
return data
self._sort(data, 0, len(data) - 1)
def _sort(self, A, lo, hi):
if lo >= 0 and hi >= 0:
if lo < hi:
p = self._partition(A, lo, hi)
self._sort(A, lo, p)
self._sort(A, p + 1, hi)
def _partition(self, A, lo, hi):
pivot = A[(hi + lo) // 2]
i = lo - 1
j = hi + 1
while True:
while True:
i += 1
if A[i] < pivot:
break
while True:
j -= 1
if A[j] > pivot:
break
if i >= j:
return j
A[i], A[j] = A[j], A[i]
EDIT: The reason was simple, when using the python version of do...while you have to invert the condition!
It's need to change < and > to >= and <= in the loops:
class QuickSort:
def sort(self, data):
if data is None:
raise TypeError()
n = len(data)
if n < 2:
return data
self._sort(data, 0, len(data) - 1)
def _sort(self, A, lo, hi):
if lo >= 0 and hi >= 0:
if lo < hi:
p = self._partition(A, lo, hi)
self._sort(A, lo, p)
self._sort(A, p + 1, hi)
def _partition(self, A, lo, hi):
pivot = A[(hi + lo) // 2]
i = lo - 1
j = hi + 1
while True:
while True:
i += 1
if A[i] >= pivot: # >= instead of <
break
while True:
j -= 1
if A[j] <= pivot: # <= instead of >
break
if i >= j:
return j
A[i], A[j] = A[j], A[i]
lst = [9, 8, 7, 6, 5, 4, 1, 2]
QuickSort().sort(lst)
print(lst) # [1, 2, 4, 5, 6, 7, 8, 9]

Why this Merge Sort does not work properly?

I need to implement Merge Sort using Python 3. I coded it. But It doesn't give proper output. Can anybody check it please?
Here my code is,
def mergeSort(A, p, r):
if p < r:
q = (p + r) // 2
mergeSort(A, p, q)
mergeSort(A, q+1, r)
Merge(A, p, q, r)
def Merge(A, p, q, r):
i = 1
j = q+1
k = 0
TEMP = [0] * (r+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
if (j > r) :
for t in range(0, q-1):
A[r-t] = A[q-t]
for t in range(0, k-1):
A[p+t] = TEMP[t+1]
A = [15, 16, 13, 10, 19, 18]
mergeSort(A, 0, len(A)-1)
print(A)
Thank you
The way you perform merge looks weird (to me), but I will correct on what you have so far.
1- Initialization value of i is wrong, it should be:
i = p
because i is the first element you will look in array A.
2- Initialization value of size of TEMP array is wrong, it should be:
(r - p + 1)
3- There seems a mistake in filling in TEMP array and/or replacing A array elements, here is the fixed code. I wrote a comment about the part after first while loop to indicate what needs to be done at that point.
def mergeSort(A,p,r):
if p < r:
q = (p+r)//2
mergeSort(A,p,q)
mergeSort(A,q+1,r)
Merge(A,p,q,r)
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r - p + 1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
"""
There are currently 2 cases
1- i > q, means we exhausted left but there are elements in the right
2- j > r, means we exhausted right but there are elements in the left
"""
if (j > r):
# copy elements at the left side to temp
while (i <= q):
TEMP[k] = A[i]
i += 1
k += 1
else:
# copy elements at the right side to temp
while (j <= r):
TEMP[k] = A[j]
j += 1
k += 1
# replace elements in A with elements in TEMP
for t in range(k):
A[p+t] = TEMP[t]
A = [15,16,13,10,19,18]
mergeSort(A,0,len(A)-1)
print(A)
The error lies in the Merge() function.
Initialisation of i=p and not i=1
After the while loop terminates, there's a chance that either i<q or j<r. We need to accommodate those cases as well.
Size of array TEMP was incorrect.
Corrected Merge Function:
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r-p+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
while i<=q:
TEMP[k] = A[i]
k+=1
i += 1
while j<=r:
TEMP[k] = A[j]
k+=1
j += 1
for t in range (p,r+1):
A[t] = TEMP[t-p]
Note: Please try using more meaningful variable names.

Why combining insert sort and quick sort get worse result?

I tried to set a cutoff to combine quick sort and insert sort which using insert sort when n(number of data to sort) is lower than cutoff. However, I found the method didn't work and even worse than before. Why and how to imporve it?
To sort 10e4 random int number, the quick sort with a cutoff(50) takes 0.6s and the method without a cutoff takes only 0.02s.
The quick sort with a cutoff(50):
def quick_sort(line, l, r):
if r - l > 50:
pivot = find_median(line, l, r)
i, j = l+1, r-2
while True:
while line[i] < pivot:
i += 1
while line[j] > pivot:
j -= 1
if i < j:
line[i], line[j] = line[j], line[i]
i += 1
j -= 1
else:
break
line[i], line[r-1] = line[r-1], line[i]
quick_sort(line, l, i-1)
quick_sort(line, i+1, r)
else:
insert_sort_index(line, l, r)
def find_median(line, l, r):
center = (l + r) / 2
if line[l] > line[r]:
line[l], line[r] = line[r], line[l]
if line[l] > line[center]:
line[l], line[center] = line[center], line[l]
if line[center] > line[r]:
line[center], line[r] = line[r], line[center]
line[center], line[r-1] = line[r-1], line[center]
return line[r-1]
def insert_sort_index(line, l, r):
if l < r:
for idi in range(l+1, r+1):
data = line[idi]
for idj in range(idi+1)[::-1]:
if idj >= l+1 and line[idj-1] > data:
line[idj] = line[idj-1]
else:
break
line[idj] = data
The method without a cutoff:
def quick_sort(line, l, r):
if r - l > 1:
pivot = find_median(line, l, r)
i, j = l+1, r-2
while True:
while line[i] < pivot:
i += 1
while line[j] > pivot:
j -= 1
if i < j:
line[i], line[j] = line[j], line[i]
i += 1
j -= 1
else:
break
line[i], line[r-1] = line[r-1], line[i]
quick_sort(line, l, i-1)
quick_sort(line, i+1, r)
else:
if r == l + 1:
if line[l] > line[r]:
line[l], line[r] = line[r], line[l]
python3 implements range and other functions as iterators/generators, so it would probably be much more efficient in this application, but the python2 range function creates a complete list in memory. You use range multiple times insert_sort_index (and create another list with the [::-1] splice. You could have passed step as an argument to range for that one).
My python2 implementation seems to be optimizing for loops with range(0,x) which made it harder to demonstrate the problem, but not when (l, r) is within a larger list, as is the case with this quicksort cutoff.
I measured aprox. double speed of the insert sort, when operating on a range of a larger list, by using a while loop for idi, idj instead of range().

Kth Largest Element

I am trying to solve the Kth Largest Element problem.I don't why following bugs appeared. "E: 6,16: Undefined variable 'quickSelect' (undefined-variable)
E: 27,19: Undefined variable 'quickSelect' (undefined-variable)
E: 30,19: Undefined variable 'quickSelect' (undefined-variable)" Here are my codes.
def kthLargestElement(self, k, A):
return quickSelect(A, 0, len(A) - 1, k)
def quickSelect(self, nums, start, end, k):
if start == end:
return nums[start]
i = start
j = end
pivot = (nums[start] + nums[end]) // 2
while i <= j:
while i <= j and nums[i] < pivot:
i += 1
while i <= j and nums[j] > pivot:
j -= 1
if i <=j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
if start + k - 1 <= j:
return quickSelect(nums, start, j, k)
if start + k - 1 >= i:
return quickSelect(nums, i, end, k - (i - start))
return nums[j + 1]
Python cannot find quickSelect because it's looking for it in the global namespace rather than looking inside your class. To fix this you can invoke your function using self.quickSelect(...) instead of quickSelect(...).

Hoare partitioning falls into infinite loop

I am trying to write a Hoare partitioning function that takes an array as input, and partitions it with the first element as pivot (I know it's not a good idea, I should be using randomized pivots, like the median-of-medians approach). Problem is that this function falls into infinite loop when the first element is the highest, as with the array [14,6,8,1,4,9,2,1,7,10,5]. I can see the error, after the first iteration of the outer while, both i and j equal 10, and hence the loop continues forever. Which portion should I mend to get the desired effect? Here's the code:
def hoare(arr):
pivot = arr[0]
i,j = 1,len(arr)-1
while i <= j:
while i < j and arr[i] < pivot:
i += 1
while j >= i and arr[j] >= pivot:
j -= 1
if i < j:
arr[i],arr[j] = arr[j],arr[i]
if j != 0:
arr[0],arr[j] = arr[j],arr[0]
return j
I believe the problem is that you've converted a do-while (or repeat-until, in Hoare's terms) loop into a while loop, so it never does the first j -= 1.
The simplest transformation in Python should be to change the two inner while loops like this:
while True:
i += 1
if not (i < j and arr[i] < pivot): break
while True:
j -= 1
if not (j >= i and arr[j] >= pivot): break
(I'm assuming here that the if i < j: is supposed to be outside the second while loop, and all of the other initial indentation is correct.)
I haven't reasoned this through completely, or run a variety of tests, but there's probably more than just this one error in your translation. You may need to also convert the outer loop into a do-while (Hoare actually makes it an explicit while TRUE with a check at the end), but I'm not sure. Anyway, for your sample input, the modified version returns 9, and arr is [10, 6, 8, 1, 4, 9, 2, 1, 7, 14, 5], which is incorrect, but it solves your infinite loop problem.
The next problem is an off-by-one error. If you're going to do the += 1 and -= 1 first in the inner loops, you have to start at -1, len(arr) rather than 0, len(arr)-1 (or, as you did, 1, len(arr)-1).
There may still be other problems. But I don't want to dig through your code finding all possible mistakes and explaining them. If you need that, tell us what our source was, and explain each transformation you made from that source, and it'll be much easier to explain where you went wrong. If not, it's much simpler to just translate Hoare's algorithm to Python directly, and then hopefully you can figure it out.
Here's a copy of the Hoare pseudocode that I found online (just replacing all tabs with two spaces):
Hoare-Partition (A, p, r)
x ← A[p]
i ← p − 1
j ← r + 1
while TRUE
repeat j ← j − 1
until A[j] ≤ x
repeat i ← i + 1
until A[i] ≥ x
if i < j
exchange A[i] ↔ A[j]
else
return j
Here's a trivial translation into Python; the only changes are minor syntax (including the way "exchange" is spelled) and turning each repeat/until into a while True/break.
def hoare(a, p, r):
x = a[p]
i, j = p-1, r+1
while True:
while True:
j -= 1
if a[j] <= x:
break
while True:
i += 1
if a[i] >= x:
break
if i < j:
a[i], a[j] = a[j], a[i]
else:
return j
For a function with the same signature as yours:
def hoare0(arr):
return hoare(arr, 0, len(arr)-1)
There is an error in this line:
while i < j and arr[i] < pivot:
It should be:
while i <= j and arr[i] < pivot:
The whole code for partition looks like:
def partition(a, l, r):
pivot = a[r]
i = l - 1
j = r
while i <= j:
if i <= j and a[i] < pivot:
i += 1
if i <= j and a[j] >= pivot:
j -= 1
if i < j:
a[i], a[j] = a[j], a[i]
a[l], a[j] = a[j], a[l]
return j
Why there was an infinite loop?
The pivot chosen here is 14.
So, after this code is executed:
while i < j and arr[i] < pivot:
i += 1
i is 10 and j is 10.
Now, when this block is executed:
while i <= j and arr[j] >= pivot:
j -= 1
As a[10] < 14, nothing happens. Since, i equals j, no swap happens. Now, since the outermost loop has condition i <= j, the loop keeps repeating.
What happens with correction?
So, after this code is executed:
while i <= j and arr[i] < pivot:
i += 1
i is 11 (because the condition is still true when i equals j) and j is 10.
Now, when this block is executed:
while i <= j and arr[j] >= pivot:
j -= 1
As a[10] < 14, nothing happens.
Now, i is 11 and j is 10, so no swap happens. But, the outermost loop is broken and a[j] swaps with pivot.
Your array becomes:
[5, 6, 8, 1, 4, 9, 2, 1, 7, 10, 14]
You can play here. It contains code with debug prints for both right and wrong partition schemes.
This Also Works :
key = arr[0]
i = 0
j = n-1
while i >= j:
while arr[i] < key:
i += 1
while arr[j] > key:
j -= 1
arr[j], arr[0] = arr[0], arr[j]
Partition algorithm has many variants, (short/long step), but we should be very careful with invariants,preconditions and non-structured programming statements (break, return ) concerning this classic algorithm.
Otherwise, we may fall in big troubles. Even when this can be against 'pythonic' philosophy of coding.
The next annotated solution (for didactic purposes) yields (10, [5, 6, 8, 1, 4, 9, 2, 1, 7, 10, 14]) for the original list [14,6,8,1,4,9,2,1,7,10,5], as expected. Comments can be stripped off,
def hoare(arr):
# P: len(arr) > 0
assert len(arr)>0
i,j = 1,len(arr)
# INV : \forall n : 1<=n<i :arr[n]<arr[0]
# \forall n : j<=n<len(arr) :arr[n]>=arr[0]
# Quote(j-i)>=0
while i < j:
aa,bb=i,j
while aa < j and arr[aa] < arr[0]:
aa += 1
while bb > aa and arr[bb-1] >= arr[0]:
bb -= 1
#let
# aa = min n : i<=n<=j: n<j -> arr[n]>=arr[0]
# bb = max n : aa<=n<=j: n>aa -> arr[n-1]<arr[0]
#in
if (bb-aa)==(j-i):
#restore
arr[i],arr[j-1] = arr[j-1],arr[i]
#step
i, j = i+1 , j -1
else:
#restore
pass
#step
i,j = aa,bb
arr[0],arr[j-1] = arr[j-1],arr[0]
return j-1,arr
# Q : \forall n : 0<=n<j-1 :arr[n]<arr[j]
# \forall n : j-1<=n<len(arr) :arr[n]>=arr[j]
EDIT:
I'm not against goto, breaks, and continues... Donald Knuth stresses that "structured programming" is an idea rather than a language...
Once you understand the laws, you can break them... is this more pythonic? Invariant keeps and quote decreases every loop.
def hoare_non_str(arr):
assert len(arr)>0
i,j = 1,len(arr)
while i < j:
while i < j and arr[i] < arr[0]:
i += 1
if i==j:
break
while j > i and arr[j-1] >= arr[0]:
j -= 1
if i==j:
break
#It is safe to swap here.
arr[i],arr[j-1] = arr[j-1],arr[i]
i = i + 1
# swap pivote
arr[0],arr[j-1] = arr[j-1],arr[0]
return j-1,arr

Categories

Resources