Last active
October 27, 2022 13:39
-
-
Save Alfex4936/e8b6b7c06a181d3faa84b155b20e6de6 to your computer and use it in GitHub Desktop.
Python Sorting Algorithms : (TimSort + Introspective Sort + bogo Sort + pancake Sort + Insertion Sort + Binary Insertion Sort + Count Sort + Merge Sort + Quick Sort + Bubble Sort)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bisect | |
import heapq | |
import inspect | |
from random import randint, shuffle | |
from statistics import mean | |
from timeit import default_timer, repeat | |
import matplotlib.pyplot as plt | |
from bigO import bigO | |
from win10toast import ToastNotifier | |
''' | |
def runAlgorithm(algorithm, array, timeResults): | |
setup_code = f"from __main__ import {algorithm}" if algorithm != "sorted" else "" | |
stmt = f"{algorithm}({array})" | |
times = repeat(setup=setup_code, stmt=stmt, repeat=3, number=5) | |
timeResults[algorithm] = round(min(times), 5) | |
print(f"def: {algorithm}(), Execution time: {mean(times)/5:.5f}초") | |
def runAlgorithm2(algorithm, array, timeResults): | |
# result | |
tot = 0.0 | |
#print("Before:", array) | |
t0 = default_timer() | |
if algorithm == "sorted": | |
result = list(sorted(array)) | |
else: | |
result = globals()[algorithm](array) | |
t1 = default_timer() | |
#print("After:", result) | |
print(f"{algorithm}() Sorting Result : {list(sorted(array)) == result}") | |
tot += (t1-t0) | |
timeResults[algorithm] = round(tot, 5) | |
print(f"def: {algorithm}(), Execution Time: {tot:.5f}s") | |
''' | |
def runAlgorithm3(algorithm, array, timeResults): | |
lib = bigO.bigO() | |
# print("Before sorting", array) | |
testArray = array.copy() | |
time, result = lib.runtime(algorithm, testArray, epoch=3, prtResult=False) | |
print(f"{algorithm.__name__}(array) Sorting Result : {sorted(testArray) == result}") | |
# print("After sorting", result) | |
timeResults[algorithm] = round(time, 5) | |
def binarySearch(array, item, start, end): | |
if start == end: | |
if array[start] > item: | |
return start | |
else: | |
return start + 1 | |
if start > end: | |
return start | |
mid = (start + end) // 2 | |
if array[mid] < item: | |
return binarySearch(array, item, mid + 1, end) | |
elif array[mid] > item: | |
return binarySearch(array, item, start, mid - 1) | |
else: | |
return mid | |
def bubbleSort(array): # in-place | stable | |
""" | |
Best : O(n) | O(1) Space | |
Average : O(n^2) | O(1) Space | |
Worst : O(n^2) | O(1) Space | |
""" | |
isSorted = False | |
counter = 0 | |
n = len(array) - 1 | |
while not isSorted: | |
isSorted = True | |
for i in range(n - counter): | |
if array[i] > array[i + 1]: | |
array[i], array[i + 1] = array[i + 1], array[i] | |
isSorted = False | |
counter += 1 | |
return array | |
def binaryInsertSort(array): | |
for index in range(1, len(array)): | |
value = array[index] | |
pos = binarySearch(array, value, 0, index - 1) | |
array = array[:pos] + [value] + \ | |
array[pos:index] + array[index + 1:] | |
return array | |
def insertSort(array, begin=0, end=None): # in-place | stable | |
''' | |
Best O(n) Time | O(1) Space | |
Average O(n^2) Time | O(1) Space | |
Worst (On^2) Time | O(1) Space | |
''' | |
if end == None: | |
end = len(array) | |
for i in range(begin, end): | |
j = i | |
toChange = array[i] | |
while (j != begin and array[j - 1] > toChange): | |
array[j] = array[j - 1] | |
j -= 1 | |
array[j] = toChange | |
return array | |
def gnomeSort(array): # in-place | stable | |
''' | |
Best : O(n) Time | O(1) Space | |
Average : O(n^2) Time | O(1) Space | |
Worst : O(n^2) Time | O(1) Space | |
''' | |
index = 0 | |
while index < len(array): | |
if index == 0 or array[index] >= array[index - 1]: | |
index += 1 | |
else: | |
array[index], array[index-1] = array[index-1], array[index] | |
index -= 1 | |
return array | |
def pancakeSort(array): # in-place | not-stable | |
# O(n^2) Time | O(N) Space | |
if len(array) <= 1: | |
return array | |
for cur in range(len(array), 1, -1): | |
# Finding index of maximum number in arr | |
index_max = array.index(max(array[0:cur])) | |
if index_max+1 != cur: | |
# Needs moving | |
if index_max != 0: | |
# reverse from 0 to index_max | |
array[:index_max+1] = reversed(array[:index_max+1]) | |
# Reverse list | |
array[:cur] = reversed(array[:cur]) | |
return array | |
def bogoSort(array): # in-place | |
""" | |
Best Case Complexity: O(n) | |
Average Case Complexity: O(n(n-1)!) | |
Worst Case Complexity: O(∞) | |
""" | |
n = len(array) | |
def is_sorted(array): | |
i = 0 | |
while i+1 < n: | |
if array[i] > array[i+1]: | |
return False | |
i += 1 | |
return True | |
while not is_sorted(array): | |
shuffle(array) | |
return array | |
def smoothSort(array): # in-place | not-stable | |
# O(n) Time | O(n) Space | |
def numbersLeonardo(size): | |
numbers = [1, 1] | |
nextNumber = numbers[-1] + numbers[-2] + 1 | |
while len(numbers) >= 2 and size > nextNumber: | |
numbers.append(nextNumber) | |
nextNumber = numbers[-1] + numbers[-2] + 1 | |
numbers.reverse() | |
return numbers | |
def arrToHeap(data): | |
leonardoNumbers = numbersLeonardo(len(data)) | |
listHeaps = [] | |
m = 0 | |
for i in leonardoNumbers: | |
if len(data) - m >= i: | |
listHeaps.append(data[m: m+i]) | |
m += i | |
for i in listHeaps: | |
heapq.heapify(i) | |
listHeaps.reverse() | |
return listHeaps | |
def countIndexes(i, indexes): | |
indexes.append(2*indexes[i]+1) | |
indexes.append(2*indexes[i]+2) | |
return indexes | |
def getList(indexPart, heap): | |
heapPart = [] | |
for i in indexPart: | |
if i < len(heap): | |
heapPart.append(heap[i]) | |
return heapPart | |
def heapDivision(heap): | |
heapleft = [] | |
heapright = [] | |
index = 0 | |
indexesLeft = [1] | |
indexesRight = [2] | |
while indexesLeft[-1] < len(heap): | |
indexesLeft = countIndexes(index, indexesLeft) | |
indexesRight = countIndexes(index, indexesRight) | |
index += 1 | |
heapleft = getList(indexesLeft, heap) | |
heapright = getList(indexesRight, heap) | |
return heapleft, heapright | |
listHeaps = arrToHeap(array) | |
result = [] | |
heapLeft, heapRight = 0, 0 | |
while (listHeaps): | |
flag = 0 | |
minIndex = listHeaps.index(min(listHeaps)) | |
currentRoot = listHeaps[0][0] | |
currentMin = listHeaps[minIndex][0] | |
heapq.heapreplace(listHeaps[0], currentMin) | |
heapq.heapreplace(listHeaps[minIndex], currentRoot) | |
if len(listHeaps[0]) > 1: | |
heapLeft, heapRight = heapDivision(listHeaps[0]) | |
flag = 1 | |
minimum = heapq.heappop(listHeaps[0]) | |
result.append(minimum) | |
listHeaps.pop(0) | |
if flag == 1: | |
listHeaps.insert(0, heapLeft) | |
listHeaps.insert(0, heapRight) | |
return result | |
def countSort(arr): # stable | |
# Time Complexity : O(n) | Space Complexity : O(n) | |
minValue = min(arr) | |
maxValue = max(arr) - minValue | |
buckets = [0 for x in range(maxValue + 1)] | |
for i in arr: | |
buckets[i - minValue] += 1 | |
index = 0 | |
for i in range(len(buckets)): | |
while buckets[i] > 0: | |
arr[index] = i + minValue | |
index += 1 | |
buckets[i] -= 1 | |
return arr | |
def quickSort(array): # in-place | not-stable | |
''' | |
Best : O(nlogn) Time | O(logn) Space | |
Average : O(nlogn) Time | O(logn) Space | |
Worst : O(n^2) Time | O(logn) Space | |
''' | |
if len(array) <= 1: | |
return array | |
smaller, equal, larger = [], [], [] | |
pivot = array[randint(0, len(array)-1)] | |
for x in array: | |
if x < pivot: | |
smaller.append(x) | |
elif x == pivot: | |
equal.append(x) | |
else: | |
larger.append(x) | |
return quickSort(smaller)+equal+quickSort(larger) | |
def heapSort(arr): # in-place | not-stable | |
# Time Complexity O(nlogn) | Space Complexity O(1) | |
def heapify(arr, n, i): # Max Heap | |
largest = i # 트리에서 가장 큰 값 찾기 | |
l = 2 * i + 1 # Left Node | |
r = 2 * i + 2 # Right Node | |
if l < n and arr[i] < arr[l]: | |
largest = l | |
if r < n and arr[largest] < arr[r]: | |
largest = r | |
# root가 최대가 아니면 | |
# 최대 값과 바꾸고, 계속 heapify | |
if largest != i: | |
arr[i], arr[largest] = arr[largest], arr[i] | |
heapify(arr, n, largest) | |
n = len(arr) | |
for i in range(n // 2, -1, -1): | |
heapify(arr, n, i) | |
for i in range(n - 1, 0, -1): | |
arr[i], arr[0] = arr[0], arr[i] | |
# Heapify root element | |
heapify(arr, i, 0) | |
return arr | |
def heapSort2(iterable): # in-place | not-stable | |
# Time Complexity O(nlogn) | |
# Using Python source at | |
# https://github.com/python/cpython/blob/975d10a4f8f5d99b01d02fc5f99305a86827f28e/Lib/heapq.py | |
def heappop(heap): | |
"""Pop the smallest item off the heap, maintaining the heap invariant.""" | |
lastelt = heap.pop() # raises appropriate IndexError if heap is empty | |
if heap: | |
returnitem = heap[0] | |
heap[0] = lastelt | |
_siftup(heap, 0) | |
return returnitem | |
return lastelt | |
def _siftup(heap, pos): | |
endpos = len(heap) | |
startpos = pos | |
newitem = heap[pos] | |
# Bubble up the smaller child until hitting a leaf. | |
childpos = 2 * pos + 1 # leftmost child position | |
while childpos < endpos: | |
# Set childpos to index of smaller child. | |
rightpos = childpos + 1 | |
if rightpos < endpos and not heap[childpos] < heap[rightpos]: | |
childpos = rightpos | |
# Move the smaller child up. | |
heap[pos] = heap[childpos] | |
pos = childpos | |
childpos = 2 * pos + 1 | |
# The leaf at pos is empty now. Put newitem there, and bubble it up | |
# to its final resting place (by sifting its parents down). | |
heap[pos] = newitem | |
_siftdown(heap, startpos, pos) | |
def _siftdown(heap, startpos, pos): | |
newitem = heap[pos] | |
# Follow the path to the root, moving parents down until finding a place | |
# newitem fits. | |
while pos > startpos: | |
parentpos = (pos - 1) >> 1 | |
parent = heap[parentpos] | |
if newitem < parent: | |
heap[pos] = parent | |
pos = parentpos | |
continue | |
break | |
heap[pos] = newitem | |
def heapify(x): | |
"""Transform list into a heap, in-place, in O(len(x)) time.""" | |
n = len(x) | |
# Transform bottom-up. The largest index there's any point to looking at | |
# is the largest with a child index in-range, so must have 2*i + 1 < n, | |
# or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so | |
# j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is | |
# (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1. | |
for i in reversed(range(n // 2)): | |
_siftup(x, i) | |
return x | |
h = heapify(iterable) | |
return [heappop(h) for _ in range(len(h))] | |
def mergeSort(array): # stable | |
""" | |
Best : O(nlogn) Time | O(n) Space | |
Average : O(nlogn) Time | O(n) Space | |
Worst : O(nlongn) Time | O(n) Space | |
""" | |
def mergeHelper(main, startIdx, endIdx, aux): | |
if startIdx == endIdx: | |
return | |
middleIdx = (startIdx + endIdx) // 2 | |
mergeHelper(aux, startIdx, middleIdx, main) | |
mergeHelper(aux, middleIdx + 1, endIdx, main) | |
merge(main, startIdx, middleIdx, endIdx, aux) | |
def merge(main, startIdx, middleIdx, endIdx, aux): | |
k = startIdx | |
i = startIdx | |
j = middleIdx + 1 | |
while i <= middleIdx and j <= endIdx: | |
if aux[i] <= aux[j]: | |
main[k] = aux[i] | |
i += 1 | |
else: | |
main[k] = aux[j] | |
j += 1 | |
k += 1 | |
while i <= middleIdx: | |
main[k] = aux[i] | |
i += 1 | |
k += 1 | |
while j <= endIdx: | |
main[k] = aux[j] | |
j += 1 | |
k += 1 | |
if len(array) <= 1: | |
return array | |
aux = array[:] | |
mergeHelper(array, 0, len(array) - 1, aux) | |
return array | |
def introSort(array): # in-place | not-stable | |
# Time Complexity O(nlogn) | Space Complexity O(logn) | |
maxDepth = 2 * (len(array).bit_length() - 1) | |
sizeThreshold = 16 | |
return introSortHelper(array, 0, len(array), sizeThreshold, maxDepth) | |
def introSortHelper(array, start, end, sizeThreshold, depthLimit): | |
def medianOf3(array, lowIdx, midIdx, highIdx): | |
if (array[lowIdx] - array[midIdx]) * (array[highIdx] - array[lowIdx]) >= 0: | |
return array[lowIdx] | |
elif (array[midIdx] - array[lowIdx]) * (array[highIdx] - array[midIdx]) >= 0: | |
return array[midIdx] | |
else: | |
return array[highIdx] | |
def getPartition(array, low, high, pivot): | |
i = low | |
j = high | |
while True: | |
while (array[i] < pivot): | |
i += 1 | |
j -= 1 | |
while (pivot < array[j]): | |
j -= 1 | |
if i >= j: | |
return i | |
array[i], array[j] = array[j], array[i] | |
i += 1 | |
while(end - start > sizeThreshold): | |
if depthLimit == 0: | |
return heapSort(array) | |
depthLimit -= 1 | |
median = medianOf3(array, start, start + | |
((end - start) // 2) + 1, end - 1) | |
p = getPartition(array, start, end, median) | |
introSortHelper(array, p, end, sizeThreshold, depthLimit) | |
end = p | |
return insertSort(array, start, end) | |
def timSort(lst): | |
""" | |
Python sort(), sorted() implementation | |
from https://github.com/hu-ng/timsort/blob/master/timsort.py | |
Best : O(n) Time | O(n) Space | |
Average : O(nlogn) Time | O(n) Space | |
Worst : O(nlogn) Time | O(n) Space | |
""" | |
def reverse(lst, s, e): | |
"""Reverse the order of a list in place | |
Input: s = starting index, e = ending index""" | |
while s < e and s != e: | |
lst[s], lst[e] = lst[e], lst[s] | |
s += 1 | |
e -= 1 | |
def make_temp_array(lst, s, e): | |
"""From the lst given, make a copy from index s to index e""" | |
array = [] | |
while s <= e: | |
array.append(lst[s]) | |
s += 1 | |
return array | |
def merge_compute_minrun(n): | |
"""Returns the minimum length of a run from 23 - 64 so that | |
the len(array)/minrun is less than or equal to a power of 2.""" | |
r = 0 | |
while n >= 32: | |
r |= n & 1 | |
n >>= 1 | |
return n + r | |
def count_run(lst, s_run): | |
"""Count the length of one run, returns starting/ending indices, | |
a boolean value to present increasing/decreasing run, | |
and the length of the run""" | |
increasing = True | |
# If count_run started at the final position of the array | |
if s_run == len(lst) - 1: | |
return [s_run, s_run, increasing, 1] | |
else: | |
e_run = s_run | |
# Decreasing run (strictly decreasing): | |
if lst[s_run] > lst[s_run + 1]: | |
while lst[e_run] > lst[e_run + 1]: | |
e_run += 1 | |
if e_run == len(lst) - 1: | |
break | |
increasing = False | |
return [s_run, e_run, increasing, e_run - s_run + 1] | |
# Increasing run (non-decreasing): | |
else: | |
while lst[e_run] <= lst[e_run + 1]: | |
e_run += 1 | |
if e_run == len(lst) - 1: | |
break | |
return [s_run, e_run, increasing, e_run - s_run + 1] | |
def bin_sort(lst, s, e, extend): | |
"""Binary insertion sort, assumed that lst[s:e + 1] is sorted. | |
Extend the run by the number indicated by 'extend'""" | |
for i in range(1, extend + 1): | |
pos = 0 | |
start = s | |
end = e + i | |
# Value to be inserted | |
value = lst[end] | |
# If the value is already bigger than the last element from start -> end: | |
# Don't do the following steps | |
if value >= lst[end - 1]: | |
continue | |
# While-loop does the binary search | |
while start <= end: | |
if start == end: | |
if lst[start] > value: | |
pos = start | |
break | |
else: | |
pos = start + 1 | |
break | |
mid = (start + end) // 2 | |
if value >= lst[mid]: | |
start = mid + 1 | |
else: | |
end = mid - 1 | |
if start > end: | |
pos = start | |
# 'Push' the elements to the right by 1 element | |
# Copy the value back the right position. | |
for x in range(e + i, pos, -1): | |
lst[x] = lst[x - 1] | |
lst[pos] = value | |
def gallop(lst, val, low, high, ltr): | |
"""Find the index of val in the slice[low:high]""" | |
if ltr == True: | |
# Used for merging from left to right | |
# The index found will be so that every element prior | |
# to that index is strictly smaller than val | |
pos = bisect.bisect_left(lst, val, low, high) | |
return pos | |
else: | |
# Used for merging from right to left | |
# The index found will be so that every element from | |
# that index onwards is strictly larger than val | |
pos = bisect.bisect_right(lst, val, low, high) | |
return pos | |
def merge(lst, stack, run_num): | |
"""Merge the two runs and update the remaining runs in the stack | |
Only consequent runs are merged, one lower, one upper.""" | |
# Make references to the to-be-merged runs | |
run_a = stack[run_num] | |
run_b = stack[run_num + 1] | |
# Make a reference to where the new combined run would be. | |
new_run = [run_a[0], run_b[1], True, run_b[1] - run_a[0] + 1] | |
# Put this new reference in the correct position in the stack | |
stack[run_num] = new_run | |
# Delete the upper run of the two runs from the stack | |
del stack[run_num + 1] | |
# If the length of run_a is smaller than or equal to length of run_b | |
if run_a[3] <= run_b[3]: | |
merge_low(lst, run_a, run_b, 7) | |
# If the length of run_a is bigger than length of run_b | |
else: | |
merge_high(lst, run_a, run_b, 7) | |
def merge_low(lst, a, b, min_gallop): | |
"""Merges the two runs quasi in-place if a is the smaller run | |
- a and b are lists that store data of runs | |
- min_gallop: threshold needed to switch to galloping mode | |
- galloping mode: uses gallop() to 'skip' elements instead of linear merge""" | |
# Make a copy of the run a, the smaller run | |
temp_array = make_temp_array(lst, a[0], a[1]) | |
# The first index of the merging area | |
k = a[0] | |
# Counter for the temp array of a | |
i = 0 | |
# Counter for b, starts at the beginning | |
j = b[0] | |
gallop_thresh = min_gallop | |
while True: | |
a_count = 0 # number of times a win in a row | |
b_count = 0 # number of times b win in a row | |
# Linear merge mode, taking note of how many times a and b wins in a row. | |
# If a_count or b_count > threshold, switch to gallop | |
while i <= len(temp_array) - 1 and j <= b[1]: | |
# if elem in a is smaller, a wins | |
if temp_array[i] <= lst[j]: | |
lst[k] = temp_array[i] | |
k += 1 | |
i += 1 | |
a_count += 1 | |
b_count = 0 | |
# If a runs out during linear merge | |
# Copy the rest of b | |
if i > len(temp_array) - 1: | |
while j <= b[1]: | |
lst[k] = lst[j] | |
k += 1 | |
j += 1 | |
return | |
# threshold reached, switch to gallop | |
if a_count >= gallop_thresh: | |
break | |
# if elem in b is smaller, b wins | |
else: | |
lst[k] = lst[j] | |
k += 1 | |
j += 1 | |
a_count = 0 | |
b_count += 1 | |
# If b runs out during linear merge | |
# copy the rest of a | |
if j > b[1]: | |
while i <= len(temp_array) - 1: | |
lst[k] = temp_array[i] | |
k += 1 | |
i += 1 | |
return | |
# threshold reached, switch to gallop | |
if b_count >= gallop_thresh: | |
break | |
# If one run is winning consistently, switch to galloping mode. | |
# i, j, and k are incremented accordingly | |
while True: | |
# Look for the position of b[j] in a | |
# bisect_left() -> a_adv = index in the slice [i: len(temp_array)] | |
# so that every elem before temp_array[a_adv] is strictly smaller than lst[j] | |
a_adv = gallop(temp_array, lst[j], i, len(temp_array), True) | |
# Copy the elements prior to a_adv to the merge area, increment k | |
for x in range(i, a_adv): | |
lst[k] = temp_array[x] | |
k += 1 | |
# Update the a_count to check successfulness of galloping | |
a_count = a_adv - i | |
# Advance i to a_adv | |
i = a_adv | |
# If run a runs out | |
if i > len(temp_array) - 1: | |
# Copy all of b over, if there is any left | |
while j <= b[1]: | |
lst[k] = lst[j] | |
k += 1 | |
j += 1 | |
return | |
# Copy b[j] over | |
lst[k] = lst[j] | |
k += 1 | |
j += 1 | |
# If b runs out | |
if j > b[1]: | |
# Copy all of a over, if there is any left | |
while i < len(temp_array): | |
lst[k] = temp_array[i] | |
k += 1 | |
i += 1 | |
return | |
# ------------------------------------------------------ | |
# Look for the position of a[i] in b | |
# b_adv is analogous to a_adv | |
b_adv = gallop(lst, temp_array[i], j, b[1] + 1, True) | |
for y in range(j, b_adv): | |
lst[k] = lst[y] | |
k += 1 | |
# Update the counters and check the conditions | |
b_count = b_adv - j | |
j = b_adv | |
# If b runs out | |
if j > b[1]: | |
# copy the rest of a over | |
while i <= len(temp_array) - 1: | |
lst[k] = temp_array[i] | |
k += 1 | |
i += 1 | |
return | |
# copy a[i] over to the merge area | |
lst[k] = temp_array[i] | |
i += 1 | |
k += 1 | |
# If a runs out | |
if i > len(temp_array) - 1: | |
# copy the rest of b over | |
while j <= b[1]: | |
lst[k] = lst[j] | |
k += 1 | |
j += 1 | |
return | |
# if galloping proves to be unsuccessful, return to linear | |
if a_count < gallop_thresh and b_count < gallop_thresh: | |
break | |
# punishment for leaving galloping | |
# makes it harder to enter galloping next time | |
gallop_thresh += 1 | |
def merge_high(lst, a, b, min_gallop): | |
"""Merges the two runs quasi in-place if b is the smaller run | |
- Analogous to merge_low, but starts from the end | |
- a and b are lists that store data of runs | |
- min_gallop: threshold needed to switch to galloping mode | |
- galloping mode: uses gallop() to 'skip' elements instead of linear merge""" | |
# Make a copy of b, the smaller run | |
temp_array = make_temp_array(lst, b[0], b[1]) | |
# Counter for the merge area, starts at the last index of array b | |
k = b[1] | |
# Counter for the temp array | |
i = len(temp_array) - 1 # Lower bound is 0 | |
# Counter for a, starts at the end this time | |
j = a[1] | |
gallop_thresh = min_gallop | |
while True: | |
a_count = 0 # number of times a win in a row | |
b_count = 0 # number of times b win in a row | |
# Linear merge, taking note of how many times a and b wins in a row. | |
# If a_count or b_count > threshold, switch to gallop | |
while i >= 0 and j >= a[0]: | |
if temp_array[i] > lst[j]: | |
lst[k] = temp_array[i] | |
k -= 1 | |
i -= 1 | |
a_count = 0 | |
b_count += 1 | |
# If b runs out during linear merge | |
if i < 0: | |
while j >= a[0]: | |
lst[k] = lst[j] | |
k -= 1 | |
j -= 1 | |
return | |
if b_count >= gallop_thresh: | |
break | |
else: | |
lst[k] = lst[j] | |
k -= 1 | |
j -= 1 | |
a_count += 1 | |
b_count = 0 | |
# If a runs out during linear merge | |
if j < a[0]: | |
while i >= 0: | |
lst[k] = temp_array[i] | |
k -= 1 | |
i -= 1 | |
return | |
if a_count >= gallop_thresh: | |
break | |
# i, j, k are DECREMENTED in this case | |
while True: | |
# Look for the position of b[i] in a[0, j + 1] | |
# ltr = False -> uses bisect_right() | |
a_adv = gallop(lst, temp_array[i], a[0], j + 1, False) | |
# Copy the elements from a_adv -> j to merge area | |
# Go backwards to the index a_adv | |
for x in range(j, a_adv - 1, -1): | |
lst[k] = lst[x] | |
k -= 1 | |
# # Update the a_count to check successfulness of galloping | |
a_count = j - a_adv + 1 | |
# Decrement index j | |
j = a_adv - 1 | |
# If run a runs out: | |
if j < a[0]: | |
while i >= 0: | |
lst[k] = temp_array[i] | |
k -= 1 | |
i -= 1 | |
return | |
# Copy the b[i] into the merge area | |
lst[k] = temp_array[i] | |
k -= 1 | |
i -= 1 | |
# If a runs out: | |
if i < 0: | |
while j >= a[0]: | |
lst[k] = lst[j] | |
k -= 1 | |
j -= 1 | |
return | |
# ------------------------------------------------- | |
# Look for the position of A[j] in B: | |
b_adv = gallop(temp_array, lst[j], 0, i + 1, False) | |
for y in range(i, b_adv - 1, -1): | |
lst[k] = temp_array[y] | |
k -= 1 | |
b_count = i - b_adv + 1 | |
i = b_adv - 1 | |
# If b runs out: | |
if i < 0: | |
while j >= a[0]: | |
lst[k] = lst[j] | |
k -= 1 | |
j -= 1 | |
return | |
# Copy the a[j] back to the merge area | |
lst[k] = lst[j] | |
k -= 1 | |
j -= 1 | |
# If a runs out: | |
if j < a[0]: | |
while i >= 0: | |
lst[k] = temp_array[i] | |
k -= 1 | |
i -= 1 | |
return | |
# if galloping proves to be unsuccessful, return to linear | |
if a_count < gallop_thresh and b_count < gallop_thresh: | |
break | |
# punishment for leaving galloping | |
gallop_thresh += 1 | |
def merge_collapse(lst, stack): | |
"""The last three runs in the stack is A, B, C. | |
Maintains invariants so that their lengths: A > B + C, B > C | |
Translated to stack positions: | |
stack[-3] > stack[-2] + stack[-1] | |
stack[-2] > stack[-1] | |
Takes a stack that holds many lists of type [s, e, bool, length]""" | |
# This loops keeps running until stack has one element | |
# or the invariant holds. | |
while len(stack) > 1: | |
if len(stack) >= 3 and stack[-3][3] <= stack[-2][3] + stack[-1][3]: | |
if stack[-3][3] < stack[-1][3]: | |
# merge -3 and -2, merge at -3 | |
merge(lst, stack, -3) | |
else: | |
# merge -2 and -1, merge at -2 | |
merge(lst, stack, -2) | |
elif stack[-2][3] <= stack[-1][3]: | |
# merge -2 and -1, merge at -2 | |
merge(lst, stack, -2) | |
else: | |
break | |
def merge_force_collapse(lst, stack): | |
"""When the invariant holds and there are > 1 run | |
in the stack, this function finishes the merging""" | |
while len(stack) > 1: | |
# Only merges at -2, because when the invariant holds, | |
# merging would be balanced | |
merge(lst, stack, -2) | |
# Starting index | |
s = 0 | |
# Ending index | |
e = len(lst) - 1 | |
# The stack | |
stack = [] | |
# Compute min_run using size of lst | |
min_run = merge_compute_minrun(len(lst)) | |
while s <= e: | |
# Find a run, return [start, end, bool, length] | |
run = count_run(lst, s) | |
# If decreasing, reverse | |
if run[2] == False: | |
reverse(lst, run[0], run[1]) | |
# Change bool to True | |
run[2] = True | |
# If length of the run is less than min_run | |
if run[3] < min_run: | |
# The number of indices by which we want to extend the run | |
# either by the distance to the end of the lst | |
# or by the length difference between run and minrun | |
extend = min(min_run - run[3], e - run[1]) | |
# Extend the run using binary insertion sort | |
bin_sort(lst, run[0], run[1], extend) | |
# Update last index of the run | |
run[1] = run[1] + extend | |
# Update the run length | |
run[3] = run[3] + extend | |
# Push the run into the stack | |
stack.append(run) | |
# Start merging to maintain the invariant | |
merge_collapse(lst, stack) | |
# Update starting position to find the next run | |
# If run[1] == end of the lst, s > e, loop exits | |
s = run[1] + 1 | |
# Some runs might be left in the stack, complete the merging. | |
merge_force_collapse(lst, stack) | |
return lst | |
def retrieve_name(var): | |
callers_local_vars = inspect.currentframe().f_back.f_locals.items() | |
return [var_name for var_name, var_val in callers_local_vars if var_val is var] | |
def switch(name): | |
return { | |
"array1": "Random", | |
"array2": "Decending sorted", | |
"array3": "Ascending sorted", | |
"array4": "Partially sorted", | |
"array5": "Ksorted", | |
"array6": "All equal", | |
"array7": "Almost equal", | |
"array8": "Hole", | |
}.get(name, "") | |
def saveResultAsPNG(timeResults, name, title): | |
times = [] | |
labels = [] | |
# Sort dictionary by values | |
for key, value in sorted(timeResults.items(), key=lambda x: x[1]): | |
times.append(value) | |
labels.append(key) | |
plt.figure(figsize=(10, 8), dpi=80) # 800 x 640 | |
ax = plt.subplot() | |
ax.set_xticks(range(len(times))) | |
ax.set_xticklabels(labels, rotation=15) | |
plt.bar(range(len(times)), times) | |
plt.title(f"{title} (n: {ARRAY_LENGTH})") | |
plt.ylabel('Times (sec)') | |
plt.savefig(name) | |
toaster = ToastNotifier() | |
toaster.show_toast("Sorting", | |
"Sorting algorithms are running", | |
icon_path="D:\DEV\Code\Python\img\python.ico", | |
duration=2) | |
# setrecursionlimit(ARRAY_LENGTH) | |
if __name__ == "__main__": | |
lib = bigO.bigO() | |
ARRAY_LENGTH = 10000 | |
# Random | |
array1 = lib.genRandomArray(ARRAY_LENGTH) | |
# Decending sorted | |
array2 = lib.genReversedArray(ARRAY_LENGTH) | |
# Ascending sorted | |
array3 = lib.genSortedArray(ARRAY_LENGTH) | |
# Partially sorted | |
array4 = lib.genPartialArray(ARRAY_LENGTH) | |
# Ksorted | |
array5 = lib.genKsortedArray(ARRAY_LENGTH, ARRAY_LENGTH.bit_length()) | |
# Equal | |
array6 = lib.genEqualArray(ARRAY_LENGTH) | |
# Almost Equal | |
array7 = lib.genAlmostEqualArray(ARRAY_LENGTH) | |
# Hole | |
array8 = lib.genHoleArray(ARRAY_LENGTH) | |
run = { | |
sorted: 0, | |
countSort: 0, | |
introSort: 0, | |
timSort: 0, | |
quickSort2: 0, | |
mergeSort: 0, | |
heapSort: 0, | |
heapSort2: 0, | |
smoothSort: 0, | |
pancakeSort: 0, | |
insertSort: 0, | |
insertSort2: 0, | |
binaryInsertSort: 0, | |
bubbleSort: 0, | |
goSort: 0 | |
} | |
test = [array1, array2, array3, array4, array5, array6, array7, array8] | |
for index in range(len(test)): | |
for function in run.keys(): | |
testArray = test[index].copy() # Shallow Copy | |
runAlgorithm3(algorithm=function, array=testArray, timeResults=run) | |
# name = switch(retrieve_name(test[index])) | |
name = switch(retrieve_name(test[index])[0]) | |
saveResultAsPNG(run, f"array{index + 1}.png", name) | |
# runAlgorithm(algorithm="bogoSort", array=testArray, timeResults=times) | |
toaster.show_toast( | |
"Sorting", "Measurements are made", icon_path=None, duration=5, threaded=False | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
win10toast == 0.9 | |
matplotlib == 3.3.1 | |
big-O-calculator == 0.0.9.8.2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment