158 lines
4.9 KiB
Markdown
158 lines
4.9 KiB
Markdown
# 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)
|
||
```
|