2026-03-31 11:31:18 +02:00
2026-03-27 17:19:51 +01:00
2026-03-31 11:31:18 +02:00
2026-03-31 11:30:26 +02:00
2026-03-30 13:09:16 +02:00
2026-03-31 11:31:18 +02:00
2026-03-27 17:19:51 +01:00

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

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

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.

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

insertion_sort(z, ctx)

print(ctx.comparisons)   # Anzahl Vergleiche
print(ctx.reads)         # Lesezugriffe
print(ctx.writes)        # Schreibzugriffe
print(ctx.additions)     # Additionen

Oder kompakt:

print(ctx.summary())

5. Komplexität über mehrere Eingabegrößen plotten

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:

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:

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)
Description
Begleitendes Repository zur Veranstaltung "Algorithmen und Datenstrukturen"
Readme 127 KiB
Languages
Python 100%