diff --git a/praktika/02_merge_sort/merge_game.py b/praktika/02_merge_sort/merge_game.py new file mode 100644 index 0000000..edbb2e2 --- /dev/null +++ b/praktika/02_merge_sort/merge_game.py @@ -0,0 +1,41 @@ +import random +import pygame +from utils.game import Game +from utils.memory_array import MemoryArray +from merge_sorting import merge_sort_stepwise + +WHITE = (255, 255, 255) +BLUE = (0, 0, 255) + +class MergeGame(Game): + + def __init__(self): + super().__init__("Merge Game", fps=10, size=(400, 400)) + random.seed() + l =list(range(1, 101)) + random.shuffle(l) + self.z = MemoryArray(l) + self.finished = False + self.sort_generator = merge_sort_stepwise(self.z) + + def update_game(self): + if not self.finished: + try: + next(self.sort_generator) + except StopIteration: + self.finished = True + return True + + def draw_game(self): + self.screen.fill(WHITE) + for i, cell in enumerate(self.z): + x = 50 + i*3 + y = 350 - cell.value * 3 + pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3)) + super().draw_game() + + +if __name__ == "__main__": + s = MergeGame() + s.run() + diff --git a/praktika/02_merge_sort/merge_sorting.py b/praktika/02_merge_sort/merge_sorting.py new file mode 100644 index 0000000..723e369 --- /dev/null +++ b/praktika/02_merge_sort/merge_sorting.py @@ -0,0 +1,87 @@ +from utils.memory_array import MemoryArray +from utils.memory_cell import MemoryCell +from utils.memory_manager import MemoryManager +from utils.memory_range import mrange +from utils.literal import Literal +from time import perf_counter as pfc + + + +def merge_sort_stepwise(z: MemoryArray, l: Literal = None, r: Literal = None, buffer: MemoryArray = None): + if l is None: + l = Literal(0) + if r is None: + r = z.length().pred() + if buffer is None: + buffer = MemoryArray(z.length()) + if l < r: + m = Literal((int(l) + int(r)) // 2) + yield from merge_sort_stepwise(z, l, m, buffer) + yield from merge_sort_stepwise(z, m.succ(), r, buffer) + yield from merge(z, l, m, r, buffer) + +def merge(z: MemoryArray, l: Literal, m: Literal, r: Literal, buffer: MemoryArray): + with MemoryCell(l) as i, MemoryCell(m.succ()) as j, MemoryCell(l) as k: + while i <= m and j <= r: + if z[i] <= z[j]: + buffer[k].set(z[i]) + i = i.succ() + else: + buffer[k].set(z[j]) + j = j.succ() + k = k.succ() + while i <= m: + buffer[k].set(z[i]) + i = i.succ() + k = k.succ() + while j <= r: + buffer[k].set(z[j]) + j = j.succ() + k = k.succ() + for k in mrange(l, r.succ()): + z[k].set(buffer[k]) + yield z + +def merge_sort(z: MemoryArray): + sort_generator = merge_sort_stepwise(z) + while True: + try: + next(sort_generator) + except StopIteration: + break + +def sort_file(filename, sort_func): + z = MemoryArray.create_array_from_file(filename) + sort_func(z) + return z + +def analyze_complexity(sort_func, sizes, presorted=False): + """ + Analysiert die Komplexität einer Sortierfunktion. + + :param sort_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 + if presorted: + random_array = MemoryArray.create_sorted_array(size) + else: + random_array = MemoryArray.create_random_array(size, -100, 100) + sort_func(random_array) + MemoryManager.save_stats(size) + + MemoryManager.plot_stats(["cells", "compares", "writes"]) + +def swap(z: MemoryArray, i: int, j: int): + tmp = z[Literal(i)].value + z[Literal(i)] = z[Literal(j)] + z[Literal(j)].set(tmp) + +if __name__ == '__main__': + analyze_complexity(merge_sort, [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"]: + print(filename) + start = pfc() + print(sort_file(filename, merge_sort)) + print(f"Dauer: {pfc() - start:.4f}s") diff --git a/utils/memory_array.py b/utils/memory_array.py index 92ab2ca..3d3b266 100644 --- a/utils/memory_array.py +++ b/utils/memory_array.py @@ -58,6 +58,10 @@ class MemoryArray: """Gibt die Größe des Speicherarrays zurück.""" return Literal(self.size) + def count_compares(self): + return sum([cell.compare_count for cell in self.cells]) + + def reset_counters(self): """Setzt alle Zähler auf 0 zurück.""" for cell in self.cells: diff --git a/utils/memory_cell.py b/utils/memory_cell.py index 1bc4d60..ae748c3 100644 --- a/utils/memory_cell.py +++ b/utils/memory_cell.py @@ -2,11 +2,24 @@ from utils.memory_manager import MemoryManager from utils.literal import Literal class MemoryCell (Literal): + + def __new__(cls, *args, **kwargs): + """Erstellt eine neue Instanz von MemoryCell.""" + instance = MemoryManager().acquire_cell() + if instance is None: + instance = super().__new__(cls) + MemoryManager().register_cell(instance) + return instance + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + MemoryManager().release_cell(self) + def __init__(self, value=None): """Initialisiert eine Speicherzelle mit optionalem Startwert.""" super().__init__(value) - manager = MemoryManager.get_instance() - manager.add_cell(self) self.write_count = 0 self.add_count = 0 self.sub_count = 0 @@ -171,10 +184,6 @@ class MemoryCell (Literal): self.div(other) return self - def __imod__(self, other): - self.mod(other) - return self - if __name__ == "__main__": a = MemoryCell(5) diff --git a/utils/memory_manager.py b/utils/memory_manager.py index 85cb2ec..f1a5ba2 100644 --- a/utils/memory_manager.py +++ b/utils/memory_manager.py @@ -1,63 +1,72 @@ -import numpy as np import matplotlib.pyplot as plt +import queue class MemoryManager: - instance = None + _instance = None stats = {} - @staticmethod - def get_instance(): - if MemoryManager.instance is None: - MemoryManager.instance = MemoryManager() - return MemoryManager.instance + def __new__(cls, *args, **kwargs): + """Erstellt eine einzige Instanz von MemoryManager.""" + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._instance._initialize() # Eigene Init-Methode, damit __init__ nicht mehrfach läuft + return cls._instance + + def _initialize(self): + """Initialisiert die Speicherverwaltung (einmalig).""" + self.cells = [] + self._pool = queue.Queue() + self._finalizers = {} + @staticmethod def count_cells(): - return len(MemoryManager.get_instance().cells) + return len(MemoryManager().cells) + @staticmethod def count_reads(): - return sum([cell.read_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.read_count for cell in MemoryManager().cells]) @staticmethod def count_writes(): - return sum([cell.write_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.write_count for cell in MemoryManager().cells]) @staticmethod def count_compares(): - return sum([cell.compare_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.compare_count for cell in MemoryManager().cells]) @staticmethod def count_adds(): - return sum([cell.add_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.add_count for cell in MemoryManager().cells]) @staticmethod def count_subs(): - return sum([cell.sub_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.sub_count for cell in MemoryManager().cells]) @staticmethod def count_muls(): - return sum([cell.mul_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.mul_count for cell in MemoryManager().cells]) @staticmethod def count_divs(): - return sum([cell.div_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.div_count for cell in MemoryManager().cells]) @staticmethod def count_bitops(): - return sum([cell.bitop_count for cell in MemoryManager.get_instance().cells]) + return sum([cell.bitop_count for cell in MemoryManager().cells]) @staticmethod def reset(): - manager = MemoryManager.get_instance() + manager = MemoryManager() for cell in manager.cells: cell.reset_counters() @staticmethod def purge(): - MemoryManager.instance = None + MemoryManager._instance = None @staticmethod def save_stats(count): @@ -91,8 +100,49 @@ class MemoryManager: plt.xlabel("n") plt.show() - def __init__(self): - self.cells = [] - def add_cell(self, cell): + def acquire_cell(self): + try: + return self._pool.get_nowait() + except queue.Empty: + return None + + def register_cell(self, cell): self.cells.append(cell) + + def release_cell(self, cell): + self._pool.put(cell) + + +class Testcell: + + def __new__(cls, *args, **kwargs): + instance = MemoryManager().acquire_cell() + if instance is None: + instance = super().__new__(cls) + MemoryManager().register_cell(instance) + return instance + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + MemoryManager().release_cell(self) + + +if __name__ == "__main__": + + # Einfaches Anlegen einer Zelle + a = Testcell() + print(MemoryManager.count_cells()) + + # Anlegen einer Zelle und Beenden des Scopes + with Testcell() as b: + print(MemoryManager.count_cells()) + + print(MemoryManager.count_cells()) + + # Reuse einer Zelle + c = Testcell() + print(MemoryManager.count_cells()) + diff --git a/vorlesung/02_elementares_sortieren/bubble_sorting.py b/vorlesung/02_elementares_sortieren/bubble_sorting.py index 2ee32b3..1ee4d8e 100644 --- a/vorlesung/02_elementares_sortieren/bubble_sorting.py +++ b/vorlesung/02_elementares_sortieren/bubble_sorting.py @@ -1,6 +1,5 @@ from utils.memory_array import MemoryArray from utils.memory_cell import MemoryCell -from utils.constants import MIN_VALUE from utils.memory_manager import MemoryManager from utils.memory_range import mrange from utils.literal import Literal diff --git a/vorlesung/02_elementares_sortieren/insert_sorting.py b/vorlesung/02_elementares_sortieren/insert_sorting.py index 6e526ee..e962dc3 100644 --- a/vorlesung/02_elementares_sortieren/insert_sorting.py +++ b/vorlesung/02_elementares_sortieren/insert_sorting.py @@ -58,4 +58,4 @@ def swap(z: MemoryArray, i: int, j: int): if __name__ == '__main__': analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) - analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True) + #analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True) diff --git a/vorlesung/03_fortgeschrittenes_sortieren/heap_game.py b/vorlesung/03_fortgeschrittenes_sortieren/heap_game.py new file mode 100644 index 0000000..8523e39 --- /dev/null +++ b/vorlesung/03_fortgeschrittenes_sortieren/heap_game.py @@ -0,0 +1,42 @@ +import random +import pygame +from utils.game import Game +from utils.memory_array import MemoryArray +from utils.literal import Literal +from heap_sorting import heap_sort_stepwise + +WHITE = (255, 255, 255) +BLUE = (0, 0, 255) + +class HeapGame(Game): + + def __init__(self): + super().__init__("Heap Game", fps=20, size=(400, 400)) + random.seed() + l =list(range(1, 101)) + random.shuffle(l) + self.z = MemoryArray(l) + self.finished = False + self.sort_generator = heap_sort_stepwise(self.z) + + def update_game(self): + if not self.finished: + try: + next(self.sort_generator) + except StopIteration: + self.finished = True + return True + + def draw_game(self): + self.screen.fill(WHITE) + for i, cell in enumerate(self.z): + x = 50 + i*3 + y = 350 - cell.value * 3 + pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3)) + super().draw_game() + + +if __name__ == "__main__": + sort_game = HeapGame() + sort_game.run() + diff --git a/vorlesung/03_fortgeschrittenes_sortieren/heap_sorting.py b/vorlesung/03_fortgeschrittenes_sortieren/heap_sorting.py new file mode 100644 index 0000000..3400789 --- /dev/null +++ b/vorlesung/03_fortgeschrittenes_sortieren/heap_sorting.py @@ -0,0 +1,93 @@ +from utils.memory_array import MemoryArray +from utils.memory_cell import MemoryCell +from utils.memory_manager import MemoryManager +from utils.memory_range import mrange +from utils.literal import Literal + +def heap_sort_stepwise(z: MemoryArray): + n = z.length() + yield from make_max_heap(z) + with MemoryCell(n) as heapsize: + for i in mrange(n, 1, -1): + swap(z, 0, i.pred()) + yield z + heapsize.set(heapsize.pred()) + yield from max_heapyfy(z, Literal(1), heapsize) + + +def heap_sort(z: MemoryArray): + sort_generator = heap_sort_stepwise(z) + while True: + try: + next(sort_generator) + except StopIteration: + break + + +def left_child(i: Literal): + return Literal(2 * int(i)) + + +def right_child(i: Literal): + return Literal(2 * int(i) + 1) + + +def adjust_index(i: Literal): + return i.pred() + + +def make_max_heap(z: MemoryArray): + n = z.length() + for i in mrange(int(n) // 2, 0, -1): + yield from max_heapyfy(z, i, n) + + +def max_heapyfy(z: MemoryArray, i: Literal, heapsize: Literal): + l = left_child(i) + r = right_child(i) + with MemoryCell(i) as max_value: + if l <= heapsize and z[adjust_index(l)] > z[adjust_index(i)]: + max_value.set(l) + if r <= heapsize and z[adjust_index(r)] > z[adjust_index(max_value)]: + max_value.set(r) + if max_value != i: + swap(z, int(i)-1, int(max_value)-1) + yield z + yield from max_heapyfy(z, max_value, heapsize) + + +def sort_file(filename, sort_func): + z = MemoryArray.create_array_from_file(filename) + sort_func(z) + return z + + +def analyze_complexity(sort_func, sizes, presorted=False): + """ + Analysiert die Komplexität einer Sortierfunktion. + + :param sort_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 + if presorted: + random_array = MemoryArray.create_sorted_array(size) + else: + random_array = MemoryArray.create_random_array(size, -100, 100) + sort_func(random_array) + MemoryManager.save_stats(size) + + MemoryManager.plot_stats(["cells", "compares", "writes"]) + + +def swap(z: MemoryArray, i: int, j: int): + tmp = z[Literal(i)].value + z[Literal(i)] = z[Literal(j)] + z[Literal(j)].set(tmp) + + +if __name__ == '__main__': + sizes = range(10, 101, 10) + analyze_complexity(heap_sort, sizes) + # analyze_complexity(quick_sort, sizes, True) diff --git a/vorlesung/03_fortgeschrittenes_sortieren/quick_game.py b/vorlesung/03_fortgeschrittenes_sortieren/quick_game.py new file mode 100644 index 0000000..dd2f630 --- /dev/null +++ b/vorlesung/03_fortgeschrittenes_sortieren/quick_game.py @@ -0,0 +1,42 @@ +import random +import pygame +from utils.game import Game +from utils.memory_array import MemoryArray +from utils.literal import Literal +from quick_sorting import quick_sort_stepwise + +WHITE = (255, 255, 255) +BLUE = (0, 0, 255) + +class QuickGame(Game): + + def __init__(self): + super().__init__("Quick Game", fps=10, size=(400, 400)) + random.seed() + l =list(range(1, 101)) + random.shuffle(l) + self.z = MemoryArray(l) + self.finished = False + self.sort_generator = quick_sort_stepwise(self.z, Literal(0), Literal(self.z.length().pred())) + + def update_game(self): + if not self.finished: + try: + next(self.sort_generator) + except StopIteration: + self.finished = True + return True + + def draw_game(self): + self.screen.fill(WHITE) + for i, cell in enumerate(self.z): + x = 50 + i*3 + y = 350 - cell.value * 3 + pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3)) + super().draw_game() + + +if __name__ == "__main__": + sort_game = QuickGame() + sort_game.run() + diff --git a/vorlesung/03_fortgeschrittenes_sortieren/quick_sorting.py b/vorlesung/03_fortgeschrittenes_sortieren/quick_sorting.py new file mode 100644 index 0000000..c34b990 --- /dev/null +++ b/vorlesung/03_fortgeschrittenes_sortieren/quick_sorting.py @@ -0,0 +1,82 @@ +from utils.memory_array import MemoryArray +from utils.memory_cell import MemoryCell +from utils.memory_manager import MemoryManager +from utils.memory_range import mrange +from utils.literal import Literal + +def quick_sort_stepwise(z: MemoryArray, l: Literal, r: Literal): + n = z.length() + if l < r: + q = partition(z, l, r) + yield z + yield from quick_sort_stepwise(z, l, q.pred()) + yield from quick_sort_stepwise(z, q.succ(), r) + yield z + + +def partition(z: MemoryArray, l: Literal, r: Literal): + 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 quick_sort(z: MemoryArray, l: Literal = None, r: Literal = None): + if l is None: + l = Literal(0) + if r is None: + r = z.length().pred() + sort_generator = quick_sort_stepwise(z, l, r) + while True: + try: + next(sort_generator) + except StopIteration: + break + + +def sort_file(filename, sort_func): + z = MemoryArray.create_array_from_file(filename) + sort_func(z) + return z + + +def analyze_complexity(sort_func, sizes, presorted=False): + """ + Analysiert die Komplexität einer Sortierfunktion. + + :param sort_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 + if presorted: + random_array = MemoryArray.create_sorted_array(size) + else: + random_array = MemoryArray.create_random_array(size, -100, 100) + sort_func(random_array) + MemoryManager.save_stats(size) + + MemoryManager.plot_stats(["cells", "compares", "writes"]) + + +def swap(z: MemoryArray, i: int, j: int): + tmp = z[Literal(i)].value + z[Literal(i)] = z[Literal(j)] + z[Literal(j)].set(tmp) + + +if __name__ == '__main__': + sizes = range(10, 101, 10) + analyze_complexity(quick_sort, sizes) + # analyze_complexity(quick_sort, sizes, True)