Umstellen der Auswertungslogik

This commit is contained in:
Oliver Hofmann
2026-03-30 13:09:16 +02:00
parent 228273f399
commit c48b5c7e59
42 changed files with 1690 additions and 1564 deletions
@@ -1,26 +1,22 @@
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.literal import Literal
from utils.algo_context import AlgoContext
from utils.algo_array import Array
from vorlesung.L05_binaere_baeume.avl_tree import AVLTree
def analyze_complexity(sizes):
"""
Analysiert die Komplexität
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
ctx = AlgoContext()
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
tree = AVLTree()
random_array = MemoryArray.create_random_array(size, -100, 100)
for i in range(size-1):
tree.insert(int(random_array[Literal(i)]))
MemoryManager.reset()
tree.insert(int(random_array[Literal(size-1)]))
MemoryManager.save_stats(size)
z = Array.random(size, -100, 100, ctx)
tree = AVLTree(ctx)
for i in range(size - 1):
tree.insert(z[i].value)
ctx.reset()
tree.insert(z[size - 1].value)
ctx.save_stats(size)
ctx.plot_stats(["comparisons"])
MemoryManager.plot_stats(["cells", "compares"])
if __name__ == "__main__":
sizes = range(1, 1001, 2)
analyze_complexity(sizes)
analyze_complexity(sizes)
@@ -1,26 +1,22 @@
from utils.memory_manager import MemoryManager
from utils.memory_array import MemoryArray
from utils.literal import Literal
from utils.algo_context import AlgoContext
from utils.algo_array import Array
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
def analyze_complexity(sizes):
"""
Analysiert die Komplexität
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
ctx = AlgoContext()
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
tree = BinaryTree()
random_array = MemoryArray.create_random_array(size, -100, 100)
for i in range(size-1):
tree.insert(int(random_array[Literal(i)]))
MemoryManager.reset()
tree.insert(int(random_array[Literal(size-1)]))
MemoryManager.save_stats(size)
z = Array.random(size, -100, 100, ctx)
tree = BinaryTree(ctx)
for i in range(size - 1):
tree.insert(z[i].value)
ctx.reset()
tree.insert(z[size - 1].value)
ctx.save_stats(size)
ctx.plot_stats(["comparisons"])
MemoryManager.plot_stats(["cells", "compares"])
if __name__ == "__main__":
sizes = range(1, 1001, 2)
analyze_complexity(sizes)
analyze_complexity(sizes)
+13 -26
View File
@@ -1,16 +1,16 @@
from utils.memory_array import MemoryArray
from vorlesung.L05_binaere_baeume.avl_tree_node import AVLTreeNode
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
import logging
from utils.algo_context import AlgoContext
from utils.algo_array import Array
class AVLTree(BinaryTree):
def __init__(self):
super().__init__()
def __init__(self, ctx: AlgoContext):
super().__init__(ctx)
def new_node(self, value):
return AVLTreeNode(value)
return AVLTreeNode(value, self.ctx)
def balance(self, node: AVLTreeNode):
node.update_balance()
@@ -47,7 +47,6 @@ class AVLTree(BinaryTree):
self.balance(parent)
return node, parent
def delete(self, value):
node, parent = super().delete(value)
if node:
@@ -58,37 +57,25 @@ class AVLTree(BinaryTree):
def graph_filename(self):
return "AVLTree"
if __name__ == "__main__":
ctx = AlgoContext()
tree = AVLTree(ctx)
values = Array.from_file("data/seq2.txt", ctx)
for cell in values:
tree.insert(cell.value)
def print_node(node, indent=0, level=0):
print((indent * 3) * " ", node.value)
tree = AVLTree()
#values = [5, 3, 7, 2, 4, 6, 5, 8]
values = MemoryArray.create_array_from_file("data/seq2.txt")
for value in values:
tree.insert(value)
print("In-order traversal:")
tree.in_order_traversal(print_node)
print("\nLevel-order traversal:")
tree.level_order_traversal(print_node)
print("\nTree structure traversal:")
tree.tree_structure_traversal(print_node)
print("\nGraph traversal:")
tree.graph_traversal()
tree.insert(9)
tree.graph_traversal()
print("\nDeleting 5:")
tree.delete(5)
print("In-order traversal after deletion:")
tree.in_order_traversal(print_node)
print("\nLevel-order traversal after deletion:")
tree.level_order_traversal(print_node)
print("\nTree structure traversal after deletion:")
tree.tree_structure_traversal(print_node)
+13 -9
View File
@@ -1,14 +1,14 @@
import random
import pygame
from utils.game import Game
from utils.algo_context import AlgoContext
from avl_tree import AVLTree
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH = 800
HEIGHT = 400
MARGIN = 20
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH, HEIGHT, MARGIN = 800, 400, 20
class AVLTreeGame(Game):
@@ -18,8 +18,12 @@ class AVLTreeGame(Game):
self.z = list(range(1, 501))
random.shuffle(self.z)
self.finished = False
self.tree = AVLTree()
self.tree.get_height = lambda node: 0 if node is None else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
self.ctx = AlgoContext()
self.tree = AVLTree(self.ctx)
self.tree.get_height = lambda node: (
0 if node is None
else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
)
self.height = self.tree.get_height(self.tree.root)
self.generator = None
@@ -43,7 +47,7 @@ class AVLTreeGame(Game):
super().draw_game()
def draw_tree(self, node, x, y, x_offset):
y_offset = (HEIGHT - (2 * MARGIN)) / self.height
y_offset = (HEIGHT - 2 * MARGIN) / self.height
if node is not None:
pygame.draw.circle(self.screen, BLUE, (x, y), 2)
if node.left is not None:
@@ -53,7 +57,7 @@ class AVLTreeGame(Game):
pygame.draw.line(self.screen, BLACK, (x, y), (x + x_offset, y + y_offset))
self.draw_tree(node.right, x + x_offset, y + y_offset, x_offset // 2)
if __name__ == "__main__":
tree_game = AVLTreeGame()
tree_game.run()
@@ -1,10 +1,13 @@
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
from utils.algo_context import AlgoContext
class AVLTreeNode(BinaryTreeNode):
def __init__(self, value):
super().__init__(value)
def __init__(self, value, ctx: AlgoContext):
super().__init__(value, ctx)
self.parent = None
self.balance = 0
self.balance = 0 # plain int – Metadaten, kein Zähler
def __repr__(self):
return f"TreeNode(id={id(self)} value={self.value}, left={self.left}, right={self.right})"
@@ -13,7 +16,7 @@ class AVLTreeNode(BinaryTreeNode):
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!", xlabel=str(self.balance))
def update_balance(self):
left_height = self.left.height() if self.left else 0
left_height = self.left.height() if self.left else 0
right_height = self.right.height() if self.right else 0
self.balance = right_height - left_height
@@ -58,4 +61,3 @@ class AVLTreeNode(BinaryTreeNode):
def left_right_rotate(self):
self.left = self.left.left_rotate()
return self.right_rotate()
+31 -39
View File
@@ -1,61 +1,53 @@
import random
from utils.memory_array import MemoryArray
from utils.memory_cell import MemoryCell
from utils.memory_manager import MemoryManager
from utils.memory_range import mrange
from utils.literal import Literal
from utils.algo_context import AlgoContext
from utils.algo_array import Array
from utils.algo_int import Int
def binary_search(z: MemoryArray, s: MemoryCell, l: Literal = None, r: Literal = None):
def binary_search(z: Array, s: Int, l: int = None, r: int = None):
"""
Perform a binary search on the sorted array z for the value x.
Binäre Suche auf dem sortierten Array z nach dem Wert s.
l, r – 0-basierte Grenzen (plain int, optional).
Gibt den Index als plain int zurück, oder None wenn nicht gefunden.
"""
if l is None:
l = Literal(0)
l = 0
if r is None:
r = Literal(z.length().pred())
r = len(z) - 1
if l > r:
return None
with MemoryCell(l) as m:
m += r
m //= Literal(2)
if s < z[m]:
return binary_search(z, s, l, m.pred())
elif s > z[m]:
return binary_search(z, s, m.succ(), r)
else:
return m
m = Int((l + r) // 2, s._ctx)
if s < z[m]:
return binary_search(z, s, l, int(m) - 1)
elif s > z[m]:
return binary_search(z, s, int(m) + 1, r)
else:
return int(m)
def analyze_complexity(sizes):
"""
Analysiert die Komplexität
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
"""
ctx = AlgoContext()
for size in sizes:
MemoryManager.purge() # Speicher zurücksetzen
random_array = MemoryArray.create_sorted_array(size)
search_value = random.randint(-100, 100)
binary_search(random_array, MemoryCell(search_value))
MemoryManager.save_stats(size)
MemoryManager.plot_stats(["cells", "compares", "adds"])
ctx.reset()
z = Array.sorted(size, ctx)
search_value = Int(random.randint(0, size - 1), ctx)
binary_search(z, search_value)
ctx.save_stats(size)
ctx.plot_stats(["comparisons", "additions"])
if __name__ == "__main__":
# Example usage
arr = MemoryArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
search_value = MemoryCell(8)
result = binary_search(arr, search_value)
ctx = AlgoContext()
arr = Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ctx)
s = Int(8, ctx)
result = binary_search(arr, s)
if result is not None:
print(f"Value {search_value} found at index {result}.")
print(f"Value {s} found at index {result}.")
else:
print(f"Value {search_value} not found in the array.")
print(f"Value {s} not found.")
sizes = range(1, 1001, 2)
analyze_complexity(sizes)
+33 -66
View File
@@ -1,4 +1,5 @@
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
from utils.algo_context import AlgoContext
from utils.project_dir import get_path
from datetime import datetime
import graphviz
@@ -6,12 +7,13 @@ import graphviz
class BinaryTree:
def __init__(self):
def __init__(self, ctx: AlgoContext):
self.root = None
self.size = 0
self.ctx = ctx
def new_node(self, value):
return BinaryTreeNode(value)
return BinaryTreeNode(value, self.ctx)
def insert(self, value):
self.size += 1
@@ -50,9 +52,6 @@ class BinaryTree:
return None
def delete(self, value):
# Der Wert wird im Baum gesucht und der erste Treffer gelöscht
# Rückgabe falls der Wert gefunden wird:
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
parent = None
current = self.root
value = self.new_node(value)
@@ -64,38 +63,25 @@ class BinaryTree:
parent = current
current = current.right
else:
# Knoten gefunden
break
else:
# Wert nicht gefunden
return None, None
return self.delete_node(current, parent)
def delete_node(self, current, parent):
# Der übergebene Knoten wird
# Rückgabe ist ein Tupel:
# der Knoten, der den zu löschenden Knoten ersetzt und der Elternknoten des gelöschten Knotens
self.size -= 1
# Fall 3: Es gibt zwei Kinder: wir suchen den Nachfolger
# Fall 3: zwei Kinder → Nachfolger suchen
if current.left and current.right:
parent = current
successor = current.right
while successor.left:
parent = successor
successor = successor.left
# Wert des Nachfolgers wird in den Knoten geschrieben, der gelöscht werden soll
current.value = successor.value
# Ab jetzt muss successor gelöscht werden; parent ist bereits richtig gesetzt
current.set(successor) # Wert kopieren (1 read + 1 write)
current = successor
# Ermitteln des einen Kindes (falls es eines gibt), sonst None
# Das eine Kind ist der Ersatz für den Knoten, der gelöscht werden soll
if current.left:
child = current.left
else:
child = current.right
child = current.left if current.left else current.right
# Falls es keinen Elternknoten gibt, ist der Ersatzknoten die Wurzel
if not parent:
self.root = child
return child, None
@@ -106,17 +92,13 @@ class BinaryTree:
parent.right = child
return child, parent
def in_order_traversal(self, callback):
def in_order_traversal_recursive(callback, current):
def _rec(callback, current):
if current is not None:
in_order_traversal_recursive(callback, current.left)
_rec(callback, current.left)
callback(current)
in_order_traversal_recursive(callback, current.right)
in_order_traversal_recursive(callback, self.root)
_rec(callback, current.right)
_rec(callback, self.root)
def level_order_traversal(self, callback):
if self.root is None:
@@ -125,23 +107,19 @@ class BinaryTree:
while queue:
current, level = queue.pop(0)
callback(current, level)
if current.left is not None:
queue.append((current.left, level + 1))
if current.right is not None:
queue.append((current.right, level + 1))
if current.left is not None: queue.append((current.left, level + 1))
if current.right is not None: queue.append((current.right, level + 1))
def tree_structure_traversal(self, callback):
def tree_structure_traversal_recursive(callback, current, level):
def _rec(callback, current, level):
nonlocal line
if current:
tree_structure_traversal_recursive(callback, current.left, level + 1)
_rec(callback, current.left, level + 1)
callback(current, level, line)
line += 1
tree_structure_traversal_recursive(callback, current.right, level + 1)
_rec(callback, current.right, level + 1)
line = 0
tree_structure_traversal_recursive(callback, self.root, 0)
_rec(callback, self.root, 0)
def graph_filename(self):
return "BinaryTree"
@@ -152,55 +130,44 @@ class BinaryTree:
if node is not None:
node.graphviz_rep(level, line, dot)
def graph_traversal_recursive(current):
def _rec(current):
nonlocal dot
if current is not None:
if current.left:
dot.edge(str(id(current)), str(id(current.left)))
graph_traversal_recursive(current.left)
_rec(current.left)
if current.right:
dot.edge(str(id(current)), str(id(current.right)))
graph_traversal_recursive(current.right)
_rec(current.right)
dot = graphviz.Digraph( name="BinaryTree",
engine="neato",
node_attr={"shape": "circle", "fontname": "Arial"},
format="pdf" )
dot = graphviz.Digraph(
name="BinaryTree",
engine="neato",
node_attr={"shape": "circle", "fontname": "Arial"},
format="pdf")
self.tree_structure_traversal(define_node)
graph_traversal_recursive(self.root)
_rec(self.root)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{self.graph_filename()}_{timestamp}.gv"
filename = get_path(filename)
dot.render(filename)
dot.render(get_path(f"{self.graph_filename()}_{timestamp}.gv"))
if __name__ == "__main__":
tree = BinaryTree()
values = [5, 3, 7, 2, 4, 6, 5, 8]
for value in values:
tree.insert(value)
ctx = AlgoContext()
tree = BinaryTree(ctx)
for v in [5, 3, 7, 2, 4, 6, 5, 8]:
tree.insert(v)
def print_node(node, indent=0, line=None):
print((indent * 3) * " ", node.value)
print("In-order traversal:")
tree.in_order_traversal(print_node)
print("\nLevel-order traversal:")
tree.level_order_traversal(print_node)
print("\nTree structure traversal:")
tree.tree_structure_traversal(print_node)
print("\nGraph traversal:")
tree.graph_traversal()
print("\nDeleting 5:")
tree.delete(5)
print("In-order traversal after deletion:")
tree.in_order_traversal(print_node)
print("\nLevel-order traversal after deletion:")
tree.level_order_traversal(print_node)
print("\nTree structure traversal after deletion:")
tree.tree_structure_traversal(print_node)
print("\n", ctx)
+14 -11
View File
@@ -1,14 +1,14 @@
import random
import pygame
from utils.game import Game
from utils.algo_context import AlgoContext
from bin_tree import BinaryTree
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH = 800
HEIGHT = 400
MARGIN = 20
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WIDTH, HEIGHT, MARGIN = 800, 400, 20
class BinTreeGame(Game):
@@ -18,14 +18,17 @@ class BinTreeGame(Game):
self.z = list(range(1, 101))
random.shuffle(self.z)
self.finished = False
self.tree = BinaryTree()
self.tree.get_height = lambda node: 0 if node is None else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
self.ctx = AlgoContext()
self.tree = BinaryTree(self.ctx)
self.tree.get_height = lambda node: (
0 if node is None
else 1 + max(self.tree.get_height(node.left), self.tree.get_height(node.right))
)
self.height = self.tree.get_height(self.tree.root)
def update_game(self):
if not self.finished:
i = self.z.pop()
self.tree.insert(i)
self.tree.insert(self.z.pop())
self.height = self.tree.get_height(self.tree.root)
if len(self.z) == 0:
self.finished = True
@@ -38,7 +41,7 @@ class BinTreeGame(Game):
super().draw_game()
def draw_tree(self, node, x, y, x_offset):
y_offset = (HEIGHT - (2 * MARGIN)) / self.height
y_offset = (HEIGHT - 2 * MARGIN) / self.height
if node is not None:
pygame.draw.circle(self.screen, BLUE, (x, y), 2)
if node.left is not None:
@@ -48,7 +51,7 @@ class BinTreeGame(Game):
pygame.draw.line(self.screen, BLACK, (x, y), (x + x_offset, y + y_offset))
self.draw_tree(node.right, x + x_offset, y + y_offset, x_offset // 2)
if __name__ == "__main__":
tree_game = BinTreeGame()
tree_game.run()
+14 -6
View File
@@ -1,14 +1,22 @@
from utils.memory_cell import MemoryCell
from utils.algo_int import Int
from utils.algo_context import AlgoContext
class BinaryTreeNode(MemoryCell):
def __init__(self, value):
super().__init__(value)
class BinaryTreeNode(Int):
"""
Knoten eines binären Suchbaums.
Erbt von Int – Vergleiche zwischen Knoten werden automatisch im
AlgoContext gezählt.
"""
def __init__(self, value, ctx: AlgoContext):
super().__init__(value, ctx)
self.left = None
self.right = None
def height(self):
left_height = self.left.height() if self.left else 0
left_height = self.left.height() if self.left else 0
right_height = self.right.height() if self.right else 0
return 1 + max(left_height, right_height)
@@ -19,4 +27,4 @@ class BinaryTreeNode(MemoryCell):
return str(self.value)
def graphviz_rep(self, row, col, dot):
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")