Musterlösung Praktikum 7
This commit is contained in:
parent
646b9c6881
commit
c478317d76
89
praktika/07_graph/aufgabe1_bfs.py
Normal file
89
praktika/07_graph/aufgabe1_bfs.py
Normal 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.
|
||||||
52
praktika/07_graph/aufgabe2_dfs.py
Normal file
52
praktika/07_graph/aufgabe2_dfs.py
Normal 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|)
|
||||||
Loading…
x
Reference in New Issue
Block a user