From add4091b5d841c9cb8a521508f3f0e17e1ab73a0 Mon Sep 17 00:00:00 2001 From: Oliver Hofmann Date: Wed, 11 Jun 2025 09:14:53 +0200 Subject: [PATCH] elektro --- praktika/10_electro/electro.py | 32 ++++++++++++++++ vorlesung/L08_graphen/graph.py | 67 +++++++++++++++++++++++++++++++++- vorlesung/L09_mst/__init__.py | 0 vorlesung/L09_mst/disjoint.py | 18 +++++++++ 4 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 praktika/10_electro/electro.py create mode 100644 vorlesung/L09_mst/__init__.py create mode 100644 vorlesung/L09_mst/disjoint.py diff --git a/praktika/10_electro/electro.py b/praktika/10_electro/electro.py new file mode 100644 index 0000000..f80ce97 --- /dev/null +++ b/praktika/10_electro/electro.py @@ -0,0 +1,32 @@ +from vorlesung.L08_graphen.graph import Graph, AdjacencyMatrixGraph +from utils.project_dir import get_path +import re + +def read_elektro_into_graph(graph: Graph, filename: str): + pattern = re.compile(r'"([^"]+)";"([^"]+)";(\d+)') + with (open(filename, "r") as file): + for line in file: + m = pattern.match(line) + if m: + start_name = m.group(1) + end_name = m.group(2) + cost = int(m.group(3)) + graph.insert_vertex(start_name) + graph.insert_vertex(end_name) + graph.connect(start_name, end_name, cost) + graph.connect(end_name, start_name, cost) + +if __name__ == "__main__": + graph = AdjacencyMatrixGraph() + read_elektro_into_graph(graph, get_path("data/elektro.txt")) + + parents, cost = graph.mst_prim() + print(f"Kosten nach Prim: {cost}") + for node, parent in parents.items(): + if parent is not None: + print(f"{node} - {parent}") + + edges, cost = graph.mst_kruskal() + print(f"Kosten nach Kruskal: {cost}") + for start_name, end_name, _ in edges: + print(f"{start_name} - {end_name}") \ No newline at end of file diff --git a/vorlesung/L08_graphen/graph.py b/vorlesung/L08_graphen/graph.py index 9f484ae..327cd8a 100644 --- a/vorlesung/L08_graphen/graph.py +++ b/vorlesung/L08_graphen/graph.py @@ -2,9 +2,12 @@ from collections import deque from typing import List from enum import Enum import graphviz +import math +import heapq from datetime import datetime from utils.project_dir import get_path from utils.priority_queue import PriorityQueue +from vorlesung.L09_mst.disjoint import DisjointValue class NodeColor(Enum): @@ -205,6 +208,68 @@ class Graph: relax(vertex, dest, weight) return distance_map, predecessor_map + def mst_prim(self, start_name: str = None): + """ Compute the minimum spanning tree of the graph using Prim's algorithm. """ + + distance_map = {} # maps vertices to their current distance from the spanning tree + parent_map = {} # maps vertices to their predecessor in the spanning tree + + Vertex.__lt__ = lambda self, other: distance_map[self] < distance_map[other] + + queue = [] + + if start_name is None: + start_name = self.all_vertices()[0].value + + # Initialize the maps + for vertex in self.all_vertices(): + distance_map[vertex] = 0 if vertex.value == start_name else math.inf + parent_map[vertex] = None + queue.append(vertex) + + heapq.heapify(queue) # Convert the list into a heap + + # Process the queue + cost = 0 # The cost of the minimum spanning tree + while len(queue) > 0: + vertex = heapq.heappop(queue) + cost += distance_map[vertex] # Add the cost of the edge to the minimum spanning tree + for (dest, w) in self.get_adjacent_vertices_with_weight(vertex.value): + if dest in queue and distance_map[dest] > w: + # Update the distance and parent maps + queue.remove(dest) + distance_map[dest] = w + parent_map[dest] = vertex + queue.append(dest) # Add the vertex back to the queue + heapq.heapify(queue) # Re-heapify the queue + + # Return the distance and predecessor maps + return parent_map, cost + + def mst_kruskal(self, start_name: str = None): + """ Compute the minimum spanning tree of the graph using Kruskal's algorithm. """ + + cost = 0 + result = [] + edges = self.all_edges() + + # Create a disjoint set for each vertex + vertex_map = {v.value: DisjointValue(v) for v in self.all_vertices()} + + # Sort the edges by weight + edges.sort(key=lambda edge: edge[2]) + + # Process the edges + for edge in edges: + start_name, end_name, weight = edge + # Check if the edge creates a cycle + if not vertex_map[start_name].same_set(vertex_map[end_name]): + result.append(edge) + vertex_map[start_name].union(vertex_map[end_name]) + cost += weight + + return result, cost + class AdjacencyListGraph(Graph): """A graph implemented as an adjacency list.""" @@ -243,8 +308,6 @@ class AdjacencyListGraph(Graph): return result - - class AdjacencyMatrixGraph(Graph): """A graph implemented as an adjacency matrix.""" def __init__(self): diff --git a/vorlesung/L09_mst/__init__.py b/vorlesung/L09_mst/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/vorlesung/L09_mst/disjoint.py b/vorlesung/L09_mst/disjoint.py new file mode 100644 index 0000000..293262e --- /dev/null +++ b/vorlesung/L09_mst/disjoint.py @@ -0,0 +1,18 @@ + + +class DisjointValue(): + + def __init__(self, value): + self.value = value + self.parent = None + + def canonical(self): + if self.parent: + return self.parent.canonical() + return self + + def same_set(self, other): + return self.canonical() == other.canonical() + + def union(self, other): + self.canonical().parent = other.canonical()