Lecture 7

This commit is contained in:
Oliver Hofmann 2024-06-13 13:57:30 +02:00
parent 00466b6ad7
commit 242b7fdc34
4 changed files with 139 additions and 10 deletions

View File

@ -0,0 +1,52 @@
from SoSe24.algodat.foundation import AlgoDatArray
class AlgoDatHeap():
def __init__(self, values: AlgoDatArray):
self.heap = values
self.size = len(values)
def left_child(self, i):
return 2*i + 1
def right_child(self, i):
return 2*i + 2
def parent(self, i):
return (i-1)//2
def max_heapify(self, i):
l = self.left_child(i)
r = self.right_child(i)
largest = i
if l <= self.size and self.heap[l-1] > self.heap[i-1]:
largest = l
if r <= self.size and self.heap[r-1] > self.heap[largest-1]:
largest = r
if largest != i:
self.heap[i-1], self.heap[largest-1] = self.heap[largest-1], self.heap[i-1]
self.max_heapify(largest)
def min_heapify(self, i):
l = self.left_child(i)
r = self.right_child(i)
smallest = i
if l <= self.size and self.heap[l-1] < self.heap[i-1]:
smallest = l
if r <= self.size and self.heap[r-1] < self.heap[smallest-1]:
smallest = r
if smallest != i:
self.heap[i-1], self.heap[smallest-1] = self.heap[smallest-1], self.heap[i-1]
self.min_heapify(smallest)
def build_max_heap(self):
for i in range(self.size//2, 0, -1):
self.max_heapify(i)
def build_min_heap(self):
for i in range(self.size//2, 0, -1):
self.min_heapify(i)
def as_list(self):
return [self.heap[i].value for i in range(self.size)]

View File

@ -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()

View File

@ -39,6 +39,8 @@ class Graph:
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
raise NotImplementedError("Please implement this method in subclass")
def all_edges(self) -> List[tuple[str, str, float]]:
raise NotImplementedError("Please implement this method in subclass")
def bfs(self, start_name: str):
"""
@ -124,6 +126,12 @@ class AdjacencyListGraph(Graph):
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
return self.adjacency_map[name]
def all_edges(self) -> List[tuple[str, str, float]]:
result = []
for name in self.adjacency_map:
for (dest, weight) in self.adjacency_map[name]:
result.append((name, dest.value, weight))
return result
@ -173,7 +181,13 @@ class AdjacencyMatrixGraph(Graph):
result.append((self.get_vertex(name), self.adjacency_matrix[index][i]))
return result
def all_edges(self) -> List[tuple[str, str, float]]:
result = []
for i in range(len(self.vertex_list)):
for j in range(len(self.vertex_list)):
if self.adjacency_matrix[i][j] is not None:
result.append((self.vertex_list[i].value, self.vertex_list[j].value, self.adjacency_matrix[i][j]))
return result

View File

@ -1,13 +1,16 @@
import math
import re
from graph import Graph, AdjacencyListGraph, AdjacencyMatrixGraph, NodeColor, Vertex
from SoSe24.lec06_graph.disjoint import DisjointValue
from graph import Graph, AdjacencyListGraph, AdjacencyMatrixGraph, Vertex
import heapq
def mst_prim(self, start_name: str = None):
""" Compute the minimum spanning tree of the graph using Prim's algorithm. """
distance_map = {}
parent_map = {}
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]
@ -20,26 +23,60 @@ def mst_prim(self, start_name: str = None):
for vertex in self.all_vertices():
distance_map[vertex] = 0 if vertex.value == start_name else math.inf
parent_map[vertex] = None
heapq.heappush(queue, vertex)
queue.append(vertex)
heapq.heapify(queue) # Convert the list into a heap
# Process the queue
cost = 0
cost = 0 # The cost of the minimum spanning tree
while len(queue) > 0:
vertex = heapq.heappop(queue)
cost += distance_map[vertex]
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
heapq.heappush(queue, dest)
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
AdjacencyListGraph.mst_prim = mst_prim
AdjacencyMatrixGraph.mst_prim = mst_prim
AdjacencyListGraph.mst_kruskal = mst_kruskal
AdjacencyMatrixGraph.mst_kruskal = mst_kruskal
def read_elektro_into_graph(graph: Graph, filename: str):
pattern = re.compile(r'"([^"]+)";"([^"]+)";(\d+)')
@ -59,8 +96,16 @@ if __name__ == "__main__":
graph = AdjacencyMatrixGraph()
read_elektro_into_graph(graph, "../../elektro.txt")
parents, cost = graph.mst_prim()
print(f"Kosten {cost}")
print(f"Kosten nach Prim: {cost}")
for node, parent in parents.items():
if parent is not None:
print(f"{node} - {parent}")
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}")