Musterlösung Praktikum 7

This commit is contained in:
Oliver Hofmann 2026-06-08 10:35:47 +02:00
parent 646b9c6881
commit c478317d76
2 changed files with 141 additions and 0 deletions

View File

@ -0,0 +1,89 @@
import time
import sys
import os
from vorlesung.L08_graphen.graph import AdjacencyListGraph
GRID = [
"Sabqponm",
"abcryxxl",
"accszExk",
"acctuvwj",
"abdefghi",
]
def height(ch):
if ch == 'S': return ord('a')
if ch == 'E': return ord('z')
return ord(ch)
def build_graph(grid):
rows = len(grid)
cols = len(grid[0])
g = AdjacencyListGraph()
for r in range(rows):
for c in range(cols):
g.insert_vertex(f"{r}_{c}")
for r in range(rows):
for c in range(cols):
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols:
if height(grid[nr][nc]) - height(grid[r][c]) <= 1:
g.connect(f"{r}_{c}", f"{nr}_{nc}")
return g, rows, cols
def find_start_end(grid):
start = end = None
for r, row in enumerate(grid):
for c, ch in enumerate(row):
if ch == 'S': start = f"{r}_{c}"
if ch == 'E': end = f"{r}_{c}"
return start, end
def solve(grid, label="Beispielgelände"):
g, rows, cols = build_graph(grid)
start, end = find_start_end(grid)
t0 = time.perf_counter()
dist_map, pred_map = g.bfs(start)
t1 = time.perf_counter()
pfad = g.path(end, pred_map)
def fmt(node):
r, c = node.split("_")
return f"({r},{c})"
print(f"=== {label} ===")
print(f"|V| = {rows * cols}, |E| <= {rows * cols * 4} (real |E| gezählt separat)")
print(f"Pfadlänge: {len(pfad) - 1} Schritte")
print(f"Pfad: {' -> '.join(fmt(n) for n in pfad)}")
print(f"BFS-Laufzeit: {(t1 - t0) * 1000:.4f} ms")
print()
return rows * cols, t1 - t0
# --- a) Modellierung ---------------------------------------------------------
# Adjazenzliste ist besser geeignet: Das Grid ist licht (sparse).
# Jeder Knoten hat maximal 4 Nachbarn => |E| <= 4|V|.
# Eine Adjazenzmatrix hätte |V|^2 Einträge; für ein 41x122-Grid wären das
# über 25 Millionen Einträge, obwohl nur ~4*|V| davon belegt wären.
# --- b) Pfadsuche -------------------------------------------------------------
v_small, t_small = solve(GRID, "Beispielgelände (8x5)")
# --- c) AoC-Karte (optional) --------------------------------------------------
if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
with open(sys.argv[1]) as f:
aoc_grid = [line.rstrip() for line in f if line.strip()]
v_large, t_large = solve(aoc_grid, f"AoC-Karte ({len(aoc_grid)}x{len(aoc_grid[0])})")
ratio_v = v_large / v_small
ratio_t = t_large / t_small if t_small > 0 else float('inf')
print(f"Verhältnis |V|: {ratio_v:.1f}x")
print(f"Verhältnis Laufzeit: {ratio_t:.1f}x")
print(f"Lineares Wachstum bestätigt: {'ja' if abs(ratio_t / ratio_v - 1) < 0.5 else 'nein'}")
# --- d) Optional: bester Startpunkt (AoC Teil 2) ------------------------------
# Hinweis: Statt BFS von jedem 'a'-Feld zu starten, dreht man den Graphen um
# und startet eine einzige BFS von E rückwärts. Der erste erreichte 'a'-Knoten
# liefert den kürzesten Pfad.

View File

@ -0,0 +1,52 @@
from vorlesung.L08_graphen.graph import AdjacencyListGraph
DEPENDENCIES = {
"main": ["parser", "codegen"],
"parser": ["lexer", "ast"],
"codegen": ["ast", "optimizer"],
"optimizer": ["utils"],
"ast": ["utils"],
"lexer": [],
"utils": [],
}
# --- a) Graph aufbauen -------------------------------------------------------
g = AdjacencyListGraph()
for module in DEPENDENCIES:
g.insert_vertex(module)
for module, deps in DEPENDENCIES.items():
for dep in deps:
g.connect(dep, module) # dep muss vor module gebaut werden
g.graph("BuildDependencies")
# --- b) DFS ausführen --------------------------------------------------------
enter_map, leave_map, pred_map = g.dfs()
print("Zeitmarken (DFS):")
for v in sorted(enter_map, key=lambda v: enter_map[v]):
print(f" {v.value:12s} begin={enter_map[v]:2d} end={leave_map[v]:2d}")
print()
# --- c) Topologische Sortierung ----------------------------------------------
sorted_vertices = sorted(leave_map, key=lambda v: leave_map[v], reverse=True)
reihenfolge = [v.value for v in sorted_vertices]
print("Topologische Reihenfolge (Build-Reihenfolge):")
print(" " + " -> ".join(reihenfolge))
print()
# Überprüfung: Für jede Abhängigkeit A->B muss A nach B in der Liste kommen.
pos = {name: i for i, name in enumerate(reihenfolge)}
ok = all(pos[dep] < pos[module]
for module, deps in DEPENDENCIES.items()
for dep in deps)
print(f"Alle Abhängigkeiten eingehalten: {ok}")
print()
# --- d) Komplexität ----------------------------------------------------------
v_count = len(list(g.all_vertices()))
e_count = sum(len(g.get_adjacent_vertices(v.value)) for v in g.all_vertices())
print(f"|V| = {v_count}, |E| = {e_count}")
# Jeder Knoten wird genau einmal grau (begin) und einmal schwarz (end) => O(|V|)
# Jede Kante wird beim ersten Besuch des Ausgangsknotens genau einmal betrachtet => O(|E|)
# Zusammen: O(|V| + |E|)