Musterlösung P3
This commit is contained in:
parent
f0b82e726a
commit
24d15e1e44
127
praktika/03_quick_and_heap/priority_queue.py
Normal file
127
praktika/03_quick_and_heap/priority_queue.py
Normal 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()
|
||||||
|
|
||||||
88
praktika/03_quick_and_heap/quick_sort_median3.py
Normal file
88
praktika/03_quick_and_heap/quick_sort_median3.py
Normal 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()
|
||||||
Loading…
x
Reference in New Issue
Block a user