Merge Sort Problems - Python - python

What's wrong with my code? It prints only a part of the vect values. Seems that the while loop breaks at some point. I don't understand why.
def print_list(vect):
for i in range(0, len(vect)):
print(vect[i])
def merge_sort(vect):
left = []
right = []
result = []
for i in range(0, int(len(vect)/2)):
left.append(vect[i])
for i in range(int(len(vect)/2), len(vect)):
right.append(vect[i])
left.sort()
right.sort()
i = 0
j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
print(len(result))
return result
vect = [3, 1, 5, 7, 10, 2, 0]
vect = merge_sort(vect)

Well, your mistake is that after your while loop
while i < len(left) and j < len(right):
...
it may be (and most probably would) that either i < len(left) or j < len(right), so you need to append appropriate part's suffix to the answer. It's easy to do with
result += left[i:]
result += right[j:]
Explanation:
Imagine the merge procedure: you have i and j at 0 at start, and at every step you move one of them forward. When you stop ? When one of them reaches the end. Let's say i has reached the end. Hereby you added the whole left part to the result, but there are still some elements in right between j and len(right), so you have to add them to the answer too.
Offtop:
You are implementing merge sort, so please have
left = merge_sort( left )
right = merge_sort( right )
instead of
left.sort()
right.sort()
Attention: you have to add the following check to your code at the beginning of merge function to avoid infinite recursion:
if len( vect ) == 1:
return vect
Also in your print_list function you can just use
print vect
or at least
for x in vect
print x

The while loop exits as soon as either left or right is exhausted. This leaves all the elements left in the unexhausted list unmerged.
Change your code to this:
def print_list(vect):
for i in range(0, len(vect)):
print(vect[i])
def merge_sort(vect):
left = []
right = []
result = []
for i in range(0, int(len(vect)/2)):
left.append(vect[i])
for i in range(int(len(vect)/2), len(vect)):
right.append(vect[i])
left.sort();
right.sort();
i = 0
j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
if i < len(left):
result.extend(left[i:])
else:
result.extend(right[j:])
print(len(result))
return result
vect = [3, 1, 5, 7, 10, 2, 0]
vect = merge_sort(vect)
print vect
If you're using the sort method on left and right, I'm a little confused as to why you're not just using it on the list as a whole. But I suppose you have your reasons. :)
Edit: I put the entire block of code there so you can see it. When I run this, it prints [0, 1, 2, 3, 5, 7, 10], which is correct.

Related

Out of range. How to add the remainder of list to accumulator?

I get [-7,-4,-2], but I want to add the remaining numbers to my accumulator but I keep going out of range in my second if statement. How would I continue to add the remaining list?
input: interleaved( [-7, -2, -1], [-4, 0, 4, 8])
def interleaved(seq1,seq2):
i = 0
j = 0
res = []
while i <len(seq1) and j <len(seq2):
if seq1[i] < seq2[j]:
res.append(seq1[i])
i+=1
if seq2[j] <= seq1[i]:
res.append(seq2[j])
j+=1
return res
Added an if statement to check whether we "finished" exploring seq1 or not (same if "check" can be applied on seq2 in case it had more negative values than seq1)
def interleaved(seq1, seq2):
i = 0
j = 0
res = []
while i < len(seq1) and j < len(seq2):
if seq1[i] < seq2[j]:
res.append(seq1[i])
i += 1
if i == len(seq1): # If we explored all of seq1 (reached the end)
for num in seq2[j:]: # Explore the rest of seq2
res.append(num) # Append the rest
break # Break the while loop and go to "return"
if seq2[j] <= seq1[i]:
res.append(seq2[j])
j += 1
return res
print(interleaved())

Sort a list of tuples, using nonlibrary sorting function (Python)

As the problems stated above, I need to use a nonlibrary sorting function, specifically a mergesort function I implemented in a previous assignment. I have my assignment working really well using activity_arr.sort(key=operator.itemgetter(2)). However, the requirements are asking that I use this function:
def merge_sort(array):
''' Sorts an array using merge sort algorithm.'''
if len(array) > 1:
# '//' is for "floor division", used in case array is filled with floats
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
# Recursion to sort left and right half of array
merge_sort(left)
merge_sort(right)
i = 0
j = 0
k = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
# Fill the rest of the array with remaining numbers in each array
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1
I assume, I will have to modify it so that I can sort my list of tuples. An example of my list to sort is example_list = [(1, 3, 4), (7, 2, 5), (1, 2, 1)]. I want to sort in ascending order by the third element of the tuple. SO it should result in the following: sorted_list = [(1, 2, 1), (1, 3, 4), (7, 2, 5)].
I am still just learning python and still have much to learn in CS in general. I have no idea how to make this change despite lots of research I mostly find people saying to use .sort(). If I left out anything important please let me know! Any help or information for me to look up will be very much appreciated. Thank you!
You can pass an index based on which you want to sort array of tuples.
For example, if you want to sort array based on index 2(indexing starts from 0), you can use it as: merge_sort(example_list,2). Idea is simple, you have to compare element based on index given, like I have done it here:
if left[i][index] < right[j][index]:
def merge_sort(array,index): #edit here
''' Sorts an array using merge sort algorithm.'''
if len(array) > 1:
# '//' is for "floor division", used in case array is filled with floats
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
# Recursion to sort left and right half of array
merge_sort(left,index) #edit here
merge_sort(right,index) #edit here
i = 0
j = 0
k = 0
while i < len(left) and j < len(right):
if left[i][index] < right[j][index]: #edit here
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
# Fill the rest of the array with remaining numbers in each array
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1

Merge Sort Problems In Python

So I'm trying to teach myself how to write a merge sort but for whatever reason I can't seem to make it work.
def merge(left, right):
result = []
i ,j = 0, 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
def mergesort(numlist):
if len(numlist) < 2:
return numlist
middle = int(len(numlist)/2)
left = mergesort(numlist[:middle])
right = mergesort(numlist[middle:])
return merge(left, right)
Every list I feed into the sort then attempt to print just comes up the exact same with no changes
Answering so this can be closed though it was fixed in comments with the help of #Jean-François Fabre.
Your current code works fine but it does not sort the list in-place. In the case of sorting a list called list_of_numbers, you need to assign the result back to list_of_numbers:
list_of_numbers = mergesort(list_of_numbers)

Merge sort in python doesnt update the array after sorting

ab = [5, 89, 23, 9]
def mergsort(array):
mid = len(array) / 2
if mid > 0:
print (array)
mergsort(array[:mid])
mergsort(array[mid:])
print(array)
merg(array)
return array
def merg(array):
print (array)
mid = len(array)//2
left = array[:mid]
right = array[mid:]
i = j = k = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i+=1
else:
array[k] = right[j]
j+=1
k+=1
while i < len(left):
array[k]=left[i]
i+=1
k+=1
while j < len(right):
array[k] = right[j]
j+=1
k+=1
print (array)
mergsort(ab)
print (ab)
The merge function sort the array given and the array is updated. But in the next recursion the array going into the merg function is not the mutated array.
In the example, first sorting happens and [5,89] and [23,9] are sorted as [5,89] and [9,23] but the merged input in the next recursion is [5,89,23,9] instead of [5,89,9,23].
I am unable to find any reason as mutating the array should affect the parent array.
One problem is with the recursive calls:
mergsort(array[:mid])
mergsort(array[mid:])
the results of these calls are not recorded - so when we continue, it's done with the same original unsorted array.
The fix:
def mergsort(array):
if len(array) == 1:
return array
mid=len(array)/2
left = mergsort(array[:mid]) # save into a parameter
right = mergsort(array[mid:]) # save into a parameter
return merge(left, right) # use the previous two
The second issue is actually the same kind of issue only with:
def merg(array)
the merge operation is done between two arrays, which means that two distinct arrays should be sent to this function, otherwise there is no recollection of mid from the function mergesort() and declaring mid to be length/2 treats the whole array and not the specific two parts that we intend to merge. The idea behind the logic inside this function is correct but should be done, as I mentioned, on two "distinct" arrays.
Last problem is the in-place swap which is incorrectly done, for example in:
array[k]=right[j]
by doing do, we erase the element at array[k]!
The fix:
def merge(left, right):
if len(left+right) < 2:
return left+right
res = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
res.append(left[i])
i += 1
elif j < len(right):
res.append(right[j])
j += 1
while i < len(left):
res.append(left[i])
i += 1
while j < len(right):
res.append(right[j])
j += 1
return res
After applying both fixes and running:
print mergsort(ab)
The output is:
[5, 9, 23, 89]
as required.

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