Quick Sort and Heap Sort

This commit is contained in:
Oliver Hofmann 2025-04-07 11:58:13 +02:00
parent 32190dc104
commit 27a37037d1
7 changed files with 346 additions and 28 deletions

View File

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

View File

@ -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())

View File

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

View File

@ -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()

View File

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

View File

@ -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()

View File

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