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/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)