forked from hofmannol/AlgoDatSoSe25
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe314b7c6c | |||
f033738d31 | |||
0401aa42ee | |||
62d6fa7459 | |||
04b6cddb39 | |||
1853c4d126 | |||
47ae350bcc |
@ -2,6 +2,16 @@ import logging
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
# logging.basicConfig(level=logging.DEBUG)
|
# logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
def timeMS(func, *args, **kwargs):
|
||||||
|
startTime = time.perf_counter()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
endTime = time.perf_counter()
|
||||||
|
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
|
||||||
|
print(f"{func.__name__} took {elapsedMS:.2f} ms")
|
||||||
|
return result
|
||||||
|
|
||||||
from utils.memory_array import MemoryArray
|
from utils.memory_array import MemoryArray
|
||||||
from utils.memory_cell import MemoryCell
|
from utils.memory_cell import MemoryCell
|
||||||
from utils.literal import Literal
|
from utils.literal import Literal
|
||||||
@ -111,5 +121,5 @@ if __name__ == '__main__':
|
|||||||
print(filename)
|
print(filename)
|
||||||
toSort = MemoryArray.create_array_from_file(filename)
|
toSort = MemoryArray.create_array_from_file(filename)
|
||||||
sorted = MemoryArray([-1] * toSort.length().get())
|
sorted = MemoryArray([-1] * toSort.length().get())
|
||||||
mergeSort(toSort, sorted)
|
timeMS(mergeSort, toSort, sorted)
|
||||||
print(sorted)
|
# print(sorted)
|
||||||
|
190
schoeffelbe/pr03.py
Normal file
190
schoeffelbe/pr03.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
# logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
def timeMS(func, *args, **kwargs):
|
||||||
|
startTime = time.perf_counter()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
endTime = time.perf_counter()
|
||||||
|
elapsedMS = (endTime - startTime) * 1000 # Convert to milliseconds
|
||||||
|
print(f"{func.__name__} took {elapsedMS:.2f} ms")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.literal import Literal
|
||||||
|
from utils.constants import MIN_VALUE
|
||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.memory_range import mrange
|
||||||
|
|
||||||
|
def example():
|
||||||
|
initial = [6, 5, 3, 8, 1, 7, 2, 4]
|
||||||
|
# initial = [-6, -5, -3, -8, 1, 7, 2, 4]
|
||||||
|
toSort = MemoryArray(initial)
|
||||||
|
quickSortIterative(toSort, Literal(0), toSort.length().pred())
|
||||||
|
logger.debug(f"sorted {toSort} vs initial {initial}")
|
||||||
|
assert all(toSort[Literal(i)] == Literal(i+1) for i in range(len(initial))), "Array not sorted correctly"
|
||||||
|
|
||||||
|
# analyze_complexity(quickSort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
|
||||||
|
def getPivot(z: MemoryArray, l: Literal, r: Literal, mode) -> Literal:
|
||||||
|
if mode == 0:
|
||||||
|
return r
|
||||||
|
else:
|
||||||
|
mid_offset = r.value - l.value
|
||||||
|
mid_offset = mid_offset // 2
|
||||||
|
mid = Literal(l.value + mid_offset)
|
||||||
|
|
||||||
|
# Return median of left, middle, and right elements
|
||||||
|
if ((z[l] <= z[mid] and z[mid] <= z[r]) or
|
||||||
|
(z[r] <= z[mid] and z[mid] <= z[l])):
|
||||||
|
return mid
|
||||||
|
elif ((z[mid] <= z[l] and z[l] <= z[r]) or
|
||||||
|
(z[r] <= z[l] and z[l] <= z[mid])):
|
||||||
|
return l
|
||||||
|
else:
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
# toSort[] --> Array to be sorted,
|
||||||
|
# left --> Starting index,
|
||||||
|
# right --> Ending index
|
||||||
|
# adapted from https://stackoverflow.com/questions/68524038/is-there-a-python-implementation-of-quicksort-without-recursion
|
||||||
|
def quickSortIterative(toSort : MemoryArray, left : Literal, right : Literal, mode=0):
|
||||||
|
# Create a manually managed stack and avoid pythons recursion-limit
|
||||||
|
size = right.value - left.value + 1
|
||||||
|
stack : MemoryArray = MemoryArray([0] * size)
|
||||||
|
top : MemoryCell = MemoryCell(-1)
|
||||||
|
|
||||||
|
# push initial values of l and h to stack
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = left
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = right
|
||||||
|
|
||||||
|
# Keep popping from stack until its empty
|
||||||
|
while top >= Literal(0):
|
||||||
|
logger.debug(f"size {size}, stack {stack}, right {right} and left {left}, top {top}")
|
||||||
|
|
||||||
|
# Pop h and l - Ensure we are not getting them by Ref, this will produce weird "JUST A LITTLE OF" Results
|
||||||
|
right = Literal(stack[top].get())
|
||||||
|
top -= Literal(1)
|
||||||
|
left = Literal(stack[top].get())
|
||||||
|
top -= Literal(1)
|
||||||
|
|
||||||
|
# Set pivot element at its correct position in sorted array
|
||||||
|
p = partitionIterative(toSort, left, right, mode)
|
||||||
|
|
||||||
|
# If there are elements on left side of pivot, then push left side to stack
|
||||||
|
if p.pred() > left:
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = left
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = p.pred()
|
||||||
|
|
||||||
|
# If there are elements on right side of pivot, then push right side to stack
|
||||||
|
if p.succ() < right:
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = p.succ()
|
||||||
|
top += Literal(1)
|
||||||
|
stack[top] = right
|
||||||
|
|
||||||
|
def partitionIterative(arr : MemoryArray, l : Literal, h : Literal, mode=0):
|
||||||
|
logger.debug(f"Partitioning {arr}, {l} and {h}")
|
||||||
|
pivot_idx : Literal = getPivot(arr, l, h, mode)
|
||||||
|
|
||||||
|
# If pivot isn't at the high end, swap it there
|
||||||
|
if pivot_idx != h:
|
||||||
|
swap(arr, int(pivot_idx), int(h))
|
||||||
|
|
||||||
|
# Carefull that we do not use a reference. I suppose python would return one here if we just assign without value>Literal cast.
|
||||||
|
# At least this helped fix weird issue
|
||||||
|
pivotValue : Literal = Literal(arr[h].value)
|
||||||
|
i : MemoryCell = MemoryCell(l.pred())
|
||||||
|
|
||||||
|
for j in mrange(l, h):
|
||||||
|
if arr[j] <= pivotValue:
|
||||||
|
i += Literal(1) # increment index of smaller element
|
||||||
|
swap(arr, int(i), int(j))
|
||||||
|
|
||||||
|
swap(arr, int(i.succ()), int(h))
|
||||||
|
return i.succ()
|
||||||
|
|
||||||
|
def LEGACY_quickSort(z: MemoryArray, l: Literal = Literal(0), r: Literal = Literal(-1), mode=0):
|
||||||
|
if r == Literal(-1):
|
||||||
|
r = z.length().pred();
|
||||||
|
if l < r:
|
||||||
|
q = LEGACY_partition(z, l, r, mode)
|
||||||
|
LEGACY_quickSort(z, l, q.pred())
|
||||||
|
LEGACY_quickSort(z, q.succ(), r)
|
||||||
|
|
||||||
|
def LEGACY_partition(z: MemoryArray, l: Literal, r: Literal, mode):
|
||||||
|
# Get pivot
|
||||||
|
pivot_idx = getPivot(z, l, r, mode)
|
||||||
|
|
||||||
|
# If pivot is not already at the right end, swap it there
|
||||||
|
if pivot_idx != r:
|
||||||
|
swap(z, int(pivot_idx), int(r))
|
||||||
|
|
||||||
|
with MemoryCell(z[r]) as pivot, MemoryCell(l) as i, MemoryCell(r.pred()) as j:
|
||||||
|
while i < j:
|
||||||
|
while z[i] < pivot:
|
||||||
|
i.set(i.succ())
|
||||||
|
while j > l and z[j] >= pivot:
|
||||||
|
j.set(j.pred())
|
||||||
|
if i < j:
|
||||||
|
swap(z, int(i), int(j))
|
||||||
|
i.set(i.succ())
|
||||||
|
j.set(j.pred())
|
||||||
|
if i == j and z[i] < pivot:
|
||||||
|
i.set(i.succ())
|
||||||
|
if z[i] != pivot:
|
||||||
|
swap(z, int(i), int(r))
|
||||||
|
return Literal(i)
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(fn, sizes):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer maximalen Teilfolgenfunktion.
|
||||||
|
|
||||||
|
:param max_sequence_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
fn(random_array, Literal(0), random_array.length().pred())
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "adds", "compares", "reads", "writes"])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# For debug, assert if working and complexity-analysis
|
||||||
|
example()
|
||||||
|
|
||||||
|
print("I ran into a MaxRecursionDepth Error. From what I read on the Internet python does not do Tailcall Optimizations")
|
||||||
|
print("Increasing recursion-limit seems like a poor Idea, therefore tried an iterative approach with manual stack-keeping")
|
||||||
|
|
||||||
|
toSort = MemoryArray.create_array_from_file("data/seq0.txt")
|
||||||
|
print(toSort)
|
||||||
|
quickSortIterative(toSort, Literal(0), toSort.length().pred())
|
||||||
|
print(toSort)
|
||||||
|
|
||||||
|
# analyze_complexity(quickSortIterative, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
for filename in ["data/seq0.txt", "data/seq1.txt", "data/seq2.txt" ,"data/seq3.txt"]:
|
||||||
|
# for filename in [ "data/seq1.txt"]:
|
||||||
|
print(filename)
|
||||||
|
toSort = MemoryArray.create_array_from_file(filename)
|
||||||
|
timeMS(quickSortIterative, toSort, Literal(0), toSort.length().pred(), mode=1)
|
||||||
|
# print(toSort)
|
||||||
|
|
||||||
|
print("Kann durch die Modifikation eine besser Laufzeit als nlog(n) erreicht werden? Nein! nlog(n) ist das Minimum. Durch die Änderung kann aber der Worst-Case fall von n^2 für z.B. bereits vorsortierte Arrays oder Arrays mit vielen Duplikaten vermieden werden.")
|
158
schoeffelbe/priorityQueue.py
Normal file
158
schoeffelbe/priorityQueue.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.literal import Literal
|
||||||
|
from utils.constants import MAX_VALUE
|
||||||
|
from utils.memory_range import mrange
|
||||||
|
|
||||||
|
# Impl of MemoryArray says we cant add our own Datatypes beside Literal and List
|
||||||
|
# BUUUUT we can just wrap our Datatype in a List :-)
|
||||||
|
# We store them in a MemoryArray internaly tho anyhow so we increment our Counters for the RAM
|
||||||
|
class HeapEntry:
|
||||||
|
def __init__(self, item, priority=1):
|
||||||
|
self.data = MemoryArray(Literal(2))
|
||||||
|
# 0: Content, 1: Prio
|
||||||
|
self.data[Literal(0)] = Literal(item)
|
||||||
|
self.data[Literal(1)] = Literal(priority)
|
||||||
|
|
||||||
|
def getItem(self):
|
||||||
|
return self.data[Literal(0)]
|
||||||
|
|
||||||
|
def getPriority(self):
|
||||||
|
return self.data[Literal(1)]
|
||||||
|
|
||||||
|
def setPriority(self, priority):
|
||||||
|
self.data[Literal(1)] = Literal(priority)
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return True
|
||||||
|
if isinstance(other, (int, float)):
|
||||||
|
return self.getPriority().value > other
|
||||||
|
return self.getPriority() > other.getPriority()
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return False
|
||||||
|
if isinstance(other, (int, float)):
|
||||||
|
return self.getPriority().value < other
|
||||||
|
return self.getPriority() < other.getPriority()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.getPriority() == other.getPriority()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"({self.getItem()}, prio={self.getPriority()})"
|
||||||
|
|
||||||
|
class PriorityQueue:
|
||||||
|
def __init__(self, max_size : Literal = Literal(100)):
|
||||||
|
self.heap = MemoryArray(max_size)
|
||||||
|
# Add uninitialized HeapEntry Values so the Adds/Compares do not fail on emtpy stack.
|
||||||
|
# Would have to switch to MIN_VALUE if we switch what is a "Higher" Prio
|
||||||
|
for i in mrange(max_size.value):
|
||||||
|
self.heap[i].set([HeapEntry(MAX_VALUE, MAX_VALUE)])
|
||||||
|
self.size = MemoryCell(0)
|
||||||
|
|
||||||
|
def parent(self, i: Literal) -> Literal:
|
||||||
|
return MemoryCell(i.pred()) // Literal(2)
|
||||||
|
|
||||||
|
def leftChild(self, i: Literal) -> Literal:
|
||||||
|
return MemoryCell(MemoryCell(2) * i) + Literal(1)
|
||||||
|
|
||||||
|
def rightChild(self, i: Literal) -> Literal:
|
||||||
|
return MemoryCell(MemoryCell(2) * i) + Literal(2)
|
||||||
|
|
||||||
|
# Swap the Lists -> Therefore get the value which is the List and then Set it again
|
||||||
|
def swap(self, i: Literal, j: Literal):
|
||||||
|
tmp_i = self.heap[i].value
|
||||||
|
tmp_j = self.heap[j].value
|
||||||
|
self.heap[i].set(tmp_j)
|
||||||
|
self.heap[j].set(tmp_i)
|
||||||
|
|
||||||
|
def maxHeapify(self, i: Literal):
|
||||||
|
left = self.leftChild(i)
|
||||||
|
right = self.rightChild(i)
|
||||||
|
largest = i
|
||||||
|
|
||||||
|
if left < Literal(self.size.value) and self.heap[left].value[0] > self.heap[largest].value[0]:
|
||||||
|
largest = left
|
||||||
|
|
||||||
|
if right < Literal(self.size.value) and self.heap[right].value[0] > self.heap[largest].value[0]:
|
||||||
|
largest = right
|
||||||
|
|
||||||
|
if largest != i:
|
||||||
|
self.swap(i, largest)
|
||||||
|
self.maxHeapify(largest)
|
||||||
|
|
||||||
|
def insert(self, entry : HeapEntry):
|
||||||
|
if self.size >= self.heap.length():
|
||||||
|
raise IndexError("Heap full")
|
||||||
|
|
||||||
|
i = self.size
|
||||||
|
self.heap[i].set([entry])
|
||||||
|
self.size += Literal(1)
|
||||||
|
|
||||||
|
while i > Literal(0) and self.heap[self.parent(i)].value[0] < self.heap[i].value[0]:
|
||||||
|
self.swap(i, self.parent(i))
|
||||||
|
i = self.parent(i)
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
if self.isEmpty():
|
||||||
|
raise IndexError("Queue is empty!")
|
||||||
|
|
||||||
|
max_item = self.heap[Literal(0)].value[0]
|
||||||
|
|
||||||
|
self.heap[Literal(0)] = self.heap[self.size - Literal(1)]
|
||||||
|
self.size -= Literal(1)
|
||||||
|
|
||||||
|
self.maxHeapify(Literal(0))
|
||||||
|
|
||||||
|
return max_item
|
||||||
|
|
||||||
|
def peek(self):
|
||||||
|
if self.isEmpty():
|
||||||
|
raise IndexError("Queue is empty")
|
||||||
|
return self.heap[Literal(0)].value[0]
|
||||||
|
|
||||||
|
def isEmpty(self):
|
||||||
|
return self.size == Literal(0)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.size
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Proof of Concept
|
||||||
|
testEntry = HeapEntry("A", 2)
|
||||||
|
print(testEntry)
|
||||||
|
testArray = MemoryArray([testEntry])
|
||||||
|
print(testArray)
|
||||||
|
print(testArray[Literal(0)])
|
||||||
|
|
||||||
|
# Queue Testing
|
||||||
|
pq = PriorityQueue()
|
||||||
|
try:
|
||||||
|
pq.pop()
|
||||||
|
assert False, "Queue should be empty"
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
assert(pq.isEmpty() and pq.size == Literal(0))
|
||||||
|
entry = HeapEntry("A", 1)
|
||||||
|
pq.insert(entry)
|
||||||
|
assert(not pq.isEmpty() and pq.size == Literal(1))
|
||||||
|
pq.peek()
|
||||||
|
assert(not pq.isEmpty())
|
||||||
|
assert(pq.pop() == HeapEntry("A", 1))
|
||||||
|
assert(pq.isEmpty())
|
||||||
|
pq.insert(HeapEntry("A", 1))
|
||||||
|
pq.insert(HeapEntry("C", 3))
|
||||||
|
pq.insert(HeapEntry("B", 2))
|
||||||
|
assert(pq.size == Literal(3))
|
||||||
|
assert(pq.pop() == HeapEntry("A", 1))
|
||||||
|
assert(pq.pop() == HeapEntry("B", 2))
|
||||||
|
assert(pq.pop() == HeapEntry("C", 3))
|
||||||
|
pq.insert(HeapEntry("A", 1))
|
||||||
|
pq.insert(HeapEntry("C", 3))
|
||||||
|
pq.insert(HeapEntry("B", 2))
|
||||||
|
print(pq.pop())
|
||||||
|
print(pq.pop())
|
||||||
|
print(pq.pop())
|
Loading…
x
Reference in New Issue
Block a user