Init
This commit is contained in:
commit
4f5a78ac05
8
data/elektro.txt
Normal file
8
data/elektro.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
"Höhleneingang";"Ost/West-Passage";5
|
||||||
|
"Höhleneingang";"Nord/Süd-Passage";3
|
||||||
|
"Nord/Süd-Passage";"Nebelraum";7
|
||||||
|
"Steiniger Pfad";"Ost/West-Passage";2
|
||||||
|
"Ost/West-Passage";"Schwefelgewölbe";4
|
||||||
|
"Schwefelgewölbe";"Steiniger Pfad";1
|
||||||
|
"Schatzkammer";"Nebelraum";2
|
||||||
|
"Steiniger Pfad";"Schatzkammer";6
|
||||||
8
data/hoehle.txt
Normal file
8
data/hoehle.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
"Höhleneingang" <> "Ost/West-Passage"
|
||||||
|
"Höhleneingang" <> "Nord/Süd-Passage"
|
||||||
|
"Nord/Süd-Passage" <> "Nebelraum"
|
||||||
|
"Steiniger Pfad" > "Ost/West-Passage"
|
||||||
|
"Ost/West-Passage" <> "Schwefelgewölbe"
|
||||||
|
"Schwefelgewölbe" > "Steiniger Pfad"
|
||||||
|
"Schatzkammer" > "Nebelraum"
|
||||||
|
"Steiniger Pfad" > "Schatzkammer"
|
||||||
9
data/labyrinth.txt
Normal file
9
data/labyrinth.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
xxxxxxxxxxxxxxxxxxxxx
|
||||||
|
x x
|
||||||
|
x S x
|
||||||
|
x x
|
||||||
|
x xxxxxxxx x
|
||||||
|
x x
|
||||||
|
x x
|
||||||
|
x A x
|
||||||
|
xxxxxxxxxxxxxxxxxxxxx
|
||||||
5
data/labyrinth1.txt
Normal file
5
data/labyrinth1.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
xxxxxAxxxxxxxxx
|
||||||
|
x xSx
|
||||||
|
xxxxxxxxxx xx x
|
||||||
|
x x
|
||||||
|
xxxxxxxxxxxxxxx
|
||||||
14
data/seq0.txt
Normal file
14
data/seq0.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
-59
|
||||||
|
52
|
||||||
|
46
|
||||||
|
14
|
||||||
|
-50
|
||||||
|
58
|
||||||
|
-87
|
||||||
|
-77
|
||||||
|
34
|
||||||
|
15
|
||||||
|
50
|
||||||
|
47
|
||||||
|
51
|
||||||
|
48
|
||||||
100
data/seq1.txt
Normal file
100
data/seq1.txt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
56
|
||||||
|
13
|
||||||
|
97
|
||||||
|
14
|
||||||
|
5
|
||||||
|
40
|
||||||
|
33
|
||||||
|
20
|
||||||
|
51
|
||||||
|
98
|
||||||
|
52
|
||||||
|
89
|
||||||
|
31
|
||||||
|
33
|
||||||
|
47
|
||||||
|
59
|
||||||
|
47
|
||||||
|
75
|
||||||
|
11
|
||||||
|
39
|
||||||
|
0
|
||||||
|
79
|
||||||
|
33
|
||||||
|
57
|
||||||
|
5
|
||||||
|
1
|
||||||
|
28
|
||||||
|
87
|
||||||
|
77
|
||||||
|
54
|
||||||
|
35
|
||||||
|
21
|
||||||
|
24
|
||||||
|
97
|
||||||
|
96
|
||||||
|
94
|
||||||
|
6
|
||||||
|
31
|
||||||
|
-45
|
||||||
|
53
|
||||||
|
-98
|
||||||
|
-44
|
||||||
|
85
|
||||||
|
-76
|
||||||
|
-48
|
||||||
|
-90
|
||||||
|
-99
|
||||||
|
47
|
||||||
|
-11
|
||||||
|
16
|
||||||
|
-98
|
||||||
|
98
|
||||||
|
-23
|
||||||
|
57
|
||||||
|
27
|
||||||
|
-35
|
||||||
|
23
|
||||||
|
65
|
||||||
|
-54
|
||||||
|
96
|
||||||
|
71
|
||||||
|
99
|
||||||
|
81
|
||||||
|
30
|
||||||
|
-7
|
||||||
|
76
|
||||||
|
-22
|
||||||
|
43
|
||||||
|
62
|
||||||
|
-49
|
||||||
|
73
|
||||||
|
59
|
||||||
|
75
|
||||||
|
36
|
||||||
|
39
|
||||||
|
15
|
||||||
|
51
|
||||||
|
-51
|
||||||
|
63
|
||||||
|
69
|
||||||
|
1
|
||||||
|
1
|
||||||
|
-25
|
||||||
|
-18
|
||||||
|
88
|
||||||
|
86
|
||||||
|
93
|
||||||
|
33
|
||||||
|
71
|
||||||
|
-95
|
||||||
|
56
|
||||||
|
2
|
||||||
|
4
|
||||||
|
11
|
||||||
|
-55
|
||||||
|
28
|
||||||
|
60
|
||||||
|
-55
|
||||||
|
-69
|
||||||
|
-97
|
||||||
1000
data/seq2.txt
Normal file
1000
data/seq2.txt
Normal file
File diff suppressed because it is too large
Load Diff
10000
data/seq3.txt
Normal file
10000
data/seq3.txt
Normal file
File diff suppressed because it is too large
Load Diff
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
matplotlib
|
||||||
|
numpy
|
||||||
|
pygame
|
||||||
|
graphviz
|
||||||
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
5
utils/constants.py
Normal file
5
utils/constants.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from utils.literal import Literal
|
||||||
|
|
||||||
|
MAX_VALUE = Literal(99999999999999999999)
|
||||||
|
MIN_VALUE = Literal(-99999999999999999999)
|
||||||
|
|
||||||
53
utils/game.py
Normal file
53
utils/game.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
|
||||||
|
def __init__(self, title, fps=60, size=(640, 400)):
|
||||||
|
self.title = title
|
||||||
|
self.fps = fps
|
||||||
|
self.size = size
|
||||||
|
self.clock = pygame.time.Clock()
|
||||||
|
self.dt = 0
|
||||||
|
self.screen = None
|
||||||
|
|
||||||
|
def init_game(self):
|
||||||
|
pygame.init()
|
||||||
|
pygame.display.set_caption(self.title)
|
||||||
|
self.screen = pygame.display.set_mode(self.size)
|
||||||
|
|
||||||
|
|
||||||
|
def game_loop(self):
|
||||||
|
while True:
|
||||||
|
# Berechnung der Zeitdifferenz seit dem letzten Frame
|
||||||
|
self.dt = self.clock.tick(self.fps) / 1000
|
||||||
|
if self.event_handling() == False:
|
||||||
|
break
|
||||||
|
if self.update_game() == False:
|
||||||
|
break
|
||||||
|
self.draw_game()
|
||||||
|
|
||||||
|
def exit_game(self):
|
||||||
|
pygame.quit()
|
||||||
|
|
||||||
|
def event_handling(self): # bleibt in der Unterklasse unverändert
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if not self.handle_event(event):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def handle_event(self, event): # wird in der Unterklasse überschrieben
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.init_game()
|
||||||
|
self.game_loop()
|
||||||
|
self.exit_game()
|
||||||
|
|
||||||
103
utils/literal.py
Normal file
103
utils/literal.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
class Literal:
|
||||||
|
def __init__(self, value):
|
||||||
|
"""Initialisiert Literal."""
|
||||||
|
if isinstance(value, Literal):
|
||||||
|
self.value = value.value
|
||||||
|
else:
|
||||||
|
self.value = value
|
||||||
|
self.read_count = 0
|
||||||
|
self.compare_count = 0
|
||||||
|
|
||||||
|
def reset_counters(self):
|
||||||
|
"""Setzt alle Zähler auf 0 zurück."""
|
||||||
|
self.read_count = 0
|
||||||
|
self.compare_count = 0
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
"""Liest den Wert aus."""
|
||||||
|
self.read_count += 1
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Vergleicht den Wert mit einem anderen Wert."""
|
||||||
|
if other is None:
|
||||||
|
return False
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value == other.value
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
if other is None:
|
||||||
|
return True
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value != other.value
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value < other.value
|
||||||
|
|
||||||
|
def __le__(self, other):
|
||||||
|
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value <= other.value
|
||||||
|
|
||||||
|
def __gt__(self, other):
|
||||||
|
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value > other.value
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
"""Vergleicht den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
assert isinstance(other, Literal), "Can only compare with Literal or MemoryCell"
|
||||||
|
self.compare_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return self.value >= other.value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Repräsentation des Werts."""
|
||||||
|
return f"{self.value}"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Repräsentation des Werts für Debugging-Zwecke."""
|
||||||
|
return f"Literal(value={self.value}, reads={self.read_count})"
|
||||||
|
|
||||||
|
def get_read_count(self):
|
||||||
|
"""Gibt zurück, wie oft der Wert gelesen wurde."""
|
||||||
|
return self.read_count
|
||||||
|
|
||||||
|
def __int__(self):
|
||||||
|
"""Gibt den Wert als Integer zurück."""
|
||||||
|
self.read_count += 1
|
||||||
|
return int(self.value)
|
||||||
|
|
||||||
|
def succ(self):
|
||||||
|
return Literal(self.value+1)
|
||||||
|
|
||||||
|
def pred(self):
|
||||||
|
return Literal(self.value-1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
l1 = Literal(5)
|
||||||
|
l2 = Literal(3)
|
||||||
|
print(l1 == l2)
|
||||||
|
print(l1 > l2)
|
||||||
|
|
||||||
126
utils/memory_array.py
Normal file
126
utils/memory_array.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.project_dir import get_path
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
class MemoryArray:
|
||||||
|
|
||||||
|
def __init__(self, parm):
|
||||||
|
if isinstance(parm, Literal):
|
||||||
|
self.init_with_size(parm)
|
||||||
|
elif isinstance(parm, list):
|
||||||
|
self.size = len(parm)
|
||||||
|
self.cells = [MemoryCell(value) for value in parm]
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid parameter type")
|
||||||
|
|
||||||
|
def init_with_size(self, size):
|
||||||
|
"""Initialisiert ein Speicherarray mit einer bestimmten Größe."""
|
||||||
|
assert isinstance(size, Literal), "Size must be a Literal or MemoryCell"
|
||||||
|
assert isinstance(size.value, int), "Size must be an int"
|
||||||
|
assert size.value > 0, "Size must be positive"
|
||||||
|
self.size = size.value
|
||||||
|
self.cells = [MemoryCell() for _ in range(self.size)]
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
"""Gibt den Wert einer Speicherzelle zurück."""
|
||||||
|
assert isinstance(index, Literal), "Index must be a Literal or MemoryCell"
|
||||||
|
assert isinstance(index.value, int), "Index value must be an int"
|
||||||
|
assert 0 <= index.value < self.size, "Index out of bounds"
|
||||||
|
return self.cells[index.value]
|
||||||
|
|
||||||
|
def __setitem__(self, index, value):
|
||||||
|
"""Setzt den Wert einer Speicherzelle."""
|
||||||
|
assert isinstance(index, Literal), "Index must be a Literal or MemoryCell"
|
||||||
|
assert isinstance(index.value, int), "Index value must be an int"
|
||||||
|
assert 0 <= index.value < self.size, "Index out of bounds"
|
||||||
|
assert isinstance(value, Literal), "Value must be a Literal or MemoryCell"
|
||||||
|
self.cells[index.value].set(value.value)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Gibt die Größe des Speicherarrays zurück."""
|
||||||
|
return self.size
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Gibt eine Liste der Speicherzellen zurück."""
|
||||||
|
return str([cell.value for cell in self.cells])
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Gibt einen Iterator über die Speicherzellen zurück."""
|
||||||
|
return iter(self.cells)
|
||||||
|
|
||||||
|
def indices(self):
|
||||||
|
"""Gibt eine Liste der Indizes der Speicherzellen zurück."""
|
||||||
|
return [Literal(i) for i in range(self.size)]
|
||||||
|
|
||||||
|
def length(self):
|
||||||
|
"""Gibt die Größe des Speicherarrays zurück."""
|
||||||
|
return Literal(self.size)
|
||||||
|
|
||||||
|
def count_compares(self):
|
||||||
|
return sum([cell.compare_count for cell in self.cells])
|
||||||
|
|
||||||
|
|
||||||
|
def reset_counters(self):
|
||||||
|
"""Setzt alle Zähler auf 0 zurück."""
|
||||||
|
for cell in self.cells:
|
||||||
|
cell.reset_counters()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_random_array(count, min_value, max_value):
|
||||||
|
"""Erzeugt ein zufälliges Speicherarray."""
|
||||||
|
size = Literal(count)
|
||||||
|
a = MemoryArray(size)
|
||||||
|
for i in a.indices():
|
||||||
|
a[i] = Literal(randint(min_value, max_value))
|
||||||
|
a.reset_counters()
|
||||||
|
return a
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_sorted_array(count):
|
||||||
|
"""Erzeugt ein sortiertes Speicherarray."""
|
||||||
|
a = MemoryArray(list(range(count)))
|
||||||
|
a.reset_counters()
|
||||||
|
return a
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_array_from_file(filename, limit=None):
|
||||||
|
"""Erzeugt ein Speicherarray aus einer Datei."""
|
||||||
|
filename = get_path(filename)
|
||||||
|
with open(filename) as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
if limit is not None:
|
||||||
|
lines = lines[:limit]
|
||||||
|
size = Literal(len(lines))
|
||||||
|
a = MemoryArray(size)
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
a[Literal(i)] = Literal(int(line))
|
||||||
|
a.reset_counters()
|
||||||
|
return a
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
result = "[ "
|
||||||
|
for cell in self.cells:
|
||||||
|
result += str(cell) + ", "
|
||||||
|
result += "]"
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import random
|
||||||
|
size = Literal(5)
|
||||||
|
a = MemoryArray(size)
|
||||||
|
for i in a.indices():
|
||||||
|
a[i] = Literal(random.randint(1,100))
|
||||||
|
print(a)
|
||||||
|
s = MemoryCell(0)
|
||||||
|
for cell in a.cells:
|
||||||
|
s += cell
|
||||||
|
print(s)
|
||||||
|
print(f"Anzahl der Additionen: {MemoryManager.count_adds()}")
|
||||||
|
|
||||||
|
a = MemoryArray.create_array_from_file("data/seq0.txt")
|
||||||
|
print(a)
|
||||||
|
|
||||||
194
utils/memory_cell.py
Normal file
194
utils/memory_cell.py
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.literal import Literal
|
||||||
|
|
||||||
|
class MemoryCell (Literal):
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""Erstellt eine neue Instanz von MemoryCell."""
|
||||||
|
instance = MemoryManager().acquire_cell()
|
||||||
|
if instance is None:
|
||||||
|
instance = super().__new__(cls)
|
||||||
|
MemoryManager().register_cell(instance)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
MemoryManager().release_cell(self)
|
||||||
|
|
||||||
|
def __init__(self, value=None):
|
||||||
|
"""Initialisiert eine Speicherzelle mit optionalem Startwert."""
|
||||||
|
super().__init__(value)
|
||||||
|
self.write_count = 0
|
||||||
|
self.add_count = 0
|
||||||
|
self.sub_count = 0
|
||||||
|
self.mul_count = 0
|
||||||
|
self.div_count = 0
|
||||||
|
self.bitop_count = 0
|
||||||
|
if value is not None:
|
||||||
|
self.write_count +=1
|
||||||
|
else:
|
||||||
|
self.value = 0
|
||||||
|
|
||||||
|
def reset_counters(self):
|
||||||
|
"""Setzt alle Zähler auf 0 zurück."""
|
||||||
|
super().reset_counters()
|
||||||
|
self.write_count = 0
|
||||||
|
self.add_count = 0
|
||||||
|
self.sub_count = 0
|
||||||
|
self.mul_count = 0
|
||||||
|
self.div_count = 0
|
||||||
|
self.bitop_count = 0
|
||||||
|
|
||||||
|
def set(self, new_value):
|
||||||
|
"""Schreibt einen neuen Wert in die Speicherzelle und erhöht den Schreibzähler."""
|
||||||
|
self.write_count += 1
|
||||||
|
if isinstance(new_value, Literal):
|
||||||
|
self.value = new_value.value
|
||||||
|
else:
|
||||||
|
self.value = new_value
|
||||||
|
|
||||||
|
def add(self, other):
|
||||||
|
"""Addiert den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
self.set((self + other).value)
|
||||||
|
|
||||||
|
def sub(self, other):
|
||||||
|
"""Subtrahiert den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
self.set((self - other).value)
|
||||||
|
|
||||||
|
def mul(self, other):
|
||||||
|
"""Multipliziert den Wert der Speicherzelle mit einem anderen Wert."""
|
||||||
|
self.set((self * other).value)
|
||||||
|
|
||||||
|
def div(self, other):
|
||||||
|
"""Dividiert den Wert der Speicherzelle durch einen anderen Wert."""
|
||||||
|
self.set((self // other).value)
|
||||||
|
|
||||||
|
def modulo(self, other):
|
||||||
|
"""Berechnet den Modulo des Wertes der Speicherzelle durch einen anderen Wert."""
|
||||||
|
self.set((self % other).value)
|
||||||
|
|
||||||
|
def lshift(self, other):
|
||||||
|
"""Verschiebt den Wert der Speicherzelle um eine bestimmte Anzahl von Bits nach links."""
|
||||||
|
assert isinstance(other, Literal), "Can only lshift Literal or MemoryCell by MemoryCell"
|
||||||
|
self.bitop_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
self.write_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
self.value <<= other.value
|
||||||
|
|
||||||
|
def rshift(self, other):
|
||||||
|
"""Verschiebt den Wert der Speicherzelle um eine bestimmte Anzahl von Bits nach rechts."""
|
||||||
|
assert isinstance(other, Literal), "Can only rshift Literal or MemoryCell by MemoryCell"
|
||||||
|
self.bitop_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
self.write_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
self.value >>= other.value
|
||||||
|
|
||||||
|
def and_op(self, other):
|
||||||
|
"""Führt ein Bitweise AND auf den Wert der Speicherzelle mit einem anderen Wert aus."""
|
||||||
|
assert isinstance(other, Literal), "Can only and Literal or MemoryCell with MemoryCell"
|
||||||
|
self.bitop_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
self.write_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
self.value &= other.value
|
||||||
|
|
||||||
|
def or_op(self, other):
|
||||||
|
"""Führt ein Bitweise OR auf den Wert der Speicherzelle mit einem anderen Wert aus."""
|
||||||
|
assert isinstance(other, Literal), "Can only or Literal or MemoryCell with MemoryCell"
|
||||||
|
self.bitop_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
self.write_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
self.value |= other.value
|
||||||
|
|
||||||
|
def xor_op(self, other):
|
||||||
|
"""Führt ein Bitweise XOR auf den Wert der Speicherzelle mit einem anderen Wert aus."""
|
||||||
|
assert isinstance(other, Literal), "Can only xor Literal or MemoryCell with MemoryCell"
|
||||||
|
self.bitop_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
self.write_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
self.value ^= other.value
|
||||||
|
|
||||||
|
def get_write_count(self):
|
||||||
|
"""Gibt zurück, wie oft der Wert geschrieben wurde."""
|
||||||
|
return self.write_count
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Repräsentation der Speicherzelle für Debugging-Zwecke."""
|
||||||
|
return f"MemoryCell(value={self.value}, reads={self.read_count}, writes={self.write_count})"
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only add Literal or MemoryCell to MemoryCell"
|
||||||
|
self.add_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value + other.value)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only add Literal or MemoryCell to MemoryCell"
|
||||||
|
self.sub_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value - other.value)
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only mul Literal or MemoryCell with MemoryCell"
|
||||||
|
self.mul_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value * other.value)
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only div Literal or MemoryCell by MemoryCell"
|
||||||
|
self.div_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value / other.value)
|
||||||
|
|
||||||
|
def __floordiv__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only div Literal or MemoryCell by MemoryCell"
|
||||||
|
self.div_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value // other.value)
|
||||||
|
|
||||||
|
def __mod__(self, other):
|
||||||
|
assert isinstance(other, Literal), "Can only div Literal or MemoryCell by MemoryCell"
|
||||||
|
self.div_count += 1
|
||||||
|
self.read_count += 1
|
||||||
|
other.read_count += 1
|
||||||
|
return Literal(self.value % other.value)
|
||||||
|
|
||||||
|
def __iadd__(self, other):
|
||||||
|
self.add(other)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __isub__(self, other):
|
||||||
|
self.sub(other)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __imul__(self, other):
|
||||||
|
self.mul(other)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __itruediv__(self, other):
|
||||||
|
self.set(self // other)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __ifloordiv__(self, other):
|
||||||
|
self.div(other)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
a = MemoryCell(5)
|
||||||
|
b = MemoryCell(3)
|
||||||
|
a += b
|
||||||
|
print(f"Ergebnis: {a}")
|
||||||
|
print(f"a wurde {a.get_read_count()} mal gelesen und {a.get_write_count()} mal geschrieben.")
|
||||||
|
print(f"b wurde {b.get_read_count()} mal gelesen und {b.get_write_count()} mal geschrieben.")
|
||||||
148
utils/memory_manager.py
Normal file
148
utils/memory_manager.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import queue
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryManager:
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
stats = {}
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""Erstellt eine einzige Instanz von MemoryManager."""
|
||||||
|
if cls._instance is None:
|
||||||
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance._initialize() # Eigene Init-Methode, damit __init__ nicht mehrfach läuft
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
"""Initialisiert die Speicherverwaltung (einmalig)."""
|
||||||
|
self.cells = []
|
||||||
|
self._pool = queue.Queue()
|
||||||
|
self._finalizers = {}
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_cells():
|
||||||
|
return len(MemoryManager().cells)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_reads():
|
||||||
|
return sum([cell.read_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_writes():
|
||||||
|
return sum([cell.write_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_compares():
|
||||||
|
return sum([cell.compare_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_adds():
|
||||||
|
return sum([cell.add_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_subs():
|
||||||
|
return sum([cell.sub_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_muls():
|
||||||
|
return sum([cell.mul_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_divs():
|
||||||
|
return sum([cell.div_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_bitops():
|
||||||
|
return sum([cell.bitop_count for cell in MemoryManager().cells])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reset():
|
||||||
|
manager = MemoryManager()
|
||||||
|
for cell in manager.cells:
|
||||||
|
cell.reset_counters()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def purge():
|
||||||
|
MemoryManager._instance = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_stats(count):
|
||||||
|
data = { "cells": MemoryManager.count_cells(),
|
||||||
|
"reads": MemoryManager.count_reads(),
|
||||||
|
"writes": MemoryManager.count_writes(),
|
||||||
|
"compares": MemoryManager.count_compares(),
|
||||||
|
"adds": MemoryManager.count_adds(),
|
||||||
|
"subs": MemoryManager.count_subs(),
|
||||||
|
"muls": MemoryManager.count_muls(),
|
||||||
|
"divs": MemoryManager.count_divs(),
|
||||||
|
"bitops": MemoryManager.count_bitops() }
|
||||||
|
MemoryManager.stats[count] = data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def plot_stats(labels):
|
||||||
|
data = MemoryManager.stats
|
||||||
|
x = list(data.keys())
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(len(labels), 1, figsize=(8, 4 * len(labels)), sharex=True)
|
||||||
|
|
||||||
|
if len(labels) == 1:
|
||||||
|
axes = [axes] # Falls nur ein Plot vorhanden ist, in eine Liste umwandeln
|
||||||
|
|
||||||
|
for ax, l in zip(axes, labels):
|
||||||
|
y = [data[k][l] for k in x]
|
||||||
|
ax.plot(x, y, label=l)
|
||||||
|
ax.set_ylabel(l)
|
||||||
|
ax.legend()
|
||||||
|
|
||||||
|
plt.xlabel("n")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def acquire_cell(self):
|
||||||
|
try:
|
||||||
|
return self._pool.get_nowait()
|
||||||
|
except queue.Empty:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def register_cell(self, cell):
|
||||||
|
self.cells.append(cell)
|
||||||
|
|
||||||
|
def release_cell(self, cell):
|
||||||
|
self._pool.put(cell)
|
||||||
|
|
||||||
|
|
||||||
|
class Testcell:
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
instance = MemoryManager().acquire_cell()
|
||||||
|
if instance is None:
|
||||||
|
instance = super().__new__(cls)
|
||||||
|
MemoryManager().register_cell(instance)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
MemoryManager().release_cell(self)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
# Einfaches Anlegen einer Zelle
|
||||||
|
a = Testcell()
|
||||||
|
print(MemoryManager.count_cells())
|
||||||
|
|
||||||
|
# Anlegen einer Zelle und Beenden des Scopes
|
||||||
|
with Testcell() as b:
|
||||||
|
print(MemoryManager.count_cells())
|
||||||
|
|
||||||
|
print(MemoryManager.count_cells())
|
||||||
|
|
||||||
|
# Reuse einer Zelle
|
||||||
|
c = Testcell()
|
||||||
|
print(MemoryManager.count_cells())
|
||||||
|
|
||||||
29
utils/memory_range.py
Normal file
29
utils/memory_range.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from utils.literal import Literal
|
||||||
|
|
||||||
|
# a generator that yields items instead of returning a list
|
||||||
|
def mrange(parm1, parm2=None, parm3=None):
|
||||||
|
if parm2 is None:
|
||||||
|
start = 0
|
||||||
|
stop = int(parm1)
|
||||||
|
step = 1
|
||||||
|
elif parm3 is None:
|
||||||
|
start = int(parm1)
|
||||||
|
stop = int(parm2)
|
||||||
|
step = 1
|
||||||
|
else:
|
||||||
|
start = int(parm1)
|
||||||
|
stop = int(parm2)
|
||||||
|
step = int(parm3)
|
||||||
|
num = start
|
||||||
|
if step > 0:
|
||||||
|
while num < stop:
|
||||||
|
yield Literal(num)
|
||||||
|
num += step
|
||||||
|
else:
|
||||||
|
while num > stop:
|
||||||
|
yield Literal(num)
|
||||||
|
num += step
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for l in mrange(10):
|
||||||
|
print(l)
|
||||||
40
utils/priority_queue.py
Normal file
40
utils/priority_queue.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import heapq
|
||||||
|
|
||||||
|
class PriorityQueue:
|
||||||
|
def __init__(self):
|
||||||
|
self.heap = []
|
||||||
|
self.entry_finder = {} # map: item -> [priority, item]
|
||||||
|
self.REMOVED = '<removed>'
|
||||||
|
self.counter = 0 # unique sequence count to break ties
|
||||||
|
|
||||||
|
def add_or_update(self, item, priority):
|
||||||
|
if item in self.entry_finder:
|
||||||
|
self.remove(item)
|
||||||
|
count = self.counter
|
||||||
|
entry = [priority, count, item]
|
||||||
|
self.entry_finder[item] = entry
|
||||||
|
heapq.heappush(self.heap, entry)
|
||||||
|
self.counter += 1
|
||||||
|
|
||||||
|
def remove(self, item):
|
||||||
|
entry = self.entry_finder.pop(item)
|
||||||
|
entry[-1] = self.REMOVED # mark as removed
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
while self.heap:
|
||||||
|
priority, count, item = heapq.heappop(self.heap)
|
||||||
|
if item != self.REMOVED:
|
||||||
|
del self.entry_finder[item]
|
||||||
|
return item, priority
|
||||||
|
return None
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pq = PriorityQueue()
|
||||||
|
pq.add_or_update('task1', 1)
|
||||||
|
pq.add_or_update('task2', float('inf'))
|
||||||
|
pq.add_or_update('task3', float('inf'))
|
||||||
|
|
||||||
|
print(pq.pop()) # Should print ('task1', 1)
|
||||||
|
pq.add_or_update('task2', 0) # Update priority of 'task1'
|
||||||
|
print(pq.pop()) # Should print ('task2', 0)
|
||||||
|
print(pq.pop()) # Should print ('task3', 3)
|
||||||
13
utils/project_dir.py
Normal file
13
utils/project_dir.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def get_path(filename) -> Path:
|
||||||
|
this_dir = Path(__file__).resolve().parent
|
||||||
|
project_dir = this_dir.parent
|
||||||
|
return project_dir / filename
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filename = get_path("data/seq0.txt")
|
||||||
|
print(filename)
|
||||||
|
print(filename.resolve())
|
||||||
|
print(filename.is_file())
|
||||||
|
print(filename.exists())
|
||||||
47
utils/test_memory_array.py
Normal file
47
utils/test_memory_array.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
import random
|
||||||
|
|
||||||
|
class TestMemoryArray(TestCase):
|
||||||
|
|
||||||
|
def test_create_array(self):
|
||||||
|
l = random.randint(5,10)
|
||||||
|
size = Literal(l)
|
||||||
|
a = MemoryArray(size)
|
||||||
|
self.assertEqual(len(a), l)
|
||||||
|
|
||||||
|
def test_set_item(self):
|
||||||
|
l = random.randint(5,10)
|
||||||
|
size = Literal(l)
|
||||||
|
a = MemoryArray(size)
|
||||||
|
i = Literal(random.randint(0,l-1))
|
||||||
|
v = Literal(random.randint(1,100))
|
||||||
|
a[i] = v
|
||||||
|
self.assertEqual(a[i].value, v.value)
|
||||||
|
|
||||||
|
def test_get_item(self):
|
||||||
|
l = random.randint(5,10)
|
||||||
|
values = [random.randint(1,100) for _ in range(l)]
|
||||||
|
a = MemoryArray(values)
|
||||||
|
for pos, i in enumerate(a.indices()):
|
||||||
|
self.assertEqual(a[i].value, values[pos])
|
||||||
|
|
||||||
|
def test_reset_counters(self):
|
||||||
|
l = random.randint(5,10)
|
||||||
|
values = [random.randint(1,100) for _ in range(l)]
|
||||||
|
a = MemoryArray(values)
|
||||||
|
for i in a.indices():
|
||||||
|
self.assertEqual(a[i].write_count, 1)
|
||||||
|
a.reset_counters()
|
||||||
|
for i in a.indices():
|
||||||
|
self.assertEqual(a[i].write_count, 0)
|
||||||
|
|
||||||
|
def test_create_random_array(self):
|
||||||
|
a = MemoryArray.create_random_array(10, 1, 100)
|
||||||
|
self.assertEqual(len(a), 10)
|
||||||
|
|
||||||
|
def test_create_array_from_file(self):
|
||||||
|
a = MemoryArray.create_array_from_file("data/seq0.txt")
|
||||||
|
self.assertEqual(len(a), 14)
|
||||||
|
|
||||||
166
utils/test_memory_cell.py
Normal file
166
utils/test_memory_cell.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.literal import Literal
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
class TestMemoryCell(TestCase):
|
||||||
|
|
||||||
|
def test_create_cell(self):
|
||||||
|
v = random.randint(1, 100)
|
||||||
|
cell = MemoryCell(v)
|
||||||
|
self.assertEqual(cell.value, v)
|
||||||
|
self.assertEqual(cell.read_count, 0)
|
||||||
|
self.assertEqual(cell.write_count, 1)
|
||||||
|
self.assertEqual(cell.add_count, 0)
|
||||||
|
self.assertEqual(cell.sub_count, 0)
|
||||||
|
self.assertEqual(cell.mul_count, 0)
|
||||||
|
self.assertEqual(cell.div_count, 0)
|
||||||
|
self.assertEqual(cell.bitop_count, 0)
|
||||||
|
|
||||||
|
def test_cast_cell(self):
|
||||||
|
v = random.randint(1, 100)
|
||||||
|
cell = MemoryCell(v)
|
||||||
|
self.assertEqual(int(cell), v)
|
||||||
|
self.assertEqual(cell.read_count, 1)
|
||||||
|
self.assertEqual(cell.write_count, 1)
|
||||||
|
self.assertEqual(cell.add_count, 0)
|
||||||
|
self.assertEqual(cell.sub_count, 0)
|
||||||
|
self.assertEqual(cell.mul_count, 0)
|
||||||
|
self.assertEqual(cell.div_count, 0)
|
||||||
|
self.assertEqual(cell.bitop_count, 0)
|
||||||
|
|
||||||
|
def test_add(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
|
||||||
|
# "in place" Addition zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
cell1 += cell2
|
||||||
|
self.assertEqual(cell1.value, v1 + v2)
|
||||||
|
self.assertEqual(cell1.add_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(cell1, MemoryCell))
|
||||||
|
self.assertTrue(isinstance(cell2, MemoryCell))
|
||||||
|
|
||||||
|
# Freie Addition zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
result = cell1 + cell2
|
||||||
|
|
||||||
|
self.assertEqual(result.value, v1 + v2)
|
||||||
|
self.assertEqual(cell1.add_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(result, Literal))
|
||||||
|
|
||||||
|
def test_sub(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
|
||||||
|
# "in place" Subtraktion zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
cell1 -= cell2
|
||||||
|
|
||||||
|
self.assertEqual(cell1.value, v1 - v2)
|
||||||
|
self.assertEqual(cell1.sub_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(cell1, MemoryCell))
|
||||||
|
self.assertTrue(isinstance(cell2, MemoryCell))
|
||||||
|
|
||||||
|
# Freie Subtraktion zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
result = cell1 - cell2
|
||||||
|
self.assertEqual(result.value, v1 - v2)
|
||||||
|
self.assertEqual(cell1.sub_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(result, Literal))
|
||||||
|
|
||||||
|
def test_mul(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
|
||||||
|
# "in place" Multiplikation zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
cell1 *= cell2
|
||||||
|
|
||||||
|
self.assertEqual(cell1.value, v1 * v2)
|
||||||
|
self.assertEqual(cell1.mul_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(cell1, MemoryCell))
|
||||||
|
self.assertTrue(isinstance(cell2, MemoryCell))
|
||||||
|
|
||||||
|
# Freie Multiplikation zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
result = cell1 * cell2
|
||||||
|
self.assertEqual(result.value, v1 * v2)
|
||||||
|
self.assertEqual(cell1.mul_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(result, Literal))
|
||||||
|
|
||||||
|
def test_div(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
|
||||||
|
# "in place" Division zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
cell1 //= cell2
|
||||||
|
|
||||||
|
self.assertEqual(cell1.value, v1 // v2)
|
||||||
|
self.assertEqual(cell1.div_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(cell1, MemoryCell))
|
||||||
|
self.assertTrue(isinstance(cell2, MemoryCell))
|
||||||
|
|
||||||
|
# Freie Division zweier MemoryCells
|
||||||
|
cell1 = MemoryCell(v1)
|
||||||
|
cell2 = MemoryCell(v2)
|
||||||
|
result = cell1 // cell2
|
||||||
|
self.assertEqual(result.value, v1 // v2)
|
||||||
|
self.assertEqual(cell1.div_count, 1)
|
||||||
|
self.assertEqual(cell1.read_count, 1)
|
||||||
|
self.assertEqual(cell2.read_count, 1)
|
||||||
|
self.assertTrue(isinstance(result, Literal))
|
||||||
|
|
||||||
|
def test_reset_counters(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
cell = MemoryCell(v1)
|
||||||
|
cell += Literal(v2)
|
||||||
|
|
||||||
|
self.assertEqual(cell.value, v1+v2)
|
||||||
|
self.assertEqual(cell.read_count, 1)
|
||||||
|
self.assertEqual(cell.add_count, 1)
|
||||||
|
self.assertEqual(cell.write_count, 2)
|
||||||
|
|
||||||
|
cell.reset_counters()
|
||||||
|
|
||||||
|
self.assertEqual(cell.value, v1+v2)
|
||||||
|
self.assertEqual(cell.read_count, 0)
|
||||||
|
self.assertEqual(cell.add_count, 0)
|
||||||
|
self.assertEqual(cell.write_count, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_set(self):
|
||||||
|
v1 = random.randint(1, 100)
|
||||||
|
v2 = random.randint(1, 100)
|
||||||
|
|
||||||
|
cell = MemoryCell(v1)
|
||||||
|
cell.set(v2)
|
||||||
|
|
||||||
|
self.assertEqual(cell.value, v2)
|
||||||
|
self.assertEqual(cell.read_count, 0)
|
||||||
|
self.assertEqual(cell.write_count, 2)
|
||||||
|
|
||||||
13
vorlesung/L01_grundlagen/euklid.py
Normal file
13
vorlesung/L01_grundlagen/euklid.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.literal import Literal
|
||||||
|
|
||||||
|
x = MemoryCell(int(input("Erste Zahl: ")))
|
||||||
|
y = MemoryCell(int(input("Zweite Zahl: ")))
|
||||||
|
|
||||||
|
while x > Literal(0):
|
||||||
|
if x < y:
|
||||||
|
x, y = y, x
|
||||||
|
x -= y
|
||||||
|
print(y)
|
||||||
|
|
||||||
|
print(f"Insgesamt gab es {x.sub_count + y.sub_count} Subtraktionen.")
|
||||||
41
vorlesung/L02_elementares_sortieren/bubble_game.py
Normal file
41
vorlesung/L02_elementares_sortieren/bubble_game.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from bubble_sorting import bubble_sort_stepwise
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
|
||||||
|
class BubbleGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Bubble Game", fps=60, size=(400, 400))
|
||||||
|
random.seed()
|
||||||
|
l =list(range(1, 101))
|
||||||
|
random.shuffle(l)
|
||||||
|
self.z = MemoryArray(l)
|
||||||
|
self.finished = False
|
||||||
|
self.sort_generator = bubble_sort_stepwise(self.z)
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
try:
|
||||||
|
next(self.sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
for i, cell in enumerate(self.z):
|
||||||
|
x = 50 + i*3
|
||||||
|
y = 350 - cell.value * 3
|
||||||
|
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
b = BubbleGame()
|
||||||
|
b.run()
|
||||||
|
|
||||||
83
vorlesung/L02_elementares_sortieren/bubble_sorting.py
Normal file
83
vorlesung/L02_elementares_sortieren/bubble_sorting.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
def bubble_sort_stepwise(z: MemoryArray):
|
||||||
|
n = z.length()
|
||||||
|
for i in mrange(n.pred()):
|
||||||
|
for j in mrange(n.pred(), i, -1):
|
||||||
|
if z[j.pred()] > z[j]:
|
||||||
|
swap(z, j, j.pred())
|
||||||
|
yield z
|
||||||
|
|
||||||
|
|
||||||
|
def bubble_sort2_stepwise(z: MemoryArray):
|
||||||
|
n = MemoryCell(z.length())
|
||||||
|
true = Literal(1)
|
||||||
|
false = Literal(0)
|
||||||
|
sortiert = MemoryCell()
|
||||||
|
while True:
|
||||||
|
sortiert.set(true)
|
||||||
|
for i in mrange(n.pred()):
|
||||||
|
if z[i] > z[i.succ()]:
|
||||||
|
swap(z, i, i.succ())
|
||||||
|
sortiert.set(false)
|
||||||
|
yield z
|
||||||
|
n -= Literal(1)
|
||||||
|
if sortiert == true or n <= Literal(1):
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def bubble_sort(z: MemoryArray):
|
||||||
|
sort_generator = bubble_sort_stepwise(z)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def bubble_sort2(z: MemoryArray):
|
||||||
|
sort_generator = bubble_sort2_stepwise(z)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def sort_file(filename, sort_func):
|
||||||
|
z = MemoryArray.create_array_from_file(filename)
|
||||||
|
sort_func(z)
|
||||||
|
return z
|
||||||
|
|
||||||
|
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sort_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
sort_func(random_array)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
# analyze_complexity(bubble_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||||
|
# analyze_complexity(bubble_sort2, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||||
41
vorlesung/L02_elementares_sortieren/insert_game.py
Normal file
41
vorlesung/L02_elementares_sortieren/insert_game.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from insert_sorting import insert_sort_stepwise
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
|
||||||
|
class InsertGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Insert Game", fps=60, size=(400, 400))
|
||||||
|
random.seed()
|
||||||
|
l =list(range(1, 101))
|
||||||
|
random.shuffle(l)
|
||||||
|
self.z = MemoryArray(l)
|
||||||
|
self.finished = False
|
||||||
|
self.sort_generator = insert_sort_stepwise(self.z)
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
try:
|
||||||
|
next(self.sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
for i, cell in enumerate(self.z):
|
||||||
|
x = 50 + i*3
|
||||||
|
y = 350 - cell.value * 3
|
||||||
|
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
b = InsertGame()
|
||||||
|
b.run()
|
||||||
|
|
||||||
61
vorlesung/L02_elementares_sortieren/insert_sorting.py
Normal file
61
vorlesung/L02_elementares_sortieren/insert_sorting.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
def insert_sort_stepwise(z: MemoryArray):
|
||||||
|
n = z.length()
|
||||||
|
j = MemoryCell()
|
||||||
|
elem = MemoryCell()
|
||||||
|
for i in mrange(n):
|
||||||
|
elem.set(z[i])
|
||||||
|
j.set(i)
|
||||||
|
while j > Literal(0) and z[j.pred()] > elem:
|
||||||
|
z[j].set(z[j.pred()])
|
||||||
|
j -= Literal(1)
|
||||||
|
yield z
|
||||||
|
z[j].set(elem)
|
||||||
|
yield z
|
||||||
|
|
||||||
|
|
||||||
|
def insert_sort(z: MemoryArray):
|
||||||
|
sort_generator = insert_sort_stepwise(z)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
def sort_file(filename, sort_func):
|
||||||
|
z = MemoryArray.create_array_from_file(filename)
|
||||||
|
sort_func(z)
|
||||||
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sort_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
sort_func(random_array)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
#analyze_complexity(insert_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||||
41
vorlesung/L02_elementares_sortieren/select_game.py
Normal file
41
vorlesung/L02_elementares_sortieren/select_game.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from select_sorting import select_sort_stepwise
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
|
||||||
|
class SelectGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Select Game", fps=60, size=(400, 400))
|
||||||
|
random.seed()
|
||||||
|
l =list(range(1, 101))
|
||||||
|
random.shuffle(l)
|
||||||
|
self.z = MemoryArray(l)
|
||||||
|
self.finished = False
|
||||||
|
self.sort_generator = select_sort_stepwise(self.z)
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
try:
|
||||||
|
next(self.sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
for i, cell in enumerate(self.z):
|
||||||
|
x = 50 + i*3
|
||||||
|
y = 350 - cell.value * 3
|
||||||
|
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
b = SelectGame()
|
||||||
|
b.run()
|
||||||
|
|
||||||
58
vorlesung/L02_elementares_sortieren/select_sorting.py
Normal file
58
vorlesung/L02_elementares_sortieren/select_sorting.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
def select_sort_stepwise(z: MemoryArray):
|
||||||
|
n = z.length()
|
||||||
|
cur_min = MemoryCell()
|
||||||
|
for i in mrange(n):
|
||||||
|
cur_min.set(i)
|
||||||
|
for j in mrange(i.succ(), n):
|
||||||
|
if z[j] < z[cur_min]:
|
||||||
|
cur_min.set(j)
|
||||||
|
swap(z, i, int(cur_min))
|
||||||
|
yield z
|
||||||
|
|
||||||
|
|
||||||
|
def select_sort(z: MemoryArray):
|
||||||
|
sort_generator = select_sort_stepwise(z)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
def sort_file(filename, sort_func):
|
||||||
|
z = MemoryArray.create_array_from_file(filename)
|
||||||
|
sort_func(z)
|
||||||
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sort_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
sort_func(random_array)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100])
|
||||||
|
# analyze_complexity(select_sort, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], True)
|
||||||
42
vorlesung/L03_fortgeschrittenes_sortieren/heap_game.py
Normal file
42
vorlesung/L03_fortgeschrittenes_sortieren/heap_game.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.literal import Literal
|
||||||
|
from heap_sorting import heap_sort_stepwise
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
|
||||||
|
class HeapGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Heap Game", fps=20, size=(400, 400))
|
||||||
|
random.seed()
|
||||||
|
l =list(range(1, 101))
|
||||||
|
random.shuffle(l)
|
||||||
|
self.z = MemoryArray(l)
|
||||||
|
self.finished = False
|
||||||
|
self.sort_generator = heap_sort_stepwise(self.z)
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
try:
|
||||||
|
next(self.sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
for i, cell in enumerate(self.z):
|
||||||
|
x = 50 + i*3
|
||||||
|
y = 350 - cell.value * 3
|
||||||
|
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sort_game = HeapGame()
|
||||||
|
sort_game.run()
|
||||||
|
|
||||||
93
vorlesung/L03_fortgeschrittenes_sortieren/heap_sorting.py
Normal file
93
vorlesung/L03_fortgeschrittenes_sortieren/heap_sorting.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
def heap_sort_stepwise(z: MemoryArray):
|
||||||
|
n = z.length()
|
||||||
|
yield from make_max_heap(z)
|
||||||
|
with MemoryCell(n) as heapsize:
|
||||||
|
for i in mrange(n, 1, -1):
|
||||||
|
swap(z, 0, i.pred())
|
||||||
|
yield z
|
||||||
|
heapsize.set(heapsize.pred())
|
||||||
|
yield from max_heapyfy(z, Literal(1), heapsize)
|
||||||
|
|
||||||
|
|
||||||
|
def heap_sort(z: MemoryArray):
|
||||||
|
sort_generator = heap_sort_stepwise(z)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def left_child(i: Literal):
|
||||||
|
return Literal(2 * int(i))
|
||||||
|
|
||||||
|
|
||||||
|
def right_child(i: Literal):
|
||||||
|
return Literal(2 * int(i) + 1)
|
||||||
|
|
||||||
|
|
||||||
|
def adjust_index(i: Literal):
|
||||||
|
return i.pred()
|
||||||
|
|
||||||
|
|
||||||
|
def make_max_heap(z: MemoryArray):
|
||||||
|
n = z.length()
|
||||||
|
for i in mrange(int(n) // 2, 0, -1):
|
||||||
|
yield from max_heapyfy(z, i, n)
|
||||||
|
|
||||||
|
|
||||||
|
def max_heapyfy(z: MemoryArray, i: Literal, heapsize: Literal):
|
||||||
|
l = left_child(i)
|
||||||
|
r = right_child(i)
|
||||||
|
with MemoryCell(i) as max_value:
|
||||||
|
if l <= heapsize and z[adjust_index(l)] > z[adjust_index(i)]:
|
||||||
|
max_value.set(l)
|
||||||
|
if r <= heapsize and z[adjust_index(r)] > z[adjust_index(max_value)]:
|
||||||
|
max_value.set(r)
|
||||||
|
if max_value != i:
|
||||||
|
swap(z, int(i)-1, int(max_value)-1)
|
||||||
|
yield z
|
||||||
|
yield from max_heapyfy(z, max_value, heapsize)
|
||||||
|
|
||||||
|
|
||||||
|
def sort_file(filename, sort_func):
|
||||||
|
z = MemoryArray.create_array_from_file(filename)
|
||||||
|
sort_func(z)
|
||||||
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sort_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
sort_func(random_array)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sizes = range(10, 101, 10)
|
||||||
|
analyze_complexity(heap_sort, sizes)
|
||||||
|
# analyze_complexity(quick_sort, sizes, True)
|
||||||
42
vorlesung/L03_fortgeschrittenes_sortieren/quick_game.py
Normal file
42
vorlesung/L03_fortgeschrittenes_sortieren/quick_game.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.literal import Literal
|
||||||
|
from quick_sorting import quick_sort_stepwise
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
|
||||||
|
class QuickGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Quick Game", fps=10, size=(400, 400))
|
||||||
|
random.seed()
|
||||||
|
l =list(range(1, 101))
|
||||||
|
random.shuffle(l)
|
||||||
|
self.z = MemoryArray(l)
|
||||||
|
self.finished = False
|
||||||
|
self.sort_generator = quick_sort_stepwise(self.z, Literal(0), Literal(self.z.length().pred()))
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
try:
|
||||||
|
next(self.sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
for i, cell in enumerate(self.z):
|
||||||
|
x = 50 + i*3
|
||||||
|
y = 350 - cell.value * 3
|
||||||
|
pygame.draw.rect(self.screen, BLUE, (x, y, 3, 3))
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sort_game = QuickGame()
|
||||||
|
sort_game.run()
|
||||||
|
|
||||||
81
vorlesung/L03_fortgeschrittenes_sortieren/quick_sorting.py
Normal file
81
vorlesung/L03_fortgeschrittenes_sortieren/quick_sorting.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
def quick_sort_stepwise(z: MemoryArray, l: Literal, r: Literal):
|
||||||
|
if l < r:
|
||||||
|
q = partition(z, l, r)
|
||||||
|
yield z
|
||||||
|
yield from quick_sort_stepwise(z, l, q.pred())
|
||||||
|
yield from quick_sort_stepwise(z, q.succ(), r)
|
||||||
|
yield z
|
||||||
|
|
||||||
|
|
||||||
|
def partition(z: MemoryArray, l: Literal, r: Literal):
|
||||||
|
with MemoryCell(z[r]) as pivot, MemoryCell(l) as i, MemoryCell(r.pred()) as j:
|
||||||
|
while i < j:
|
||||||
|
while z[i] < pivot:
|
||||||
|
i.set(i.succ())
|
||||||
|
while j > l and z[j] >= pivot:
|
||||||
|
j.set(j.pred())
|
||||||
|
if i < j:
|
||||||
|
swap(z, int(i), int(j))
|
||||||
|
i.set(i.succ())
|
||||||
|
j.set(j.pred())
|
||||||
|
if i == j and z[i] < pivot:
|
||||||
|
i.set(i.succ())
|
||||||
|
if z[i] != pivot:
|
||||||
|
swap(z, int(i), int(r))
|
||||||
|
return Literal(i)
|
||||||
|
|
||||||
|
|
||||||
|
def quick_sort(z: MemoryArray, l: Literal = None, r: Literal = None):
|
||||||
|
if l is None:
|
||||||
|
l = Literal(0)
|
||||||
|
if r is None:
|
||||||
|
r = z.length().pred()
|
||||||
|
sort_generator = quick_sort_stepwise(z, l, r)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
next(sort_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def sort_file(filename, sort_func):
|
||||||
|
z = MemoryArray.create_array_from_file(filename)
|
||||||
|
sort_func(z)
|
||||||
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sort_func, sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sort_func: Die Funktion, die analysiert wird.
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
sort_func(random_array)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sizes = range(10, 101, 5)
|
||||||
|
#analyze_complexity(quick_sort, sizes)
|
||||||
|
analyze_complexity(quick_sort, sizes, True)
|
||||||
60
vorlesung/L04_besondere_sortierverfahren/count_sorting.py
Normal file
60
vorlesung/L04_besondere_sortierverfahren/count_sorting.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def count_sort(a: MemoryArray, b: MemoryArray, k: int):
|
||||||
|
c = MemoryArray(Literal(k + 1))
|
||||||
|
for i in mrange(Literal(k + 1)):
|
||||||
|
c[i].set(Literal(0))
|
||||||
|
|
||||||
|
for j in mrange(a.length()):
|
||||||
|
c[a[j]].set(c[a[j]].succ())
|
||||||
|
|
||||||
|
for i in mrange(Literal(1), Literal(k + 1)):
|
||||||
|
c[i].set(int(c[i]) + int(c[i.pred()]))
|
||||||
|
|
||||||
|
for j in mrange(a.length().pred(), Literal(-1), Literal(-1)):
|
||||||
|
b[c[a[j]].pred()].set(a[j])
|
||||||
|
c[a[j]].set(c[a[j]].pred())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sizes, presorted=False):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität einer Sortierfunktion.
|
||||||
|
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
if presorted:
|
||||||
|
random_array = MemoryArray.create_sorted_array(size, 0, 100)
|
||||||
|
else:
|
||||||
|
random_array = MemoryArray.create_random_array(size, 0, 100)
|
||||||
|
dest_array = MemoryArray(Literal(size))
|
||||||
|
count_sort(random_array, dest_array, 100)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "writes"])
|
||||||
|
|
||||||
|
|
||||||
|
def swap(z: MemoryArray, i: int, j: int):
|
||||||
|
tmp = z[Literal(i)].value
|
||||||
|
z[Literal(i)] = z[Literal(j)]
|
||||||
|
z[Literal(j)].set(tmp)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# Test the count_sort function
|
||||||
|
a = MemoryArray([2, 5, 3, 0, 2, 3, 0, 3])
|
||||||
|
b = MemoryArray(Literal(len(a)))
|
||||||
|
count_sort(a, b, 5)
|
||||||
|
|
||||||
|
sizes = range(10, 101, 10)
|
||||||
|
analyze_complexity(sizes)
|
||||||
|
# analyze_complexity(sizes, True)
|
||||||
1
vorlesung/L05_binaere_baeume/__init__.py
Normal file
1
vorlesung/L05_binaere_baeume/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from vorlesung.L05_binaere_baeume.bin_tree import BinaryTree
|
||||||
26
vorlesung/L05_binaere_baeume/analyze_avl_tree.py
Normal file
26
vorlesung/L05_binaere_baeume/analyze_avl_tree.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.literal import Literal
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sizes = range(1, 1001, 2)
|
||||||
|
analyze_complexity(sizes)
|
||||||
26
vorlesung/L05_binaere_baeume/analyze_binary_tree.py
Normal file
26
vorlesung/L05_binaere_baeume/analyze_binary_tree.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.literal import Literal
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sizes = range(1, 1001, 2)
|
||||||
|
analyze_complexity(sizes)
|
||||||
94
vorlesung/L05_binaere_baeume/avl_tree.py
Normal file
94
vorlesung/L05_binaere_baeume/avl_tree.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
class AVLTree(BinaryTree):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
def new_node(self, value):
|
||||||
|
return AVLTreeNode(value)
|
||||||
|
|
||||||
|
|
||||||
|
def balance(self, node: AVLTreeNode):
|
||||||
|
node.update_balance()
|
||||||
|
if node.balance == -2:
|
||||||
|
if node.left.balance <= 0:
|
||||||
|
node = node.right_rotate()
|
||||||
|
else:
|
||||||
|
node = node.left_right_rotate()
|
||||||
|
elif node.balance == 2:
|
||||||
|
if node.right.balance >= 0:
|
||||||
|
node = node.left_rotate()
|
||||||
|
else:
|
||||||
|
node = node.right_left_rotate()
|
||||||
|
if node.parent:
|
||||||
|
self.balance(node.parent)
|
||||||
|
else:
|
||||||
|
self.root = node
|
||||||
|
|
||||||
|
def insert(self, value):
|
||||||
|
insert_generator = self.insert_stepwise(value)
|
||||||
|
node, parent = None, None
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
node, parent = next(insert_generator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
return node, parent
|
||||||
|
|
||||||
|
def insert_stepwise(self, value):
|
||||||
|
node, parent = super().insert(value)
|
||||||
|
yield None, None
|
||||||
|
node.parent = parent
|
||||||
|
if parent:
|
||||||
|
self.balance(parent)
|
||||||
|
return node, parent
|
||||||
|
|
||||||
|
|
||||||
|
def delete(self, value):
|
||||||
|
node, parent = super().delete(value)
|
||||||
|
if node:
|
||||||
|
node.parent = parent
|
||||||
|
if parent:
|
||||||
|
self.balance(parent)
|
||||||
|
|
||||||
|
def graph_filename(self):
|
||||||
|
return "AVLTree"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
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)
|
||||||
59
vorlesung/L05_binaere_baeume/avl_tree_game.py
Normal file
59
vorlesung/L05_binaere_baeume/avl_tree_game.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from avl_tree import AVLTree
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 400
|
||||||
|
MARGIN = 20
|
||||||
|
|
||||||
|
class AVLTreeGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("AVLTree Game", fps=10, size=(WIDTH, HEIGHT))
|
||||||
|
random.seed()
|
||||||
|
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.height = self.tree.get_height(self.tree.root)
|
||||||
|
self.generator = None
|
||||||
|
|
||||||
|
def update_game(self):
|
||||||
|
if not self.finished:
|
||||||
|
if self.generator is None:
|
||||||
|
self.generator = self.tree.insert_stepwise(self.z.pop())
|
||||||
|
try:
|
||||||
|
next(self.generator)
|
||||||
|
except StopIteration:
|
||||||
|
self.generator = None
|
||||||
|
if self.generator is None and len(self.z) == 0:
|
||||||
|
self.finished = True
|
||||||
|
self.height = self.tree.get_height(self.tree.root)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
if self.height > 0:
|
||||||
|
self.draw_tree(self.tree.root, WIDTH // 2, MARGIN, WIDTH // 4 - MARGIN)
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
def draw_tree(self, node, x, y, x_offset):
|
||||||
|
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:
|
||||||
|
pygame.draw.line(self.screen, BLACK, (x, y), (x - x_offset, y + y_offset))
|
||||||
|
self.draw_tree(node.left, x - x_offset, y + y_offset, x_offset // 2)
|
||||||
|
if node.right is not None:
|
||||||
|
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()
|
||||||
|
|
||||||
61
vorlesung/L05_binaere_baeume/avl_tree_node.py
Normal file
61
vorlesung/L05_binaere_baeume/avl_tree_node.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
||||||
|
|
||||||
|
class AVLTreeNode(BinaryTreeNode):
|
||||||
|
def __init__(self, value):
|
||||||
|
super().__init__(value)
|
||||||
|
self.parent = None
|
||||||
|
self.balance = 0
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"TreeNode(id={id(self)} value={self.value}, left={self.left}, right={self.right})"
|
||||||
|
|
||||||
|
def graphviz_rep(self, row, col, dot):
|
||||||
|
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
|
||||||
|
right_height = self.right.height() if self.right else 0
|
||||||
|
self.balance = right_height - left_height
|
||||||
|
|
||||||
|
def right_rotate(self):
|
||||||
|
new_root = self.left
|
||||||
|
new_root.parent = self.parent
|
||||||
|
self.left = new_root.right
|
||||||
|
if self.left:
|
||||||
|
self.left.parent = self
|
||||||
|
new_root.right = self
|
||||||
|
self.parent = new_root
|
||||||
|
if new_root.parent:
|
||||||
|
if new_root.parent.left is self:
|
||||||
|
new_root.parent.left = new_root
|
||||||
|
else:
|
||||||
|
new_root.parent.right = new_root
|
||||||
|
self.update_balance()
|
||||||
|
new_root.update_balance()
|
||||||
|
return new_root
|
||||||
|
|
||||||
|
def left_rotate(self):
|
||||||
|
new_root = self.right
|
||||||
|
new_root.parent = self.parent
|
||||||
|
self.right = new_root.left
|
||||||
|
if self.right:
|
||||||
|
self.right.parent = self
|
||||||
|
new_root.left = self
|
||||||
|
self.parent = new_root
|
||||||
|
if new_root.parent:
|
||||||
|
if new_root.parent.left is self:
|
||||||
|
new_root.parent.left = new_root
|
||||||
|
else:
|
||||||
|
new_root.parent.right = new_root
|
||||||
|
self.update_balance()
|
||||||
|
new_root.update_balance()
|
||||||
|
return new_root
|
||||||
|
|
||||||
|
def right_left_rotate(self):
|
||||||
|
self.right = self.right.right_rotate()
|
||||||
|
return self.left_rotate()
|
||||||
|
|
||||||
|
def left_right_rotate(self):
|
||||||
|
self.left = self.left.left_rotate()
|
||||||
|
return self.right_rotate()
|
||||||
|
|
||||||
61
vorlesung/L05_binaere_baeume/bin_search.py
Normal file
61
vorlesung/L05_binaere_baeume/bin_search.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def binary_search(z: MemoryArray, s: MemoryCell, l: Literal = None, r: Literal = None):
|
||||||
|
"""
|
||||||
|
Perform a binary search on the sorted array z for the value x.
|
||||||
|
"""
|
||||||
|
if l is None:
|
||||||
|
l = Literal(0)
|
||||||
|
if r is None:
|
||||||
|
r = Literal(z.length().pred())
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sizes):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität
|
||||||
|
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
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"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
if result is not None:
|
||||||
|
print(f"Value {search_value} found at index {result}.")
|
||||||
|
else:
|
||||||
|
print(f"Value {search_value} not found in the array.")
|
||||||
|
|
||||||
|
|
||||||
|
sizes = range(1, 1001, 2)
|
||||||
|
analyze_complexity(sizes)
|
||||||
206
vorlesung/L05_binaere_baeume/bin_tree.py
Normal file
206
vorlesung/L05_binaere_baeume/bin_tree.py
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
||||||
|
from utils.project_dir import get_path
|
||||||
|
from datetime import datetime
|
||||||
|
import graphviz
|
||||||
|
|
||||||
|
|
||||||
|
class BinaryTree:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.root = None
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
def new_node(self, value):
|
||||||
|
return BinaryTreeNode(value)
|
||||||
|
|
||||||
|
def insert(self, value):
|
||||||
|
self.size += 1
|
||||||
|
value = self.new_node(value)
|
||||||
|
if self.root is None:
|
||||||
|
self.root = value
|
||||||
|
return self.root, None
|
||||||
|
else:
|
||||||
|
current = self.root
|
||||||
|
while True:
|
||||||
|
if value < current:
|
||||||
|
if current.left:
|
||||||
|
current = current.left
|
||||||
|
else:
|
||||||
|
current.left = value
|
||||||
|
return current.left, current
|
||||||
|
elif value >= current:
|
||||||
|
if current.right:
|
||||||
|
current = current.right
|
||||||
|
else:
|
||||||
|
current.right = value
|
||||||
|
return current.right, current
|
||||||
|
else:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def search(self, value):
|
||||||
|
current = self.root
|
||||||
|
value = self.new_node(value)
|
||||||
|
while current:
|
||||||
|
if value < current:
|
||||||
|
current = current.left
|
||||||
|
elif value > current:
|
||||||
|
current = current.right
|
||||||
|
else:
|
||||||
|
return current
|
||||||
|
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)
|
||||||
|
while current:
|
||||||
|
if value < current:
|
||||||
|
parent = current
|
||||||
|
current = current.left
|
||||||
|
elif value > current:
|
||||||
|
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
|
||||||
|
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 = 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
|
||||||
|
|
||||||
|
# Falls es keinen Elternknoten gibt, ist der Ersatzknoten die Wurzel
|
||||||
|
if not parent:
|
||||||
|
self.root = child
|
||||||
|
return child, None
|
||||||
|
elif parent.left is current:
|
||||||
|
parent.left = child
|
||||||
|
return child, parent
|
||||||
|
else:
|
||||||
|
parent.right = child
|
||||||
|
return child, parent
|
||||||
|
|
||||||
|
|
||||||
|
def in_order_traversal(self, callback):
|
||||||
|
|
||||||
|
def in_order_traversal_recursive(callback, current):
|
||||||
|
if current is not None:
|
||||||
|
in_order_traversal_recursive(callback, current.left)
|
||||||
|
callback(current)
|
||||||
|
in_order_traversal_recursive(callback, current.right)
|
||||||
|
|
||||||
|
in_order_traversal_recursive(callback, self.root)
|
||||||
|
|
||||||
|
|
||||||
|
def level_order_traversal(self, callback):
|
||||||
|
if self.root is None:
|
||||||
|
return
|
||||||
|
queue = [(self.root, 0)]
|
||||||
|
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))
|
||||||
|
|
||||||
|
def tree_structure_traversal(self, callback):
|
||||||
|
|
||||||
|
def tree_structure_traversal_recursive(callback, current, level):
|
||||||
|
nonlocal line
|
||||||
|
if current:
|
||||||
|
tree_structure_traversal_recursive(callback, current.left, level + 1)
|
||||||
|
callback(current, level, line)
|
||||||
|
line += 1
|
||||||
|
tree_structure_traversal_recursive(callback, current.right, level + 1)
|
||||||
|
|
||||||
|
line = 0
|
||||||
|
tree_structure_traversal_recursive(callback, self.root, 0)
|
||||||
|
|
||||||
|
def graph_filename(self):
|
||||||
|
return "BinaryTree"
|
||||||
|
|
||||||
|
def graph_traversal(self):
|
||||||
|
def define_node(node, level, line):
|
||||||
|
nonlocal dot
|
||||||
|
if node is not None:
|
||||||
|
node.graphviz_rep(level, line, dot)
|
||||||
|
|
||||||
|
def graph_traversal_recursive(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)
|
||||||
|
if current.right:
|
||||||
|
dot.edge(str(id(current)), str(id(current.right)))
|
||||||
|
graph_traversal_recursive(current.right)
|
||||||
|
|
||||||
|
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)
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
filename = f"{self.graph_filename()}_{timestamp}.gv"
|
||||||
|
filename = get_path(filename)
|
||||||
|
dot.render(filename)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tree = BinaryTree()
|
||||||
|
values = [5, 3, 7, 2, 4, 6, 5, 8]
|
||||||
|
|
||||||
|
for value in values:
|
||||||
|
tree.insert(value)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
54
vorlesung/L05_binaere_baeume/bin_tree_game.py
Normal file
54
vorlesung/L05_binaere_baeume/bin_tree_game.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import random
|
||||||
|
import pygame
|
||||||
|
from utils.game import Game
|
||||||
|
from bin_tree import BinaryTree
|
||||||
|
|
||||||
|
WHITE = (255, 255, 255)
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
BLACK = (0, 0, 0)
|
||||||
|
WIDTH = 800
|
||||||
|
HEIGHT = 400
|
||||||
|
MARGIN = 20
|
||||||
|
|
||||||
|
class BinTreeGame(Game):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("BinTree Game", fps=10, size=(WIDTH, HEIGHT))
|
||||||
|
random.seed()
|
||||||
|
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.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.height = self.tree.get_height(self.tree.root)
|
||||||
|
if len(self.z) == 0:
|
||||||
|
self.finished = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def draw_game(self):
|
||||||
|
self.screen.fill(WHITE)
|
||||||
|
if self.height > 0:
|
||||||
|
self.draw_tree(self.tree.root, WIDTH // 2, MARGIN, WIDTH // 4 - MARGIN)
|
||||||
|
super().draw_game()
|
||||||
|
|
||||||
|
def draw_tree(self, node, x, y, x_offset):
|
||||||
|
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:
|
||||||
|
pygame.draw.line(self.screen, BLACK, (x, y), (x - x_offset, y + y_offset))
|
||||||
|
self.draw_tree(node.left, x - x_offset, y + y_offset, x_offset // 2)
|
||||||
|
if node.right is not None:
|
||||||
|
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()
|
||||||
|
|
||||||
22
vorlesung/L05_binaere_baeume/bin_tree_node.py
Normal file
22
vorlesung/L05_binaere_baeume/bin_tree_node.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
|
||||||
|
class BinaryTreeNode(MemoryCell):
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
super().__init__(value)
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
|
||||||
|
def height(self):
|
||||||
|
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)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"TreeNode(value={self.value}, left={self.left}, right={self.right})"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
def graphviz_rep(self, row, col, dot):
|
||||||
|
dot.node(str(id(self)), label=str(self.value), pos=f"{col},{-row}!")
|
||||||
58
vorlesung/L06_b_baeume/analyze_b_tree.py
Normal file
58
vorlesung/L06_b_baeume/analyze_b_tree.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.literal import Literal
|
||||||
|
from b_tree import BTree
|
||||||
|
from b_tree_node import BTreeNode
|
||||||
|
|
||||||
|
class MemoryManagerBTree(MemoryManager):
|
||||||
|
"""
|
||||||
|
Diese Klasse erweitert den MemoryManager, um spezifische Statistiken für B-Bäume zu speichern.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_loads():
|
||||||
|
return sum([cell.loaded_count for cell in MemoryManager().cells if isinstance(cell, BTreeNode)])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def count_saves():
|
||||||
|
return sum([cell.saved_count for cell in MemoryManager().cells if isinstance(cell, BTreeNode)])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_stats(count):
|
||||||
|
data = { "cells": MemoryManager.count_cells(),
|
||||||
|
"reads": MemoryManager.count_reads(),
|
||||||
|
"writes": MemoryManager.count_writes(),
|
||||||
|
"compares": MemoryManager.count_compares(),
|
||||||
|
"adds": MemoryManager.count_adds(),
|
||||||
|
"subs": MemoryManager.count_subs(),
|
||||||
|
"muls": MemoryManager.count_muls(),
|
||||||
|
"divs": MemoryManager.count_divs(),
|
||||||
|
"bitops": MemoryManager.count_bitops(),
|
||||||
|
"loads": MemoryManagerBTree.count_loads(),
|
||||||
|
"saves": MemoryManagerBTree.count_saves() }
|
||||||
|
MemoryManager.stats[count] = data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sizes):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität
|
||||||
|
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
tree = BTree(5)
|
||||||
|
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)]))
|
||||||
|
MemoryManagerBTree.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares", "loads", "saves"])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sizes = range(1, 1001, 2)
|
||||||
|
analyze_complexity(sizes)
|
||||||
120
vorlesung/L06_b_baeume/b_tree.py
Normal file
120
vorlesung/L06_b_baeume/b_tree.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from b_tree_node import BTreeNode
|
||||||
|
|
||||||
|
class BTree:
|
||||||
|
def __init__(self, m: int):
|
||||||
|
self.m = m
|
||||||
|
self.root = BTreeNode(m)
|
||||||
|
|
||||||
|
def search(self, value, start: BTreeNode = None) -> BTreeNode | None:
|
||||||
|
if not start:
|
||||||
|
start = self.root
|
||||||
|
start.load()
|
||||||
|
i = 0
|
||||||
|
if not isinstance(value, MemoryCell):
|
||||||
|
value = MemoryCell(value)
|
||||||
|
while i < start.n and value > start.value[Literal(i)]:
|
||||||
|
i += 1
|
||||||
|
if i < start.n and value == start.value[Literal(i)]:
|
||||||
|
return start
|
||||||
|
if start.leaf:
|
||||||
|
return None
|
||||||
|
return self.search(value, start.children[i])
|
||||||
|
|
||||||
|
def split_child(self, parent: BTreeNode, i: int):
|
||||||
|
child = parent.children[i]
|
||||||
|
child.load()
|
||||||
|
h = BTreeNode(self.m)
|
||||||
|
h.leaf = child.leaf
|
||||||
|
h.n = self.m - 1
|
||||||
|
for j in range(self.m - 1):
|
||||||
|
h.value[Literal(j)] = child.value[Literal(j + self.m)]
|
||||||
|
if not h.leaf:
|
||||||
|
for j in range(self.m):
|
||||||
|
h.children[j] = child.children[j + self.m]
|
||||||
|
for j in range(self.m, child.n + 1):
|
||||||
|
child.children[j] = None
|
||||||
|
child.n = self.m - 1
|
||||||
|
child.save()
|
||||||
|
h.save()
|
||||||
|
for j in range(parent.n, i, -1):
|
||||||
|
parent.children[j + 1] = parent.children[j]
|
||||||
|
parent.value[Literal(j)] = parent.value[Literal(j - 1)]
|
||||||
|
parent.children[i + 1] = h
|
||||||
|
parent.value[Literal(i)] = child.value[Literal(self.m - 1)]
|
||||||
|
parent.n += 1
|
||||||
|
parent.save()
|
||||||
|
|
||||||
|
def insert(self, value):
|
||||||
|
if not isinstance(value, MemoryCell):
|
||||||
|
value = MemoryCell(value)
|
||||||
|
r = self.root
|
||||||
|
if r.n == 2 * self.m - 1:
|
||||||
|
h = BTreeNode(self.m)
|
||||||
|
self.root = h
|
||||||
|
h.leaf = False
|
||||||
|
h.n = 0
|
||||||
|
h.children[0] = r
|
||||||
|
self.split_child(h, 0)
|
||||||
|
self.insert_in_node(h, value)
|
||||||
|
else:
|
||||||
|
self.insert_in_node(r, value)
|
||||||
|
|
||||||
|
def insert_in_node(self, start: BTreeNode, value):
|
||||||
|
start.load()
|
||||||
|
i = start.n
|
||||||
|
if start.leaf:
|
||||||
|
while i >= 1 and value < start.value[Literal(i-1)]:
|
||||||
|
start.value[Literal(i)] = start.value[Literal(i-1)]
|
||||||
|
i -= 1
|
||||||
|
start.value[Literal(i)].set(value)
|
||||||
|
start.n += 1
|
||||||
|
start.save()
|
||||||
|
else:
|
||||||
|
j = 0
|
||||||
|
while j < start.n and value > start.value[Literal(j)]:
|
||||||
|
j += 1
|
||||||
|
if start.children[j].n == 2 * self.m - 1:
|
||||||
|
self.split_child(start, j)
|
||||||
|
if value > start.value[Literal(j)]:
|
||||||
|
j += 1
|
||||||
|
self.insert_in_node(start.children[j], value)
|
||||||
|
|
||||||
|
def traversal(self, callback):
|
||||||
|
def traversal_recursive(node, callback):
|
||||||
|
i = 0
|
||||||
|
while i < node.n:
|
||||||
|
if not node.leaf:
|
||||||
|
traversal_recursive(node.children[i], callback)
|
||||||
|
callback(node.value[Literal(i)])
|
||||||
|
i += 1
|
||||||
|
if not node.leaf:
|
||||||
|
traversal_recursive(node.children[i], callback)
|
||||||
|
|
||||||
|
traversal_recursive(self.root, callback)
|
||||||
|
|
||||||
|
def walk(self):
|
||||||
|
def print_key(key):
|
||||||
|
print(key, end=" ")
|
||||||
|
|
||||||
|
self.traversal(print_key)
|
||||||
|
|
||||||
|
def height(self, start: BTreeNode = None):
|
||||||
|
if not start:
|
||||||
|
start = self.root
|
||||||
|
if start.leaf:
|
||||||
|
return 0
|
||||||
|
return 1 + self.height(start.children[0])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
a = MemoryArray.create_array_from_file("data/seq3.txt")
|
||||||
|
tree = BTree(3)
|
||||||
|
for cell in a:
|
||||||
|
tree.insert(cell)
|
||||||
|
print(f"Height: {tree.height()}")
|
||||||
|
tree.walk()
|
||||||
|
s = tree.search(0)
|
||||||
|
print(f"\nKnoten mit 0: {str(s)}")
|
||||||
29
vorlesung/L06_b_baeume/b_tree_node.py
Normal file
29
vorlesung/L06_b_baeume/b_tree_node.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
|
||||||
|
class BTreeNode(MemoryCell):
|
||||||
|
|
||||||
|
def __init__(self, m: int):
|
||||||
|
super().__init__()
|
||||||
|
self.m = m
|
||||||
|
self.n = 0
|
||||||
|
self.leaf = True
|
||||||
|
self.value = MemoryArray(Literal(2 * m - 1))
|
||||||
|
self.children = [None] * (2 * m)
|
||||||
|
self.loaded_count = 0
|
||||||
|
self.saved_count = 0
|
||||||
|
|
||||||
|
def reset_counters(self):
|
||||||
|
super().reset_counters()
|
||||||
|
self.loaded_count = 0
|
||||||
|
self.saved_count = 0
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
self.loaded_count += 1
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.saved_count += 1
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "(" + " ".join([str(self.value[Literal(i)]) for i in range(self.n)]) + ")"
|
||||||
0
vorlesung/L07_hashtable/__init__.py
Normal file
0
vorlesung/L07_hashtable/__init__.py
Normal file
60
vorlesung/L07_hashtable/analyze_hashtable.py
Normal file
60
vorlesung/L07_hashtable/analyze_hashtable.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import math
|
||||||
|
import random
|
||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.memory_manager import MemoryManager
|
||||||
|
from vorlesung.L07_hashtable.hashtable import HashTableOpenAddressing
|
||||||
|
|
||||||
|
#Goldener Schnitt
|
||||||
|
a = Literal((math.sqrt(5) - 1) / 2)
|
||||||
|
|
||||||
|
# Hashfunktion nach multiplikativer Methode
|
||||||
|
def h(x: MemoryCell, m: Literal) -> Literal:
|
||||||
|
with MemoryCell(int(x * a)) as integer_part, MemoryCell(x * a) as full_product:
|
||||||
|
with MemoryCell(full_product - integer_part) as fractional_part:
|
||||||
|
return Literal(abs(int(fractional_part * m)))
|
||||||
|
|
||||||
|
# Quadratische Sondierung
|
||||||
|
def f(x: MemoryCell, i: Literal, m: Literal) -> Literal:
|
||||||
|
c1 = 1
|
||||||
|
c2 = 5
|
||||||
|
with MemoryCell(h(x, m)) as initial_hash, MemoryCell(c2 * int(i) * int(i)) as quadratic_offset:
|
||||||
|
with MemoryCell(initial_hash + quadratic_offset) as probe_position:
|
||||||
|
probe_position += Literal(c1 * int(i)) # Linear component
|
||||||
|
return probe_position % m
|
||||||
|
|
||||||
|
# Symmetrische quadratische Sondierung
|
||||||
|
def fs(x: MemoryCell, i: Literal, m: Literal) -> Literal:
|
||||||
|
with MemoryCell(h(x, m)) as base_hash, MemoryCell(int(i) * int(i)) as square:
|
||||||
|
if int(i) % 2 == 0: # gerades i: Vorwärtssondierung
|
||||||
|
with MemoryCell(base_hash + square) as position:
|
||||||
|
return position % m
|
||||||
|
else: # ungerades i: Rückwärtssondierung
|
||||||
|
with MemoryCell(base_hash - square) as position:
|
||||||
|
return position % m
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_complexity(sizes):
|
||||||
|
"""
|
||||||
|
Analysiert die Komplexität
|
||||||
|
|
||||||
|
:param sizes: Eine Liste von Eingabegrößen für die Analyse.
|
||||||
|
"""
|
||||||
|
for size in sizes:
|
||||||
|
MemoryManager.purge() # Speicher zurücksetzen
|
||||||
|
ht = HashTableOpenAddressing(size, f)
|
||||||
|
random_array = MemoryArray.create_random_array(size, -100, 100)
|
||||||
|
for cell in random_array:
|
||||||
|
ht.insert(cell)
|
||||||
|
MemoryManager.reset()
|
||||||
|
cell = random.choice(random_array.cells)
|
||||||
|
ht.search(cell)
|
||||||
|
MemoryManager.save_stats(size)
|
||||||
|
|
||||||
|
MemoryManager.plot_stats(["cells", "compares"])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sizes = range(1, 1001, 10)
|
||||||
|
analyze_complexity(sizes)
|
||||||
76
vorlesung/L07_hashtable/hashtable.py
Normal file
76
vorlesung/L07_hashtable/hashtable.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
from utils.literal import Literal
|
||||||
|
from utils.memory_array import MemoryArray
|
||||||
|
from utils.memory_cell import MemoryCell
|
||||||
|
from utils.memory_range import mrange
|
||||||
|
|
||||||
|
|
||||||
|
UNUSED_MARK = "UNUSED"
|
||||||
|
DELETED_MARK = "DELETED"
|
||||||
|
|
||||||
|
class HashTableOpenAddressing:
|
||||||
|
def __init__(self, m: Literal, f: Callable[[MemoryCell, Literal, Literal], Literal]):
|
||||||
|
if not isinstance(m, Literal):
|
||||||
|
m = Literal(m)
|
||||||
|
self.m = m
|
||||||
|
self.f = f
|
||||||
|
self.table = MemoryArray(m)
|
||||||
|
for i in mrange(m):
|
||||||
|
self.table[i].value = UNUSED_MARK
|
||||||
|
|
||||||
|
def insert(self, x: MemoryCell):
|
||||||
|
with MemoryCell(0) as i:
|
||||||
|
while i < self.m:
|
||||||
|
j = self.f(x, i, self.m)
|
||||||
|
if self.is_free(j):
|
||||||
|
self.table[j].set(x)
|
||||||
|
return True
|
||||||
|
i.set(i.succ())
|
||||||
|
return False
|
||||||
|
|
||||||
|
def search(self, x: MemoryCell):
|
||||||
|
with MemoryCell(0) as i:
|
||||||
|
while i < self.m:
|
||||||
|
j = self.f(x, i, self.m)
|
||||||
|
if self.is_unused(j):
|
||||||
|
return False
|
||||||
|
if self.table[j] == x:
|
||||||
|
return True
|
||||||
|
i.set(i.succ())
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete(self, x: MemoryCell):
|
||||||
|
with MemoryCell(0) as i:
|
||||||
|
while i < self.m:
|
||||||
|
j = self.f(x, i, self.m)
|
||||||
|
if self.is_unused(j):
|
||||||
|
return False
|
||||||
|
if self.table[j] == x:
|
||||||
|
self.table[j].value = DELETED_MARK
|
||||||
|
return True
|
||||||
|
i.set(i.succ())
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.table)
|
||||||
|
|
||||||
|
def alpha(self):
|
||||||
|
with MemoryCell(0) as i:
|
||||||
|
used = 0
|
||||||
|
while i < self.m:
|
||||||
|
used += 0 if self.is_free(i) else 1
|
||||||
|
i.set(i.succ())
|
||||||
|
return used / int(self.m)
|
||||||
|
|
||||||
|
def is_unused(self, i: Literal):
|
||||||
|
if self.table[i].value == UNUSED_MARK:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_deleted(self, i: Literal):
|
||||||
|
if self.table[i].value == DELETED_MARK:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_free(self, i: Literal):
|
||||||
|
return self.is_unused(i) or self.is_deleted(i)
|
||||||
0
vorlesung/L08_graphen/__init__.py
Normal file
0
vorlesung/L08_graphen/__init__.py
Normal file
45
vorlesung/L08_graphen/aoc2212.py
Normal file
45
vorlesung/L08_graphen/aoc2212.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from vorlesung.L08_graphen.graph import Graph, AdjacencyMatrixGraph
|
||||||
|
from utils.project_dir import get_path
|
||||||
|
|
||||||
|
graph = AdjacencyMatrixGraph()
|
||||||
|
start = ""
|
||||||
|
end = ""
|
||||||
|
|
||||||
|
def read_file(filename: str = "data/aoc2212.txt"):
|
||||||
|
"""Read a file and return the content as a string."""
|
||||||
|
|
||||||
|
def adjust_char(char):
|
||||||
|
"""Adjust character for comparison."""
|
||||||
|
if char == 'S':
|
||||||
|
return 'a'
|
||||||
|
elif char == 'E':
|
||||||
|
return 'z'
|
||||||
|
return char
|
||||||
|
|
||||||
|
global start, end
|
||||||
|
with open(get_path(filename), "r") as file:
|
||||||
|
quest = file.read().strip().splitlines()
|
||||||
|
for row, line in enumerate(quest):
|
||||||
|
for col, char in enumerate(line):
|
||||||
|
label = f"{row},{col}"
|
||||||
|
graph.insert_vertex(label)
|
||||||
|
if char == "S":
|
||||||
|
start = label
|
||||||
|
if char == "E":
|
||||||
|
end = label
|
||||||
|
for row, line in enumerate(quest):
|
||||||
|
for col, char in enumerate(line):
|
||||||
|
for neighbor in [(row - 1, col), (row, col - 1), (row + 1, col), (row, col + 1)]:
|
||||||
|
if 0 <= neighbor[0] < len(quest) and 0 <= neighbor[1] < len(line):
|
||||||
|
if ord(adjust_char(quest[neighbor[0]][neighbor[1]])) <= ord(adjust_char(char)) + 1:
|
||||||
|
label1 = f"{row},{col}"
|
||||||
|
label2 = f"{neighbor[0]},{neighbor[1]}"
|
||||||
|
graph.connect(label1, label2)
|
||||||
|
|
||||||
|
|
||||||
|
# Lösung des Adventskalenders 2022, Tag 12
|
||||||
|
read_file("data/aoc2212test.txt")
|
||||||
|
graph.graph()
|
||||||
|
distance_map, predecessor_map = graph.bfs(start)
|
||||||
|
print(distance_map[graph.get_vertex(end)])
|
||||||
|
print(graph.path(end, predecessor_map))
|
||||||
366
vorlesung/L08_graphen/graph.py
Normal file
366
vorlesung/L08_graphen/graph.py
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
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):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Vertex({self.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, weight: float = 1):
|
||||||
|
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 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):
|
||||||
|
"""
|
||||||
|
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 dfs(self):
|
||||||
|
"""
|
||||||
|
Perform a depth-first search starting at the first vertex.
|
||||||
|
: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 : dict[Vertex, NodeColor]= {}
|
||||||
|
enter_map : dict[Vertex, int] = {}
|
||||||
|
leave_map : dict[Vertex, int] = {}
|
||||||
|
predecessor_map : dict[Vertex, Vertex | None] = {}
|
||||||
|
white_vertices = set(self.all_vertices())
|
||||||
|
time_counter = 0
|
||||||
|
|
||||||
|
def dfs_visit(vertex):
|
||||||
|
nonlocal time_counter
|
||||||
|
color_map[vertex] = NodeColor.GRAY
|
||||||
|
white_vertices.remove(vertex)
|
||||||
|
time_counter += 1
|
||||||
|
enter_map[vertex] = time_counter
|
||||||
|
for dest in self.get_adjacent_vertices(vertex.value):
|
||||||
|
if color_map[dest] == NodeColor.WHITE:
|
||||||
|
predecessor_map[dest] = vertex
|
||||||
|
dfs_visit(dest)
|
||||||
|
color_map[vertex] = NodeColor.BLACK
|
||||||
|
time_counter += 1
|
||||||
|
leave_map[vertex] = time_counter
|
||||||
|
|
||||||
|
# Initialize the maps
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
color_map[vertex] = NodeColor.WHITE
|
||||||
|
predecessor_map[vertex] = None
|
||||||
|
|
||||||
|
while white_vertices:
|
||||||
|
v = white_vertices.pop()
|
||||||
|
dfs_visit(v)
|
||||||
|
|
||||||
|
return enter_map, leave_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
|
||||||
|
|
||||||
|
def graph(self, filename: str = "Graph"):
|
||||||
|
dot = graphviz.Digraph( name=filename,
|
||||||
|
node_attr={"fontname": "Arial"},
|
||||||
|
format="pdf" )
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
dot.node(str(id(vertex)), label=str(vertex.value))
|
||||||
|
for edge in self.all_edges():
|
||||||
|
dot.edge(str(id(self.get_vertex(edge[0]))), str(id(self.get_vertex(edge[1]))), label=str(edge[2]))
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
filename = f"{filename}_{timestamp}.gv"
|
||||||
|
filename = get_path(filename)
|
||||||
|
dot.render(filename)
|
||||||
|
|
||||||
|
def dijkstra(self, start_name: str) -> tuple[dict[Vertex, float], dict[Vertex, Vertex | None]]:
|
||||||
|
"""
|
||||||
|
Führt den Dijkstra-Algorithmus für kürzeste Pfade durch, implementiert mit Knotenfarben.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start_name: Name des Startknotens
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Ein Tupel aus zwei Dictionaries:
|
||||||
|
- distance_map: Abbildung von Knoten auf ihre kürzeste Distanz vom Startknoten
|
||||||
|
- predecessor_map: Abbildung von Knoten auf ihre Vorgänger im kürzesten Pfad
|
||||||
|
"""
|
||||||
|
|
||||||
|
def relax(vertex, dest, weight):
|
||||||
|
"""
|
||||||
|
Entspannt die Kante zwischen vertex und dest.
|
||||||
|
Aktualisiert die Distanz und den Vorgänger, wenn ein kürzerer Pfad gefunden wird.
|
||||||
|
"""
|
||||||
|
if distance_map[vertex] + weight < distance_map[dest]:
|
||||||
|
distance_map[dest] = distance_map[vertex] + weight
|
||||||
|
predecessor_map[dest] = vertex
|
||||||
|
queue.add_or_update(dest, distance_map[dest])
|
||||||
|
|
||||||
|
# Initialisierung der Maps
|
||||||
|
distance_map = {} # Speichert kürzeste Distanzen
|
||||||
|
predecessor_map = {} # Speichert Vorgänger
|
||||||
|
|
||||||
|
# Initialisiere alle Knoten
|
||||||
|
queue = PriorityQueue()
|
||||||
|
for vertex in self.all_vertices():
|
||||||
|
distance_map[vertex] = float('inf') # Initiale Distanz unendlich
|
||||||
|
predecessor_map[vertex] = None # Initialer Vorgänger None
|
||||||
|
queue.add_or_update(vertex, distance_map[vertex]) # Füge Knoten zur Prioritätswarteschlange hinzu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Setze Startknoten
|
||||||
|
start_node = self.get_vertex(start_name)
|
||||||
|
distance_map[start_node] = 0
|
||||||
|
queue.add_or_update(start_node, distance_map[start_node])
|
||||||
|
|
||||||
|
while True:
|
||||||
|
entry = queue.pop()
|
||||||
|
if entry is None:
|
||||||
|
break
|
||||||
|
vertex = entry[0]
|
||||||
|
for dest, weight in self.get_adjacent_vertices_with_weight(vertex.value):
|
||||||
|
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."""
|
||||||
|
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, weight: float = 1):
|
||||||
|
adjacency_list = self.adjacency_map[name1]
|
||||||
|
dest = self.vertex_map[name2]
|
||||||
|
adjacency_list.append((dest, weight))
|
||||||
|
|
||||||
|
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 list(map(lambda x: x[0], self.adjacency_map[name]))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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(None)
|
||||||
|
self.adjacency_matrix.append([None] * len(self.vertex_list)) # add a new row
|
||||||
|
|
||||||
|
def connect(self, name1: str, name2: str, weight: float = 1):
|
||||||
|
index1 = self.index_map[name1]
|
||||||
|
index2 = self.index_map[name2]
|
||||||
|
self.adjacency_matrix[index1][index2] = weight
|
||||||
|
|
||||||
|
|
||||||
|
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] is not None:
|
||||||
|
name = self.vertex_list[i].value
|
||||||
|
result.append(self.get_vertex(name))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_adjacent_vertices_with_weight(self, name: str) -> List[tuple[Vertex, float]]:
|
||||||
|
index = self.index_map[name]
|
||||||
|
result = []
|
||||||
|
for i in range(len(self.vertex_list)):
|
||||||
|
if self.adjacency_matrix[index][i] is not None:
|
||||||
|
name = self.vertex_list[i].value
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0
vorlesung/L09_mst/__init__.py
Normal file
0
vorlesung/L09_mst/__init__.py
Normal file
18
vorlesung/L09_mst/disjoint.py
Normal file
18
vorlesung/L09_mst/disjoint.py
Normal 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()
|
||||||
0
vorlesung/__init__.py
Normal file
0
vorlesung/__init__.py
Normal file
Loading…
x
Reference in New Issue
Block a user