AlgoDatSoSe26/README.md
2026-03-31 11:31:18 +02:00

158 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)
```