I'm learning the following codes.
def merge(left, right, compare):
"""Assumes left and right are sorted lists and compare defines an ordering on the elements.
Returns a new sorted (by compare) list containing the same elements as (left + right) would contain."""
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if compare(left[i], right[j]):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while (i < len(left)):
result.append(left[i])
i += 1
while (j < len(right)):
result.append(right[j])
j += 1
return result
def mergeSort(L, compare = lambda x, y: x < y):
"""Assumes L is a list, compare defines an ordering on elements of L
Returns a new sorted list with the same elements as L"""
if len(L) < 2:
return L[:]
else:
middle = len(L)//2
left = mergeSort(L[:middle], compare) # line A
right = mergeSort(L[middle:], compare) # line B
return merge(left, right, compare)
The book asked me to give an example L = [2,1,4,5,3]
Then type mergeSort(L) in the console turns out to be [1,2,3,4,5]
My questions lie in the middle of mergeSort.
Why "left" and "right" should return a sorted list (from the definition of function merge) after the above steps. Can anyone explain in details for what have happened when # line A & # line B are executed?
I substitute:
"left = mergeSort(L[:middle], compare)" into "left = mergeSort(L[:2], compare)".
However, whenever I run mergeSort(L), Python always tells me 'Kernel died, restarting' after this substitution. Why the output is so different in this case when middle == 2? What's wrong when I substitute 'middle' into an integer?
NOTE: all of the above codes are from Figure 10.5 of the book Introduction to Computation and Programming Using Python
Related
I'm having trouble writing a function in python that will take a list, split it into 2 equal sides and then recursively add each element in each half. In the end returning the sum of both halves.
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Any tips are appreciated, thanks!
You're overcomplicating the else block. You don't need to call sumLists on leftHalf[1:] and rightHalf[1:] and manually add the first respective elements; it suffices to call sumLists on the complete lists.
This slicing is what's causing your RuntimeError. A leftHalf with length one will have a leftHalf[1:] of length zero. But your function recurses forever for lengths of length zero because you didn't write an if case for that scenario.
You could rewrite your else so that it doesn't require slicing:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
... Or you could add a special case for empty lists:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
elif len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Or both:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
I think aList[half:] is the right side, and aList[:half] is the left side, is it right?
the follow code will sum the list left and right side, hope this can solve your problem.
def sumlist(l):
if not l or not isinstance(l, list):
return 0 # or return other default value.
if len(l) == 1:
return l[0]
half = len(l) // 2
left = l[:half] # left
right = l[-half:] # right
return sumlist(left) + sumlist(right)
test:
l = [1,2,3,4,5,6,7,8,9]
result = sumlist(l)
print(result) # 40
Maybe I misread your question, but do you actually need to do both things in one function?
To sum a list recursively, you could define that the empty list (your base case) has the sum 0. Any non-empty list has the sum of the first element plus the sum of the remainder (the "tail") of the list:
def sumList(a):
return 0 if not a else a[0] + sumList(a[1:])
To split a list recursively, you could keep track of the "left" and "right" list. The left list starts out being your input, the right list is empty. You then recursively prepend the last element of the left list to the right list until the right list is no longer shorter than the left:
def splitList(a, b=None):
b = b or []
return (a, b) if len(a) <= len(b) else splitList(a[:-1], [a[-1]] + b)
I suspect that passing around slices of lists is very inefficient in Python, so it might be better to rather pass around indices, e.g.
def sumList(a, idx=None):
idx = idx or 0
return 0 if idx >= len(a) else a[idx] + sumList(a, idx+1)
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
I'm trying to use a merge sort algorithm to count the inversions in an array that contains all the numbers in the range 1 to 100,000. It works fine when I input a small data set but doesn't return the correct answer when I input the file containing the larger array. Is there a problem with the way I'm inputting the file, maybe? I've never had to input a file to an array before, so I can't say definitively whether I'm doing it correctly.
file = open('IntegerArray.txt','r')
integerArray = []
for line in file:
integerArray.append(line)
inversions = 0
def divide(list):
if len(list) > 1:
middle = int(len(list) / 2)
left = divide(list[:middle])
right = divide(list[middle:])
#left = divide(left)
#right = divide(right)
elif len(list) <= 1:
return list
return merge(left,right)
def merge(left,right):
global inversions
result = []
while left != [] and right != []:
if left[0] < right[0]:
result.append(left[0])
if len(left) > 1:
left = left[1:]
else:
left = []
elif left[0] > right[0]:
result.append(right[0])
inversions += len(left)
if len(right) > 1:
right = right[1:]
else:
right = []
if left != []:
result += left
if right != []:
result += right
return result
divide(integerArray)
print(inversions)
This should return 2407905288 but returns 2397819672 instead.
Seems it should not work for most of cases with numbers bigger than 9! You're keeping numbers in a list of strings. So your comparator in merge functions compares two strings, so for example 2 is bigger than 12!
At least you need to change your first lines to:
file = open('IntegerArray.txt','r')
integerArray = []
for line in file.readlines():
integerArray.append(int(line.strip()))
try k way merge sort http://www.sanfoundry.com/java-program-k-way-merge-algorithm/ . the problem with large data set is that simple merge sort has to bring two large arrays into the main memory at run time which is not possible sometimes.
This is my Python code for merge sort, and I can't understand why I am getting an IndexError on line 23 .
line 23 does not give an error if put before the for loop but in the for loop it says list index out of range. :(
from math import floor
def merge_sort(a,p,r):
if p < r:
q = (p+r)/2
merge_sort(a,p,q)
merge_sort(a,q+1,r)
merge(a,p,q,r)
def merge(A,p,q,r):
n1 = q - r +1
n2 = r - q
L = []
R = []
#print n1
for i in range (1,n1):
L.append(a[p+i-1])
for j in range (1,n2):
R.append(a[q+j])
L.append(10000)
R.append(10000)
i,j=0,0
for k in range (p,r):
if L[i] <= R [j]: # This is where the error occurs
A[k] = L[i]
i = i + 1
else :
A[k] = R[j]
j = j + 1
a=[1,4,9,8,2,3,8,2,9]
merge_sort(a,1,len(a))
print a
This is not python, this is Pascal with Python syntax.
Here is implementation of merge sorting algo (based on Merge sort wiki article):
def merge_sort(data):
if len(data) <= 1:
return data
middle = len(data) / 2
left = merge_sort(data[0:middle])
right = merge_sort(data[middle:])
return merge(left, right)
def merge(left, right):
result = []
while left or right:
if left and right:
if left[0] <= right[0]:
result.append(left.pop(0))
continue
result.append(right.pop(0))
continue
if left:
result.append(left.pop(0))
elif right:
result.append(right.pop(0))
return result
data = [1, 4, 9, 8, 2, 3, 8, 2, 9]
result = merge_sort(data)
assert len(data) == len(result)
print result
I agree with the commenters/other posters, this script has a Python syntax (apart from indentations that are not the customary 4 spaces...) but is far from Pythonic...
Anyhow, for your problem: it looks like the issue shows up in merge when (p,q,r)=(1,2,3). In that case, n1=0 and n2=1. Looping on range(1,n1) will get you nothing, therefore your L will only have 1 element (the 10000 you append to it outside the loop).
Now, when you reach the for k in range (p,r) loop:
on the first iteration, k=p=1, L[0] <= R[0] and you add 1 to i, so i=1
on the second iteration, you try to access L[i], ie L[1], which isn't defined. Hence, the IndexError.
A few advices:
Learn how to use a debugger. Lots of IDEs come with some implementation, it really helps.
Remember that lists start with an index of 0. It's something you tend to forget when you come from another language.
Here's my python implementation of merge sort:
def sort(lst):
if len(lst) < 2:
return lst
def merge(left, right):
i = j = 0
merged = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merged.append(left[i])
i += 1
else:
merged.append(right[j])
j += 1
return merged
middle = len(lst) / 2
return merge(sort(lst[:middle]), sort(lst[middle:]))
When I use it, however, this implementation only returns a list with a single item - the smallest item in the list.
For instance, sort([1,2,3]) returns [1], and sort([4,2,-1,5]) returns [-1].
Why? Thank you.
You're very close, your merge() function will stop after one list is exhausted so you need to add the remaining ones to the merged list:
def sort(lst):
if len(lst) < 2:
return lst
def merge(left, right):
i = j = 0
merged = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
merged.append(left[i])
i += 1
else:
merged.append(right[j])
j += 1
merged.extend(left[i:])
merged.extend(right[j:])
return merged
middle = len(lst) / 2
return merge(sort(lst[:middle]), sort(lst[middle:]))
The problem is in the loop condition in merge(). The loop stops once you've reached the end of either of the two lists, while you of course want all elements from both. If you try testing the merge() function in isolation, you can see how this causes problems:
>>> merge([1, 5], [2, 7])
[1, 2, 5]
You need to ensure that once you've reached the end of one list that you still copy the remaining elements from the other one.
One way of doing this is to add another pair of loops which append any remaining elements from left or right to merged after the main merge loop.