Compare commits
No commits in common. "fe533e6d4a2af0ba606cbdbc2d64c340b91673ed" and "c48b5c7e59d7b596686944e1844373b94debc57e" have entirely different histories.
fe533e6d4a
...
c48b5c7e59
157
README.md
157
README.md
@ -1,157 +0,0 @@
|
|||||||
# Algorithmen und Datenstrukturen – SoSe 26
|
|
||||||
|
|
||||||
Dieses Repository enthält den Code zur Vorlesung. Neben den Vorlesungsbeispielen
|
|
||||||
bietet es ein kleines Framework, mit dem ihr eure eigenen Algorithmen
|
|
||||||
**automatisch auf Operationen messen** könnt – ohne den Algorithmus selbst
|
|
||||||
anfassen zu müssen.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Wozu das Framework?
|
|
||||||
|
|
||||||
In der Vorlesung analysieren wir Algorithmen theoretisch mit der O-Notation.
|
|
||||||
Das Framework erlaubt euch, diese Theorie **empirisch zu überprüfen**: Ihr
|
|
||||||
implementiert euren Algorithmus genauso wie in Python üblich – nur dass ihr
|
|
||||||
statt `list` und `int` die Wrapper-Typen `Array` und `Int` verwendet. Das
|
|
||||||
Framework zählt dann im Hintergrund automatisch Vergleiche, Lese- und
|
|
||||||
Schreibzugriffe sowie arithmetische Operationen.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Schnellstart
|
|
||||||
|
|
||||||
### 1. Kontext anlegen
|
|
||||||
|
|
||||||
```python
|
|
||||||
from utils.algo_context import AlgoContext
|
|
||||||
from utils.algo_array import Array
|
|
||||||
from utils.algo_range import Range
|
|
||||||
|
|
||||||
ctx = AlgoContext()
|
|
||||||
```
|
|
||||||
|
|
||||||
`AlgoContext` ist der Zähler. Ihr legt ihn einmal an und gebt ihn an eure
|
|
||||||
Datenstrukturen weiter.
|
|
||||||
|
|
||||||
### 2. Array befüllen
|
|
||||||
|
|
||||||
```python
|
|
||||||
z = Array.random(10, -100, 100, ctx) # 10 Zufallszahlen zwischen -100 und 100
|
|
||||||
# oder:
|
|
||||||
z = Array([5, 3, 8, 1, 9, 2], ctx) # eigene Werte
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Algorithmus schreiben
|
|
||||||
|
|
||||||
Schreibt euren Algorithmus wie gewohnt. `Range` ist ein Drop-in-Ersatz für
|
|
||||||
`range` und liefert ebenfalls `Int`-Objekte – allerdings ohne Verbindung zum
|
|
||||||
Zähler. Dadurch wird **Indexarithmetik** (`j - 1`, `i + 1`) nicht mitgezählt,
|
|
||||||
während Operationen auf Array-*Inhalten* (die ja den echten `ctx` tragen) weiterhin
|
|
||||||
erfasst werden. Das entspricht der üblichen Konvention: gezählt werden nur
|
|
||||||
Operationen auf den Daten, nicht die Schleifensteuerung.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def insertion_sort(z: Array, ctx: AlgoContext):
|
|
||||||
for i in Range(1, len(z)):
|
|
||||||
elem = z[i] # liest z[i] (1 Lesezugriff)
|
|
||||||
j = int(i) - 1
|
|
||||||
while j >= 0 and z[j] > elem: # vergleicht (1 Vergleich pro Schritt)
|
|
||||||
z[j + 1] = z[j] # schreibt (1 Schreibzugriff)
|
|
||||||
j -= 1
|
|
||||||
z[j + 1] = elem
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Ausführen und Ergebnis ablesen
|
|
||||||
|
|
||||||
```python
|
|
||||||
insertion_sort(z, ctx)
|
|
||||||
|
|
||||||
print(ctx.comparisons) # Anzahl Vergleiche
|
|
||||||
print(ctx.reads) # Lesezugriffe
|
|
||||||
print(ctx.writes) # Schreibzugriffe
|
|
||||||
print(ctx.additions) # Additionen
|
|
||||||
```
|
|
||||||
|
|
||||||
Oder kompakt:
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(ctx.summary())
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Komplexität über mehrere Eingabegrößen plotten
|
|
||||||
|
|
||||||
```python
|
|
||||||
def analyze(sort_func, sizes):
|
|
||||||
ctx = AlgoContext()
|
|
||||||
for size in sizes:
|
|
||||||
ctx.reset()
|
|
||||||
z = Array.random(size, -100, 100, ctx)
|
|
||||||
sort_func(z, ctx)
|
|
||||||
ctx.save_stats(size)
|
|
||||||
ctx.plot_stats(["comparisons", "writes"])
|
|
||||||
|
|
||||||
analyze(insertion_sort, range(10, 201, 10))
|
|
||||||
```
|
|
||||||
|
|
||||||
Das öffnet ein Matplotlib-Fenster mit dem Verlauf der gezählten Operationen
|
|
||||||
über die Eingabegröße – ihr seht direkt, ob euer Algorithmus z.B. quadratisch oder
|
|
||||||
linear wächst.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verfügbare Zähler
|
|
||||||
|
|
||||||
| Attribut | Was wird gezählt |
|
|
||||||
|-----------------|-------------------------------------------|
|
|
||||||
| `comparisons` | `<`, `>`, `<=`, `>=`, `==`, `!=` |
|
|
||||||
| `reads` | Lesezugriffe auf `Int`-Werte |
|
|
||||||
| `writes` | Schreibzugriffe (Zuweisung, `swap`) |
|
|
||||||
| `additions` | `+`, `+=` |
|
|
||||||
| `subtractions` | `-`, `-=` |
|
|
||||||
| `multiplications` | `*`, `*=` |
|
|
||||||
| `divisions` | `/`, `//`, `/=` |
|
|
||||||
| `bitops` | `&`, `\|`, `^`, `<<`, `>>` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Struktur des Repositories
|
|
||||||
|
|
||||||
```
|
|
||||||
utils/ Framework-Dateien (AlgoContext, Int, Array, Range)
|
|
||||||
vorlesung/ Vorlesungsbeispiele, geordnet nach Lektionsnummer
|
|
||||||
praktika/ Aufgabenstellungen der Praktika
|
|
||||||
playground/ Eure eigenen Abgaben und Experimente
|
|
||||||
data/ Beispieldatensätze
|
|
||||||
```
|
|
||||||
|
|
||||||
Die Vorlesungsbeispiele in `vorlesung/` sind vollständig mit dem Framework
|
|
||||||
instrumentiert und können als Vorlage für eigene Implementierungen dienen.
|
|
||||||
|
|
||||||
## Datenpfade
|
|
||||||
|
|
||||||
Die Demodaten in `data/` werden über `path()` aus `utils.algo_path` adressiert.
|
|
||||||
Die Funktion liefert immer den absoluten Pfad relativ zum Projektverzeichnis –
|
|
||||||
unabhängig davon, aus welchem Verzeichnis ihr das Skript startet oder welche IDE
|
|
||||||
ihr verwendet:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from utils.algo_path import path
|
|
||||||
|
|
||||||
z = Array.from_file(path("data/seq0.txt"), ctx)
|
|
||||||
```
|
|
||||||
|
|
||||||
## PriorityQueue
|
|
||||||
|
|
||||||
Für Graphalgorithmen (z.B. Dijkstra) steht eine fertige `PriorityQueue` bereit.
|
|
||||||
Sie basiert intern auf einem Heap – das Prinzip habt ihr in der Vorlesung zu
|
|
||||||
Heap Sort kennengelernt. Als Abkürzung dürft ihr die fertige Klasse verwenden:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from utils.algo_priority_queue import PriorityQueue
|
|
||||||
|
|
||||||
pq = PriorityQueue()
|
|
||||||
pq.add_or_update("A", 0) # Knoten mit Priorität (Distanz) eintragen
|
|
||||||
pq.add_or_update("B", 5)
|
|
||||||
pq.add_or_update("B", 2) # Priorität aktualisieren
|
|
||||||
node, dist = pq.pop() # liefert ("A", 0)
|
|
||||||
```
|
|
||||||
@ -1,6 +1,4 @@
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
from utils.algo_path import path
|
|
||||||
from utils.algo_priority_queue import PriorityQueue
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
from random import randint
|
from random import randint
|
||||||
from utils.algo_context import AlgoContext, _NullContext, NULL_CTX
|
from utils.algo_context import AlgoContext, _NullContext, NULL_CTX
|
||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
from utils.algo_path import path
|
from utils.project_dir import get_path
|
||||||
|
|
||||||
|
|
||||||
class Array:
|
class Array:
|
||||||
@ -130,8 +130,8 @@ class Array:
|
|||||||
limit : int | None
|
limit : int | None
|
||||||
Optionale Obergrenze für die Anzahl eingelesener Zeilen.
|
Optionale Obergrenze für die Anzahl eingelesener Zeilen.
|
||||||
"""
|
"""
|
||||||
filepath = path(filename)
|
path = get_path(filename)
|
||||||
with open(filepath) as f:
|
with open(path) as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
lines = lines[:limit]
|
lines = lines[:limit]
|
||||||
|
|||||||
@ -117,7 +117,7 @@ class _NullContext:
|
|||||||
"""
|
"""
|
||||||
Kontext der alle Operationen stillschweigend ignoriert.
|
Kontext der alle Operationen stillschweigend ignoriert.
|
||||||
|
|
||||||
Wird intern von Range() verwendet, damit Schleifenindex-Arithmetik
|
Wird intern von irange() verwendet, damit Schleifenindex-Arithmetik
|
||||||
standardmäßig nicht mitgezählt wird.
|
standardmäßig nicht mitgezählt wird.
|
||||||
|
|
||||||
__setattr__ ist absichtlich ein no-op: ``ctx.reads += 1`` bleibt wirkungslos.
|
__setattr__ ist absichtlich ein no-op: ``ctx.reads += 1`` bleibt wirkungslos.
|
||||||
|
|||||||
@ -212,14 +212,6 @@ class Int:
|
|||||||
self._value //= other._value
|
self._value //= other._value
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __imod__(self, other):
|
|
||||||
other = self._wrap(other)
|
|
||||||
self._ctx.reads += 2
|
|
||||||
self._ctx.divisions += 1
|
|
||||||
self._ctx.writes += 1
|
|
||||||
self._value %= other._value
|
|
||||||
return self
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Bitoperationen (2 reads + 1 bitop + 1 write für in-place)
|
# Bitoperationen (2 reads + 1 bitop + 1 write für in-place)
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
def path(filename) -> Path:
|
|
||||||
"""Gibt den absoluten Pfad zu einer Datei im Projektverzeichnis zurück.
|
|
||||||
|
|
||||||
Funktioniert unabhängig vom Arbeitsverzeichnis und der verwendeten IDE,
|
|
||||||
da der Pfad relativ zur Position dieses Moduls berechnet wird.
|
|
||||||
|
|
||||||
Beispiel
|
|
||||||
--------
|
|
||||||
from utils.algo_path import path
|
|
||||||
z = Array.from_file(path("data/seq0.txt"), ctx)
|
|
||||||
"""
|
|
||||||
project_dir = Path(__file__).resolve().parent.parent
|
|
||||||
return project_dir / filename
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
filename = path("data/seq0.txt")
|
|
||||||
print(filename)
|
|
||||||
print(filename.resolve())
|
|
||||||
print(filename.is_file())
|
|
||||||
print(filename.exists())
|
|
||||||
@ -3,14 +3,14 @@ from utils.algo_context import AlgoContext, NULL_CTX
|
|||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
|
|
||||||
|
|
||||||
def Range(start_or_stop, stop=None, step: int = 1, ctx: AlgoContext = None):
|
def irange(start_or_stop, stop=None, step: int = 1, ctx: AlgoContext = None):
|
||||||
"""
|
"""
|
||||||
Drop-in Ersatz für range(), der Int-Objekte zurückgibt.
|
Drop-in Ersatz für range(), der Int-Objekte zurückgibt.
|
||||||
|
|
||||||
Wird wie Pythons range() aufgerufen:
|
Wird wie Pythons range() aufgerufen:
|
||||||
Range(stop)
|
irange(stop)
|
||||||
Range(start, stop)
|
irange(start, stop)
|
||||||
Range(start, stop, step)
|
irange(start, stop, step)
|
||||||
|
|
||||||
Indexarithmetik und Zählung
|
Indexarithmetik und Zählung
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -20,13 +20,13 @@ def Range(start_or_stop, stop=None, step: int = 1, ctx: AlgoContext = None):
|
|||||||
sollen in die Komplexitätsanalyse einfließen.
|
sollen in die Komplexitätsanalyse einfließen.
|
||||||
|
|
||||||
Mit ctx-Argument werden auch Indexoperationen gezählt:
|
Mit ctx-Argument werden auch Indexoperationen gezählt:
|
||||||
for j in Range(n, ctx=ctx): ...
|
for j in irange(n, ctx=ctx): ...
|
||||||
|
|
||||||
Beispiel
|
Beispiel
|
||||||
--------
|
--------
|
||||||
ctx = AlgoContext()
|
ctx = AlgoContext()
|
||||||
z = Array.random(10, 0, 99, ctx)
|
z = Array.random(10, 0, 99, ctx)
|
||||||
for i in Range(len(z) - 1): # i ist Int, Arithmetik nicht gezählt
|
for i in irange(len(z) - 1): # i ist Int, Arithmetik nicht gezählt
|
||||||
if z[i] > z[i + 1]: # Vergleich gezählt (z-Zellen haben ctx)
|
if z[i] > z[i + 1]: # Vergleich gezählt (z-Zellen haben ctx)
|
||||||
z.swap(i, i + 1)
|
z.swap(i, i + 1)
|
||||||
"""
|
"""
|
||||||
@ -38,7 +38,7 @@ def Range(start_or_stop, stop=None, step: int = 1, ctx: AlgoContext = None):
|
|||||||
start, stop_ = int(start_or_stop), int(stop)
|
start, stop_ = int(start_or_stop), int(stop)
|
||||||
step = int(step)
|
step = int(step)
|
||||||
|
|
||||||
assert step != 0, "Range: step darf nicht 0 sein"
|
assert step != 0, "irange: step darf nicht 0 sein"
|
||||||
|
|
||||||
num = start
|
num = start
|
||||||
if step > 0:
|
if step > 0:
|
||||||
|
|||||||
@ -35,6 +35,6 @@ if __name__ == "__main__":
|
|||||||
pq.add_or_update('task3', float('inf'))
|
pq.add_or_update('task3', float('inf'))
|
||||||
|
|
||||||
print(pq.pop()) # Should print ('task1', 1)
|
print(pq.pop()) # Should print ('task1', 1)
|
||||||
pq.add_or_update('task2', 0) # Update priority of 'task2'
|
pq.add_or_update('task2', 0) # Update priority of 'task1'
|
||||||
print(pq.pop()) # Should print ('task2', 0)
|
print(pq.pop()) # Should print ('task2', 0)
|
||||||
print(pq.pop()) # Should print ('task3', 3)
|
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())
|
||||||
175
utils/test_algo_array.py
Normal file
175
utils/test_algo_array.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import unittest
|
||||||
|
from utils.algo_context import AlgoContext
|
||||||
|
from utils.algo_int import Int
|
||||||
|
from utils.algo_array import Array
|
||||||
|
|
||||||
|
|
||||||
|
class TestArray(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.ctx = AlgoContext()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Erzeugung
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_from_list(self):
|
||||||
|
z = Array([3, 1, 4, 1, 5], self.ctx)
|
||||||
|
self.assertEqual(len(z), 5)
|
||||||
|
self.assertEqual(z[0].value, 3)
|
||||||
|
self.assertEqual(z[4].value, 5)
|
||||||
|
|
||||||
|
def test_random(self):
|
||||||
|
z = Array.random(20, 0, 99, self.ctx)
|
||||||
|
self.assertEqual(len(z), 20)
|
||||||
|
for cell in z:
|
||||||
|
self.assertGreaterEqual(cell.value, 0)
|
||||||
|
self.assertLessEqual(cell.value, 99)
|
||||||
|
|
||||||
|
def test_sorted(self):
|
||||||
|
z = Array.sorted(5, self.ctx)
|
||||||
|
values = [z[i].value for i in range(5)]
|
||||||
|
self.assertEqual(values, [0, 1, 2, 3, 4])
|
||||||
|
|
||||||
|
def test_from_file(self):
|
||||||
|
z = Array.from_file("data/seq0.txt", self.ctx)
|
||||||
|
self.assertGreater(len(z), 0)
|
||||||
|
|
||||||
|
def test_random_uses_no_write_counts(self):
|
||||||
|
"""Fabrikmethoden sollen keine Schreibzugriffe verzeichnen."""
|
||||||
|
z = Array.random(10, 0, 9, self.ctx)
|
||||||
|
self.assertEqual(self.ctx.writes, 0)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Zugriff
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_getitem_returns_int_cell(self):
|
||||||
|
z = Array([10, 20], self.ctx)
|
||||||
|
cell = z[0]
|
||||||
|
self.assertIsInstance(cell, Int)
|
||||||
|
self.assertEqual(cell.value, 10)
|
||||||
|
|
||||||
|
def test_getitem_with_int_index(self):
|
||||||
|
z = Array([10, 20, 30], self.ctx)
|
||||||
|
i = Int(2, self.ctx)
|
||||||
|
self.assertEqual(z[i].value, 30)
|
||||||
|
|
||||||
|
def test_getitem_no_read_count(self):
|
||||||
|
"""Array-Zugriff allein soll keinen read-Zähler erhöhen."""
|
||||||
|
z = Array([5, 6, 7], self.ctx)
|
||||||
|
_ = z[0]
|
||||||
|
self.assertEqual(self.ctx.reads, 0)
|
||||||
|
|
||||||
|
def test_setitem_plain_int(self):
|
||||||
|
z = Array([1, 2, 3], self.ctx)
|
||||||
|
z[0] = 99
|
||||||
|
self.assertEqual(z[0].value, 99)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 0)
|
||||||
|
|
||||||
|
def test_setitem_int_object(self):
|
||||||
|
z = Array([1, 2, 3], self.ctx)
|
||||||
|
v = Int(42, self.ctx)
|
||||||
|
z[1] = v
|
||||||
|
self.assertEqual(z[1].value, 42)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 1)
|
||||||
|
|
||||||
|
def test_setitem_cell_to_cell(self):
|
||||||
|
"""z[i] = z[j] kopiert den Wert (keine Alias-Referenz)."""
|
||||||
|
z = Array([10, 20], self.ctx)
|
||||||
|
z[0] = z[1]
|
||||||
|
self.assertEqual(z[0].value, 20)
|
||||||
|
# Wert ändern – z[1] darf sich nicht mitändern
|
||||||
|
z[0] = 99
|
||||||
|
self.assertEqual(z[1].value, 20)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Swap
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_swap_exchanges_values(self):
|
||||||
|
z = Array([1, 2, 3, 4, 5], self.ctx)
|
||||||
|
z.swap(0, 4)
|
||||||
|
self.assertEqual(z[0].value, 5)
|
||||||
|
self.assertEqual(z[4].value, 1)
|
||||||
|
|
||||||
|
def test_swap_counts_reads_and_writes(self):
|
||||||
|
z = Array([1, 2], self.ctx)
|
||||||
|
z.swap(0, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
self.assertEqual(self.ctx.writes, 2)
|
||||||
|
|
||||||
|
def test_swap_with_int_indices(self):
|
||||||
|
z = Array([10, 20, 30], self.ctx)
|
||||||
|
i = Int(0, self.ctx)
|
||||||
|
j = Int(2, self.ctx)
|
||||||
|
z.swap(i, j)
|
||||||
|
self.assertEqual(z[0].value, 30)
|
||||||
|
self.assertEqual(z[2].value, 10)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Vergleich über Array-Zellen (zählt beim Int, nicht beim Array)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_cell_comparison_counts_in_ctx(self):
|
||||||
|
z = Array([5, 3], self.ctx)
|
||||||
|
result = z[0] > z[1]
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.assertEqual(self.ctx.comparisons, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Iteration
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_iteration(self):
|
||||||
|
z = Array([1, 2, 3], self.ctx)
|
||||||
|
values = [c.value for c in z]
|
||||||
|
self.assertEqual(values, [1, 2, 3])
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Länge
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_len(self):
|
||||||
|
z = Array([1, 2, 3, 4], self.ctx)
|
||||||
|
self.assertEqual(len(z), 4)
|
||||||
|
|
||||||
|
def test_length_returns_int(self):
|
||||||
|
z = Array([1, 2, 3], self.ctx)
|
||||||
|
n = z.length()
|
||||||
|
self.assertIsInstance(n, Int)
|
||||||
|
self.assertEqual(n.value, 3)
|
||||||
|
|
||||||
|
|
||||||
|
class TestArrayIntegration(unittest.TestCase):
|
||||||
|
"""Bubble Sort als Integrationstest für das gesamte Framework."""
|
||||||
|
|
||||||
|
def test_bubble_sort_produces_correct_result(self):
|
||||||
|
import sys, os
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
|
||||||
|
'../vorlesung/L02_elementares_sortieren'))
|
||||||
|
from bubble_sorting import bubble_sort
|
||||||
|
ctx = AlgoContext()
|
||||||
|
z = Array([5, 3, 1, 4, 2], ctx)
|
||||||
|
bubble_sort(z, ctx)
|
||||||
|
values = [z[i].value for i in range(len(z))]
|
||||||
|
self.assertEqual(values, [1, 2, 3, 4, 5])
|
||||||
|
|
||||||
|
def test_bubble_sort_counts_comparisons(self):
|
||||||
|
import sys, os
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
|
||||||
|
'../vorlesung/L02_elementares_sortieren'))
|
||||||
|
from bubble_sorting import bubble_sort
|
||||||
|
ctx = AlgoContext()
|
||||||
|
z = Array([5, 4, 3, 2, 1], ctx) # worst case
|
||||||
|
bubble_sort(z, ctx)
|
||||||
|
# n=5: maximal n*(n-1)/2 = 10 Vergleiche
|
||||||
|
self.assertGreater(ctx.comparisons, 0)
|
||||||
|
self.assertLessEqual(ctx.comparisons, 10)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
193
utils/test_algo_int.py
Normal file
193
utils/test_algo_int.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import unittest
|
||||||
|
from utils.algo_context import AlgoContext
|
||||||
|
from utils.algo_int import Int
|
||||||
|
|
||||||
|
|
||||||
|
class TestInt(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.ctx = AlgoContext()
|
||||||
|
|
||||||
|
def _int(self, v):
|
||||||
|
return Int(v, self.ctx)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Vergleiche
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_comparison_counts_reads_and_compare(self):
|
||||||
|
a, b = self._int(5), self._int(3)
|
||||||
|
result = a > b
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.assertEqual(self.ctx.comparisons, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
|
||||||
|
def test_all_comparison_operators(self):
|
||||||
|
a, b = self._int(4), self._int(4)
|
||||||
|
self.assertTrue(a == b)
|
||||||
|
self.assertFalse(a != b)
|
||||||
|
self.assertTrue(a <= b)
|
||||||
|
self.assertTrue(a >= b)
|
||||||
|
self.assertFalse(a < b)
|
||||||
|
self.assertFalse(a > b)
|
||||||
|
self.assertEqual(self.ctx.comparisons, 6)
|
||||||
|
self.assertEqual(self.ctx.reads, 12)
|
||||||
|
|
||||||
|
def test_comparison_with_plain_int(self):
|
||||||
|
a = self._int(5)
|
||||||
|
result = a > 3 # 3 wird auto-gewrappt, kein extra Zähler
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.assertEqual(self.ctx.comparisons, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
|
||||||
|
def test_eq_with_none_returns_false(self):
|
||||||
|
a = self._int(1)
|
||||||
|
self.assertFalse(a == None) # noqa: E711
|
||||||
|
self.assertEqual(self.ctx.comparisons, 0) # keine Zählung
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Arithmetik (binär)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_addition_returns_new_int(self):
|
||||||
|
a, b = self._int(3), self._int(4)
|
||||||
|
c = a + b
|
||||||
|
self.assertIsInstance(c, Int)
|
||||||
|
self.assertEqual(c.value, 7)
|
||||||
|
self.assertEqual(self.ctx.additions, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
self.assertEqual(self.ctx.writes, 0)
|
||||||
|
|
||||||
|
def test_subtraction(self):
|
||||||
|
a, b = self._int(10), self._int(4)
|
||||||
|
c = a - b
|
||||||
|
self.assertEqual(c.value, 6)
|
||||||
|
self.assertEqual(self.ctx.subtractions, 1)
|
||||||
|
|
||||||
|
def test_multiplication(self):
|
||||||
|
a, b = self._int(3), self._int(4)
|
||||||
|
c = a * b
|
||||||
|
self.assertEqual(c.value, 12)
|
||||||
|
self.assertEqual(self.ctx.multiplications, 1)
|
||||||
|
|
||||||
|
def test_floordiv(self):
|
||||||
|
a, b = self._int(10), self._int(3)
|
||||||
|
c = a // b
|
||||||
|
self.assertEqual(c.value, 3)
|
||||||
|
self.assertEqual(self.ctx.divisions, 1)
|
||||||
|
|
||||||
|
def test_mod(self):
|
||||||
|
a, b = self._int(10), self._int(3)
|
||||||
|
c = a % b
|
||||||
|
self.assertEqual(c.value, 1)
|
||||||
|
self.assertEqual(self.ctx.divisions, 1)
|
||||||
|
|
||||||
|
def test_arithmetic_with_plain_int(self):
|
||||||
|
a = self._int(5)
|
||||||
|
c = a + 3
|
||||||
|
self.assertEqual(c.value, 8)
|
||||||
|
self.assertEqual(self.ctx.additions, 1)
|
||||||
|
|
||||||
|
def test_result_shares_context(self):
|
||||||
|
a = self._int(5)
|
||||||
|
b = self._int(3)
|
||||||
|
c = a + b # c hat denselben ctx
|
||||||
|
_ = c > self._int(0) # Vergleich auf c zählt im selben ctx
|
||||||
|
self.assertEqual(self.ctx.comparisons, 1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Augmented assignment
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_iadd_counts_read_add_write(self):
|
||||||
|
a, b = self._int(5), self._int(3)
|
||||||
|
a += b
|
||||||
|
self.assertEqual(a.value, 8)
|
||||||
|
self.assertEqual(self.ctx.reads, 2)
|
||||||
|
self.assertEqual(self.ctx.additions, 1)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
|
||||||
|
def test_isub(self):
|
||||||
|
a, b = self._int(10), self._int(4)
|
||||||
|
a -= b
|
||||||
|
self.assertEqual(a.value, 6)
|
||||||
|
self.assertEqual(self.ctx.subtractions, 1)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
|
||||||
|
def test_imul(self):
|
||||||
|
a, b = self._int(3), self._int(4)
|
||||||
|
a *= b
|
||||||
|
self.assertEqual(a.value, 12)
|
||||||
|
self.assertEqual(self.ctx.multiplications, 1)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
|
||||||
|
def test_iadd_with_plain_int(self):
|
||||||
|
a = self._int(5)
|
||||||
|
a += 1
|
||||||
|
self.assertEqual(a.value, 6)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# set()
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_set_plain_counts_one_write(self):
|
||||||
|
a = self._int(0)
|
||||||
|
a.set(42)
|
||||||
|
self.assertEqual(a.value, 42)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 0)
|
||||||
|
|
||||||
|
def test_set_int_counts_write_and_read(self):
|
||||||
|
a = self._int(0)
|
||||||
|
b = self._int(7)
|
||||||
|
a.set(b)
|
||||||
|
self.assertEqual(a.value, 7)
|
||||||
|
self.assertEqual(self.ctx.writes, 1)
|
||||||
|
self.assertEqual(self.ctx.reads, 1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Typkonvertierung
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_int_conversion(self):
|
||||||
|
a = self._int(5)
|
||||||
|
self.assertEqual(int(a), 5)
|
||||||
|
|
||||||
|
def test_index_usage(self):
|
||||||
|
a = self._int(2)
|
||||||
|
lst = [10, 20, 30]
|
||||||
|
self.assertEqual(lst[a], 30) # __index__
|
||||||
|
|
||||||
|
def test_hash(self):
|
||||||
|
a = self._int(5)
|
||||||
|
self.assertEqual(hash(a), hash(5))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# AlgoContext.reset()
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def test_context_reset(self):
|
||||||
|
a, b = self._int(3), self._int(4)
|
||||||
|
_ = a > b
|
||||||
|
self.ctx.reset()
|
||||||
|
self.assertEqual(self.ctx.comparisons, 0)
|
||||||
|
self.assertEqual(self.ctx.reads, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class TestIntNullCtx(unittest.TestCase):
|
||||||
|
"""Int ohne expliziten ctx (irange-Verwendung) – keine Zählung."""
|
||||||
|
|
||||||
|
def test_arithmetic_without_ctx_produces_no_counts(self):
|
||||||
|
from utils.algo_context import NULL_CTX
|
||||||
|
a = Int(5) # uses NULL_CTX by default
|
||||||
|
b = Int(3)
|
||||||
|
_ = a + b
|
||||||
|
_ = a > b
|
||||||
|
# NULL_CTX hat feste 0-Attribute – keine Ausnahme, keine Zählung
|
||||||
|
self.assertEqual(NULL_CTX.additions, 0)
|
||||||
|
self.assertEqual(NULL_CTX.comparisons, 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from bubble_sorting import bubble_sort_stepwise
|
from bubble_sorting import bubble_sort_stepwise
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
|
|
||||||
|
|
||||||
def bubble_sort_stepwise(z: Array, ctx: AlgoContext):
|
def bubble_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||||
@ -11,8 +11,8 @@ def bubble_sort_stepwise(z: Array, ctx: AlgoContext):
|
|||||||
Wird von bubble_game.py für die Visualisierung verwendet.
|
Wird von bubble_game.py für die Visualisierung verwendet.
|
||||||
"""
|
"""
|
||||||
n = len(z)
|
n = len(z)
|
||||||
for i in Range(n - 1):
|
for i in irange(n - 1):
|
||||||
for j in Range(n - 1, i, -1):
|
for j in irange(n - 1, i, -1):
|
||||||
if z[j - 1] > z[j]:
|
if z[j - 1] > z[j]:
|
||||||
z.swap(j - 1, j)
|
z.swap(j - 1, j)
|
||||||
yield z
|
yield z
|
||||||
@ -27,7 +27,7 @@ def bubble_sort2_stepwise(z: Array, ctx: AlgoContext):
|
|||||||
n = len(z)
|
n = len(z)
|
||||||
while True:
|
while True:
|
||||||
swapped = False
|
swapped = False
|
||||||
for i in Range(n - 1):
|
for i in irange(n - 1):
|
||||||
if z[i] > z[i + 1]:
|
if z[i] > z[i + 1]:
|
||||||
z.swap(i, i + 1)
|
z.swap(i, i + 1)
|
||||||
swapped = True
|
swapped = True
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from insert_sorting import insert_sort_stepwise
|
from insert_sorting import insert_sort_stepwise
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
|
|
||||||
|
|
||||||
def insert_sort_stepwise(z: Array, ctx: AlgoContext):
|
def insert_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||||
@ -13,7 +13,7 @@ def insert_sort_stepwise(z: Array, ctx: AlgoContext):
|
|||||||
n = len(z)
|
n = len(z)
|
||||||
elem = Int(0, ctx) # Zwischenregister für das einzufügende Element
|
elem = Int(0, ctx) # Zwischenregister für das einzufügende Element
|
||||||
|
|
||||||
for i in Range(n):
|
for i in irange(n):
|
||||||
elem.set(z[i]) # 1 read + 1 write
|
elem.set(z[i]) # 1 read + 1 write
|
||||||
j = Int(int(i), ctx)
|
j = Int(int(i), ctx)
|
||||||
while j > 0 and z[j - 1] > elem:
|
while j > 0 and z[j - 1] > elem:
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from select_sorting import select_sort_stepwise
|
from select_sorting import select_sort_stepwise
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
|
|
||||||
|
|
||||||
def select_sort_stepwise(z: Array, ctx: AlgoContext):
|
def select_sort_stepwise(z: Array, ctx: AlgoContext):
|
||||||
@ -13,9 +13,9 @@ def select_sort_stepwise(z: Array, ctx: AlgoContext):
|
|||||||
n = len(z)
|
n = len(z)
|
||||||
cur_min = Int(0, ctx) # Index des aktuellen Minimums
|
cur_min = Int(0, ctx) # Index des aktuellen Minimums
|
||||||
|
|
||||||
for i in Range(n):
|
for i in irange(n):
|
||||||
cur_min.set(Int(int(i), ctx))
|
cur_min.set(Int(int(i), ctx))
|
||||||
for j in Range(int(i) + 1, n):
|
for j in irange(int(i) + 1, n):
|
||||||
if z[j] < z[cur_min]:
|
if z[j] < z[cur_min]:
|
||||||
cur_min.set(Int(int(j), ctx))
|
cur_min.set(Int(int(j), ctx))
|
||||||
z.swap(int(i), int(cur_min))
|
z.swap(int(i), int(cur_min))
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from heap_sorting import heap_sort_stepwise
|
from heap_sorting import heap_sort_stepwise
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from quick_sorting import quick_sort_stepwise
|
from quick_sorting import quick_sort_stepwise
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
|
|
||||||
|
|
||||||
def count_sort(a: Array, b: Array, k: int, ctx: AlgoContext):
|
def count_sort(a: Array, b: Array, k: int, ctx: AlgoContext):
|
||||||
@ -14,15 +14,15 @@ def count_sort(a: Array, b: Array, k: int, ctx: AlgoContext):
|
|||||||
c = Array([0] * (k + 1), ctx) # Zählarray
|
c = Array([0] * (k + 1), ctx) # Zählarray
|
||||||
|
|
||||||
# Häufigkeiten zählen
|
# Häufigkeiten zählen
|
||||||
for j in Range(len(a)):
|
for j in irange(len(a)):
|
||||||
c[a[j]] = c[a[j]] + 1
|
c[a[j]] = c[a[j]] + 1
|
||||||
|
|
||||||
# Kumulierte Summen bilden
|
# Kumulierte Summen bilden
|
||||||
for i in Range(1, k + 1):
|
for i in irange(1, k + 1):
|
||||||
c[i] = c[i] + c[i - 1]
|
c[i] = c[i] + c[i - 1]
|
||||||
|
|
||||||
# Stabil in b einsortieren (rückwärts für Stabilität)
|
# Stabil in b einsortieren (rückwärts für Stabilität)
|
||||||
for j in Range(len(a) - 1, -1, -1):
|
for j in irange(len(a) - 1, -1, -1):
|
||||||
b[c[a[j]] - 1] = a[j]
|
b[c[a[j]] - 1] = a[j]
|
||||||
c[a[j]] = c[a[j]] - 1
|
c[a[j]] = c[a[j]] - 1
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from avl_tree import AVLTree
|
from avl_tree import AVLTree
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
from vorlesung.L05_binaere_baeume.bin_tree_node import BinaryTreeNode
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_path import path
|
from utils.project_dir import get_path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import graphviz
|
import graphviz
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ class BinaryTree:
|
|||||||
self.tree_structure_traversal(define_node)
|
self.tree_structure_traversal(define_node)
|
||||||
_rec(self.root)
|
_rec(self.root)
|
||||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
dot.render(path(f"{self.graph_filename()}_{timestamp}.gv"))
|
dot.render(get_path(f"{self.graph_filename()}_{timestamp}.gv"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import pygame
|
import pygame
|
||||||
from utils.algo_game import Game
|
from utils.game import Game
|
||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from bin_tree import BinaryTree
|
from bin_tree import BinaryTree
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from collections.abc import Callable
|
|||||||
from utils.algo_context import AlgoContext
|
from utils.algo_context import AlgoContext
|
||||||
from utils.algo_array import Array
|
from utils.algo_array import Array
|
||||||
from utils.algo_int import Int
|
from utils.algo_int import Int
|
||||||
from utils.algo_range import Range
|
from utils.algo_range import irange
|
||||||
|
|
||||||
|
|
||||||
UNUSED_MARK = "UNUSED"
|
UNUSED_MARK = "UNUSED"
|
||||||
|
|||||||
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))
|
||||||
@ -5,8 +5,8 @@ import graphviz
|
|||||||
import math
|
import math
|
||||||
import heapq
|
import heapq
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from utils.algo_path import path
|
from utils.project_dir import get_path
|
||||||
from utils.algo_priority_queue import PriorityQueue
|
from utils.priority_queue import PriorityQueue
|
||||||
from vorlesung.L09_mst.disjoint import DisjointValue
|
from vorlesung.L09_mst.disjoint import DisjointValue
|
||||||
|
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ class Graph:
|
|||||||
dot.edge(str(id(self.get_vertex(edge[0]))), str(id(self.get_vertex(edge[1]))), label=str(edge[2]))
|
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")
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
filename = f"{filename}_{timestamp}.gv"
|
filename = f"{filename}_{timestamp}.gv"
|
||||||
filename = path(filename)
|
filename = get_path(filename)
|
||||||
dot.render(filename)
|
dot.render(filename)
|
||||||
|
|
||||||
def dijkstra(self, start_name: str) -> tuple[dict[Vertex, float], dict[Vertex, Vertex | None]]:
|
def dijkstra(self, start_name: str) -> tuple[dict[Vertex, float], dict[Vertex, Vertex | None]]:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user