Musterlösung P3

This commit is contained in:
Oliver Hofmann 2026-05-04 10:39:08 +02:00
parent f0b82e726a
commit 24d15e1e44
2 changed files with 215 additions and 0 deletions

View File

@ -0,0 +1,127 @@
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
from utils.algo_context import AlgoContext
from utils.algo_int import Int
class PriorityQueue:
"""Max-Heap-basierte Vorrang-Warteschlange mit Operationszählung.
Höhere Prioritätswerte werden zuerst entnommen. Prioritäten werden als
Int-Objekte gespeichert, sodass Vergleiche automatisch im AlgoContext
gezählt werden. Swaps zählen je 2 reads + 2 writes.
"""
def __init__(self, ctx: AlgoContext = None):
self._ctx = ctx if ctx is not None else AlgoContext()
self._heap = [] # Liste von (Int priority, item)
def insert(self, item, priority):
"""Fügt item mit der gegebenen Priorität ein."""
self._heap.append((Int(priority, self._ctx), item))
self._ctx.writes += 1
self._sift_up(len(self._heap) - 1)
def pop(self):
"""Entfernt das Element mit der höchsten Priorität und gibt es zurück."""
if self.is_empty():
raise IndexError("Priority queue is empty")
self._swap(0, len(self._heap) - 1)
self._ctx.reads += 1
_, item = self._heap.pop()
if self._heap:
self._sift_down(0)
return item
def peek(self):
"""Gibt das Element mit der höchsten Priorität zurück (ohne Entfernung)."""
if self.is_empty():
raise IndexError("Priority queue is empty")
self._ctx.reads += 1
return self._heap[0][1]
def is_empty(self):
"""Gibt True zurück, wenn die Warteschlange leer ist."""
return len(self._heap) == 0
def __len__(self):
return len(self._heap)
def __repr__(self):
pairs = sorted(self._heap, key=lambda t: t[0].value, reverse=True)
return "PriorityQueue([" + ", ".join(f"{p.value}:{v}" for p, v in pairs) + "])"
# ── interne Heap-Operationen ───────────────────────────────────────────────
#
# Terminologie-Hinweis: In der Vorlesung heißt die Abwärts-
# Operation MAX-HEAPIFY sie stellt die Heap-Eigenschaft für einen
# Teilbaum wieder her. _sift_down ist funktional identisch damit, folgt
# aber der in der übrigen Literatur gebräuchlicheren Richtungsbezeichnung
# (sift = „sieben/sinken lassen").
#
# _sift_up hat kein direktes Gegenstück in der Vorlesung, weil Heapsort
# nie ein Element einfügt: Dort wird nur nach unten geheapifiziert.
# Für eine vollständige Priority Queue mit insert() wird jedoch auch die
# Aufwärts-Richtung benötigt.
def _swap(self, i, j):
self._ctx.reads += 2
self._ctx.writes += 2
self._heap[i], self._heap[j] = self._heap[j], self._heap[i]
def _sift_up(self, i):
while i > 0:
parent = (i - 1) // 2
if self._heap[i][0] > self._heap[parent][0]: # Int: 2 reads + 1 cmp
self._swap(i, parent)
i = parent
else:
break
def _sift_down(self, i):
n = len(self._heap)
while True:
largest, left, right = i, 2 * i + 1, 2 * i + 2
if left < n and self._heap[left][0] > self._heap[largest][0]:
largest = left
if right < n and self._heap[right][0] > self._heap[largest][0]:
largest = right
if largest == i:
break
self._swap(i, largest)
i = largest
# ── Demo & Auswertung ─────────────────────────────────────────────────────────
if __name__ == '__main__':
import matplotlib.pyplot as plt
import random
SIZES = list(range(10, 210, 10))
metrics = {m: [] for m in ("comparisons", "reads", "writes")}
for n in SIZES:
ctx = AlgoContext()
pq = PriorityQueue(ctx)
for p in random.sample(range(n * 10), n):
pq.insert(None, p)
while not pq.is_empty():
pq.pop()
for m in metrics:
metrics[m].append(getattr(ctx, m))
titles = {"comparisons": "Vergleiche", "reads": "Lesezugriffe", "writes": "Schreibzugriffe"}
fig, axes = plt.subplots(len(metrics), 1, figsize=(10, 9), sharex=True)
for ax, (metric, values) in zip(axes, metrics.items()):
ax.plot(SIZES, values, marker='o', markersize=3)
ax.set_ylabel(titles[metric])
axes[0].set_title("PriorityQueue n inserts + n pops")
axes[-1].set_xlabel("n")
plt.tight_layout()
plt.show()

View File

@ -0,0 +1,88 @@
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
from utils.algo_context import AlgoContext
from utils.algo_array import Array
from vorlesung.L03_fortgeschrittenes_sortieren.quick_sorting import quick_sort, partition
def median3(z: Array, l: int, r: int) -> int:
"""
Gibt den Index des wertmäßig mittleren Elements aus z[l], z[m], z[r] zurück.
Vergleiche werden durch den AlgoContext der Array-Elemente gezählt.
"""
m = (l + r) // 2
a, b, c = z[l], z[m], z[r]
if a <= b:
if b <= c:
return m # a <= b <= c → b ist Median
elif a <= c:
return r # a <= c < b → c ist Median
else:
return l # c < a <= b → a ist Median
else:
if a <= c:
return l # b < a <= c → a ist Median
elif b <= c:
return r # b <= c < a → c ist Median
else:
return m # c < b < a → b ist Median
def partition_median3(z: Array, l: int, r: int, ctx: AlgoContext) -> int:
pivot_idx = median3(z, l, r)
if pivot_idx != r:
z.swap(pivot_idx, r) # Pivot an den rechten Rand
return partition(z, l, r, ctx)
def quick_sort_m3(z: Array, ctx: AlgoContext, l: int = None, r: int = None):
if l is None:
l = 0
if r is None:
r = len(z) - 1
if l < r:
q = partition_median3(z, l, r, ctx)
quick_sort_m3(z, ctx, l, q - 1)
quick_sort_m3(z, ctx, q + 1, r)
if __name__ == '__main__':
import matplotlib.pyplot as plt
SIZES = list(range(10, 210, 10))
algorithms = [
("Quick (klassisch)", quick_sort),
("Quick (Median-3)", quick_sort_m3),
]
comparisons = {name: [] for name, _ in algorithms}
writes = {name: [] for name, _ in algorithms}
for name, sort_func in algorithms:
ctx = AlgoContext()
for n in SIZES:
ctx.reset()
z = Array.random(n, -1000, 1000, ctx)
sort_func(z, ctx)
comparisons[name].append(ctx.comparisons)
writes[name].append(ctx.writes)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
for name in comparisons:
ax1.plot(SIZES, comparisons[name], label=name, marker='o', markersize=3)
ax1.set_ylabel("Vergleiche")
ax1.set_title("Quick Sort: klassisch vs. Median-of-Three")
ax1.legend()
for name in writes:
ax2.plot(SIZES, writes[name], label=name, marker='o', markersize=3)
ax2.set_ylabel("Schreibzugriffe")
ax2.set_xlabel("n")
ax2.legend()
plt.tight_layout()
plt.show()