Compare commits

..

2 Commits

Author SHA1 Message Date
Oliver Hofmann
50cf0b9b52 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	SoSe24/lec04_trees/avl_tree.py
#	SoSe24/lec04_trees/b_tree.py
#	SoSe24/lec05_hash/hash_table.py
2024-05-29 10:05:24 +02:00
Oliver Hofmann
e0cbf69759 Lecture 6 2024-05-29 10:05:06 +02:00
4 changed files with 193 additions and 24 deletions

View File

@ -2,7 +2,6 @@ from SoSe24.algodat.foundation import AlgoDatArray, AlgoDatValue, read_int_seque
from SoSe24.lec04_trees.bin_tree import BinTree, BinTreeNode from SoSe24.lec04_trees.bin_tree import BinTree, BinTreeNode
from time import perf_counter as pfc from time import perf_counter as pfc
class AVLTreeNode(BinTreeNode): class AVLTreeNode(BinTreeNode):
def __init__(self, value: AlgoDatValue): def __init__(self, value: AlgoDatValue):
super().__init__(value) super().__init__(value)

View File

@ -12,7 +12,6 @@ class BTreeNode:
def __str__(self): def __str__(self):
return "(" + " ".join([str(self.keys[i]) for i in range(self.n)]) + ")" return "(" + " ".join([str(self.keys[i]) for i in range(self.n)]) + ")"
class BTree: class BTree:
def __init__(self, m: int): def __init__(self, m: int):
self.m = m self.m = m

View File

@ -4,45 +4,36 @@ from time import perf_counter as pfc
#Goldener Schnitt #Goldener Schnitt
import math import math
a = (math.sqrt(5) - 1) / 2 a = (math.sqrt(5) - 1) / 2
explore_steps = 0
def h(x, m): def h(x, m):
return int(x*a - int(x*a) * m) return int(x*a - int(x*a) * m)
def f(x, i, m): def f(x, i, m):
return (h(x, m) + i + 14*i*i) % m return (h(x, m) + i + 14*i*i) % m
def f1(x, i, m): def f1(x, i, m):
if i % 2 == 0: if i % 2 == 0:
return (h(x, m) + i*i) % m return (h(x, m) + i*i) % m
return ((h(x, m) - i*i) % m + m) % m return ((h(x, m) - i*i) % m + m) % m
class HashTable: class HashTable:
def __init__(self, m, hash_function, exploratory_function=None): def __init__(self, m, h, f=None):
self.m = m self.m = m
self.h = hash_function self.h = h
self.f = exploratory_function self.f = f
self.n = 0
self.table = AlgoDatArray(m) self.table = AlgoDatArray(m)
def insert(self, x): def insert(self, x):
global explore_steps
i = 0 i = 0
while i < self.m: while i < self.m:
j = self.f(x.value, i, self.m) j = self.f(x.value, i, self.m)
if self.is_free(j): if self.is_free(j):
self.table[j].value = x.value self.table[j].value = x.value
self.n += 1
return True return True
i += 1 i += 1
explore_steps += 1
return False return False
def search(self, x): def search(self, x):
global explore_steps
i = 0 i = 0
while i < self.m: while i < self.m:
j = f(x, i, self.m) j = f(x, i, self.m)
@ -51,29 +42,30 @@ class HashTable:
if self.table[j] == None: if self.table[j] == None:
return False return False
i += 1 i += 1
explore_steps += 1
return False return False
def delete(self, x): def delete(self, x):
global explore_steps
i = 0 i = 0
while i < self.m: while i < self.m:
j = f(x, i, self.m) j = f(x, i, self.m)
if self.table[j].value == x: if self.table[j].value == x:
self.table[j].value = "DELETED" self.table[j].value = "DELETED"
self.n -= 1
return True return True
if self.table[j].value is None: if self.table[j].value is None:
return False return False
i += 1 i += 1
explore_steps += 1
return False return False
def __str__(self): def __str__(self):
return str(self.table) return str(self.table)
def alpha(self): def alpha(self):
return self.n / self.m i=0
used = 0
while i < self.m:
used += 0 if self.is_free(i) else 1
i += 1
return used / self.m
def is_free(self, i): def is_free(self, i):
if self.table[i] == None: if self.table[i] == None:
@ -82,20 +74,18 @@ class HashTable:
return True return True
return False return False
if __name__ == "__main__": if __name__ == "__main__":
z = read_int_sequence("../../seq1.txt") z = read_int_sequence("../../seq1.txt")
start = pfc() start = pfc()
hash = HashTable(31, h, f) hash = HashTable(31, h, f)
for algodat_value in z: for i in z:
hash.insert(algodat_value) hash.insert(i)
print(hash) print(hash)
print(f"Alpha: {hash.alpha()}") print(f"Alpha: {hash.alpha()}")
hash.delete(11) hash.delete(34)
hash.search(47) hash.search(47)
hash.search(243) hash.search(243)
print(hash) print(hash)
print(f"Alpha: {hash.alpha()}") print(f"Alpha: {hash.alpha()}")
print(f"Dauer: {pfc() - start:.4f}s") print(f"Dauer: {pfc() - start:.4f}s")
print(f"Sondierungsschritte: {explore_steps}")
AlgoDatValue.summary() AlgoDatValue.summary()

181
SoSe24/lec06_graph/bfs.py Normal file
View File

@ -0,0 +1,181 @@
from collections import deque
from typing import List
import re
from enum import Enum
class NodeColor(Enum):
"""Enumeration for node colors in a graph traversal."""
WHITE = 1 # WHITE: not visited
GRAY = 2 # GRAY: visited but not all neighbors visited
BLACK = 3 # BLACK: visited and all neighbors visited
class Vertex:
"""A vertex in a graph."""
def __init__(self, value):
self.value = value
class Graph:
"""A graph."""
def insert_vertex(self, name: str):
raise NotImplementedError("Please implement this method in subclass")
def connect(self, name1: str, name2: str):
raise NotImplementedError("Please implement this method in subclass")
def all_vertices(self) -> List[Vertex]:
raise NotImplementedError("Please implement this method in subclass")
def get_vertex(self, name: str) -> Vertex:
raise NotImplementedError("Please implement this method in subclass")
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
raise NotImplementedError("Please implement this method in subclass")
def bfs(self, start_name: str):
"""
Perform a breadth-first search starting at the given vertex.
:param start_name: the name of the vertex to start at
:return: a tuple of two dictionaries, the first mapping vertices to distances from the start vertex,
the second mapping vertices to their predecessors in the traversal tree
"""
color_map = {} # maps vertices to their color
distance_map = {} # maps vertices to their distance from the start vertex
predecessor_map = {} # maps vertices to their predecessor in the traversal tree
# Initialize the maps
for vertex in self.all_vertices():
color_map[vertex] = NodeColor.WHITE
distance_map[vertex] = None
predecessor_map[vertex] = None
# Start at the given vertex
start_node = self.get_vertex(start_name)
color_map[start_node] = NodeColor.GRAY
distance_map[start_node] = 0
# Initialize the queue with the start vertex
queue = deque()
queue.append(start_node)
# Process the queue
while len(queue) > 0:
vertex = queue.popleft()
for dest in self.get_adjacent_vertices(vertex.value):
if color_map[dest] == NodeColor.WHITE:
color_map[dest] = NodeColor.GRAY
distance_map[dest] = distance_map[vertex] + 1
predecessor_map[dest] = vertex
queue.append(dest)
color_map[vertex] = NodeColor.BLACK
# Return the distance and predecessor maps
return distance_map, predecessor_map
def path(self, destination, map):
"""
Compute the path from the start vertex to the given destination vertex.
The map parameter is the predecessor map
"""
path = []
destination_node = self.get_vertex(destination)
while destination_node is not None:
path.insert(0, destination_node.value)
destination_node = map[destination_node]
return path
class AdjacencyListGraph(Graph):
"""A graph implemented as an adjacency list."""
def __init__(self):
self.adjacency_map = {} # maps vertex names to lists of adjacent vertices
self.vertex_map = {} # maps vertex names to vertices
def insert_vertex(self, name: str):
if name not in self.vertex_map:
self.vertex_map[name] = Vertex(name)
if name not in self.adjacency_map:
self.adjacency_map[name] = []
def connect(self, name1: str, name2: str):
adjacency_list = self.adjacency_map[name1]
dest = self.vertex_map[name2]
adjacency_list.append(dest)
def all_vertices(self) -> List[Vertex]:
return list(self.vertex_map.values())
def get_vertex(self, name: str) -> Vertex:
return self.vertex_map[name]
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
return self.adjacency_map[name]
class AdjacencyMatrixGraph(Graph):
"""A graph implemented as an adjacency matrix."""
def __init__(self):
self.index_map = {} # maps vertex names to indices
self.vertex_list = [] # list of vertices
self.adjacency_matrix = [] # adjacency matrix
def insert_vertex(self, name: str):
if name not in self.index_map:
self.index_map[name] = len(self.vertex_list)
self.vertex_list.append(Vertex(name))
for row in self.adjacency_matrix: # add a new column to each row
row.append(0)
self.adjacency_matrix.append([0] * len(self.vertex_list)) # add a new row
def connect(self, name1: str, name2: str):
index1 = self.index_map[name1]
index2 = self.index_map[name2]
self.adjacency_matrix[index1][index2] = 1
def all_vertices(self) -> List[Vertex]:
return self.vertex_list
def get_vertex(self, name: str) -> Vertex:
index = self.index_map[name]
return self.vertex_list[index]
def get_adjacent_vertices(self, name: str) -> List[Vertex]:
index = self.index_map[name]
result = []
for i in range(len(self.vertex_list)):
if self.adjacency_matrix[index][i] == 1:
name = self.vertex_list[i].value
result.append(self.get_vertex(name))
return result
def read_cave_into_graph(graph: Graph, filename: str):
"""Read a cave description from a file and insert it into the given graph."""
with open(filename, "r") as file:
lines = file.readlines()
for line in lines:
# match a line with two node names and an optional direction
m = re.match(r"(^\s*\"(.*)\"\s*([<>]*)\s*\"(.*)\"\s*)", line)
if m:
startnode = m.group(2)
endnode = m.group(4)
opcode = m.group(3)
graph.insert_vertex(startnode)
graph.insert_vertex(endnode)
if '>' in opcode:
graph.connect(startnode, endnode)
if '<' in opcode:
graph.connect(endnode, startnode)
if __name__ == "__main__":
graph = AdjacencyListGraph()
#graph = AdjacencyMatrixGraph()
read_cave_into_graph(graph, "../../hoehle.txt")
_, predecessor_map = graph.bfs('Höhleneingang')
path = graph.path('Schatzkammer', predecessor_map)
print(path)
_, predecessor_map = graph.bfs('Schatzkammer')
path = graph.path('Höhleneingang', predecessor_map)
print(path)