# coding: utf-8 # # 1 Best cases # **1.1** Bubble sort # The short version of Bubble sort is given in the online book: # https://runestone.academy/runestone/static/FIT5211/SortSearch/TheBubbleSort.html # At every iteration, it detects whether an exchange has been made, and only continues iteration if that is the case. Given a sorted list, the first iteration will make no exchange, thus no other iteration will be performed, and only the inner loop will actually iterate over the entire list. The running time in that case is thus $O(n)$. # **1.2** Selection sort # After every pass of selection sort, the only thing we know is that one more item is now in place. The algorithm has no way of knowing and thus using the fact that the list is already sorted. The algorithm will thus perform the whole $O(n^2)$ operations, as in the worst case. # **1.3** Merge sort # Merge splits the input list into two, without looking at its contents. the contents are only looked at upon merging, after every item in the list has been split into a sublist of size 1. At this point, all merging operations still need to happen. In this case, the running time is thus also $O(n \log{n})$. # # 2 Merge sort without slicing # In[1]: #start is the index of the first item #end is the index past the the last item def indexMergeSort(alist, start = None, end = None): if start == None: start = 0 if end == None: end = len(alist) #note that the print operations still use slicing for convenience #print("Input: ",alist[start:end]) length = end - start if length > 1: #print("Splitting ",alist[start:end]) mid = start + length//2 indexMergeSort(alist, start, mid) indexMergeSort(alist, mid, end) i=start # index for the left part of the list j=mid # index for the right part of the list #we use a temporary list templist = [None] * (length) k = 0 #index for the temp list while i < mid and j < end: if alist[i] < alist[j]: templist[k] = alist[i] i=i+1 else: #we swap templist[k] = alist[j] j=j+1 k=k+1 while i < mid: templist[k] = alist[i] i=i+1 k=k+1 while j < end: templist[k]=alist[j] j=j+1 k=k+1 #we copy the results for k in range(length): alist[start+k] = templist[k] #print("Merging ",alist[start:mid], alist[mid:end]) alist = [54,26,93,17,77,31,44,55,20] indexMergeSort(alist) print(alist) # # 3 Benchmarking sorting algorithms # In[2]: import random def generatelist(n, lower = 0, upper = 1000, seed = 0): #https://docs.python.org/3/library/random.html random.seed(seed) l = [None] * n for i in range(0,n): l[i] = random.randrange(lower, upper) return l # In[3]: print(generatelist(10)) # Below we have copied the sorting functions from the online book: # In[4]: def bubbleSort(alist): for passnum in range(len(alist)-1,0,-1): for i in range(passnum): if alist[i]>alist[i+1]: temp = alist[i] alist[i] = alist[i+1] alist[i+1] = temp # In[5]: def shortBubbleSort(alist): exchanges = True passnum = len(alist)-1 while passnum > 0 and exchanges: exchanges = False for i in range(passnum): if alist[i]>alist[i+1]: exchanges = True temp = alist[i] alist[i] = alist[i+1] alist[i+1] = temp passnum = passnum-1 # In[6]: def selectionSort(alist): for fillslot in range(len(alist)-1,0,-1): positionOfMax=0 for location in range(1,fillslot+1): if alist[location]>alist[positionOfMax]: positionOfMax = location temp = alist[fillslot] alist[fillslot] = alist[positionOfMax] alist[positionOfMax] = temp # In[7]: def insertionSort(alist): for index in range(1,len(alist)): currentvalue = alist[index] position = index while position>0 and alist[position-1]>currentvalue: alist[position]=alist[position-1] position = position-1 alist[position]=currentvalue # In[8]: def shellSort(alist): sublistcount = len(alist)//2 while sublistcount > 0: for startposition in range(sublistcount): gapInsertionSort(alist,startposition,sublistcount) #print("After increments of size",sublistcount, # "The list is",alist) sublistcount = sublistcount // 2 def gapInsertionSort(alist,start,gap): for i in range(start+gap,len(alist),gap): currentvalue = alist[i] position = i while position>=gap and alist[position-gap]>currentvalue: alist[position]=alist[position-gap] position = position-gap alist[position]=currentvalue # In[9]: def mergeSort(alist): #print("Splitting ",alist) if len(alist)>1: mid = len(alist)//2 lefthalf = alist[:mid] righthalf = alist[mid:] mergeSort(lefthalf) mergeSort(righthalf) i=0 j=0 k=0 while i < len(lefthalf) and j < len(righthalf): if lefthalf[i] < righthalf[j]: alist[k]=lefthalf[i] i=i+1 else: alist[k]=righthalf[j] j=j+1 k=k+1 while i < len(lefthalf): alist[k]=lefthalf[i] i=i+1 k=k+1 while j < len(righthalf): alist[k]=righthalf[j] j=j+1 k=k+1 #print("Merging ",alist) # In[10]: def quickSort(alist): quickSortHelper(alist,0,len(alist)-1) def quickSortHelper(alist,first,last): if first= pivotvalue and rightmark >= leftmark: rightmark = rightmark -1 if rightmark < leftmark: 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 # Now we benchmark them: # In[11]: sorting_algorithms = [bubbleSort, shortBubbleSort, selectionSort, insertionSort, shellSort, mergeSort, indexMergeSort, quickSort] #matplotlib.org/gallery/lines_bars_and_markers/line_styles_reference.html styles = {} styles[bubbleSort] = "r-" styles[shortBubbleSort] = "r:" styles[selectionSort] = "y-" styles[insertionSort] = "g-" styles[shellSort] = "g:" styles[mergeSort] = "b--" styles[indexMergeSort] = "b:" styles[quickSort] = "y--" # In[19]: from timeit import default_timer as timer import datetime # matplotlib may need to be installed separately import matplotlib.pyplot as plt def computeandplot(sorting_algorithms): step = 100 nsamples = 100 samples = range(0, nsamples) timelimit = 0.05 #seconds, per run totaltime = 0 print("Maximum computing time:", str(datetime.timedelta(seconds= timelimit*nsamples*len(sorting_algorithms)))) for sortalgo in sorting_algorithms: attimelimit = False times = [timelimit for _ in samples] for i in samples: if i == 0: times[i] = 0 continue n = step * i if attimelimit == False: l = generatelist(n) start = timer() sortalgo(l) end = timer() times[i] = end - start totaltime += times[i] if times[i] > timelimit: times[i] = timelimit attimelimit = True plt.plot(samples, times, styles[sortalgo], label=sortalgo.__name__) print("Actual computing time:", str(datetime.timedelta(seconds=int(totaltime)))) plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.xlabel("list size / {}".format(step)) plt.ylabel("time in seconds") plt.show() computeandplot(sorting_algorithms) # # 4 Sorting small integers # **4.1** # In[13]: def countsort(alist): maxv = max(alist) l = [0]*(maxv+1) for i in alist: l[i] += 1 k = 0 for i in range(0, len(alist)): while l[k] == 0: k += 1 # we count the number of values = k in alist alist[i] = k l[k] -= 1 # In[14]: alist = [54,26,93,17,77,31,44,55,20] countsort(alist) print(alist) # **4.2** # In[15]: import math DEFAULT_BUCKET_SIZE = 5 def bucketsort(array=alist, bucketSize=DEFAULT_BUCKET_SIZE): if len(array) == 0: return array # Determine minimum and maximum values minValue = array[0] maxValue = array[0] for i in range(1, len(array)): if array[i] < minValue: minValue = array[i] elif array[i] > maxValue: maxValue = array[i] # Initialize buckets bucketCount = math.floor((maxValue - minValue) / bucketSize) + 1 buckets = [] for i in range(0, bucketCount): buckets.append([]) # Distribute input array values into buckets for i in range(0, len(array)): buckets[math.floor((array[i] - minValue) / bucketSize)].append(array[i]) # Sort buckets and place back into input array array = [] for i in range(0, len(buckets)): insertion_sort(buckets[i]) for j in range(0, len(buckets[i])): array.append(buckets[i][j]) return array # In[16]: def default_compare(a, b): if a < b: return -1 elif a > b: return 1 return 0 # In[17]: def insertion_sort(array): for i in range(1, len(array)): item = array[i] indexHole = i while indexHole > 0 and default_compare(array[indexHole - 1], item) > 0: array[indexHole] = array[indexHole - 1] indexHole -= 1 array[indexHole] = item return array # In[18]: new_sorting_algorithms = [bucketsort, indexMergeSort, quickSort] styles[bucketsort] = "g--" computeandplot(new_sorting_algorithms)