← Startseite

Labor 2: Algorithmen denken — auf Papier und in Code

Fortgeschrittene Algorithmen und Programmierung • HTW Berlin
Dauer: 180 Minuten (3 Stunden)

🧠 Worum geht es?

In Vorlesung 3 habt ihr drei Algorithmen kennengelernt: den Münzwechsel, den Gauß-Trick und den Palindrom-Check. Außerdem in Vorlesung 2 die Datentypen und Eingabe/Ausgabe in C.

Heute setzt ihr beides um — aber mit einer wichtigen Drehung:

🎯 Lernziele

📝 Pseudocode

Algorithmus in Klartext aufschreiben, bevor Code entsteht

♻️ Drei Algorithmen

Münzwechsel, Gauß-Trick, Palindrom selbst implementieren

📊 Vergleichen

Schleife vs. Formel — wie viel macht der Algorithmus aus?

🔣 Datentypen-Praxis

Wann %d, wann %f? Was passiert bei 30/7?

ℹ️ Vorgehen pro Aufgabe

  1. Problem lesen — was ist Eingabe, was ist Ausgabe?
  2. Pseudocode schreiben — Schritt für Schritt in Klartext (auf Papier oder als Kommentar)
  3. Mit Sitznachbar besprechen — kann er/sie deinen Algorithmus nachvollziehen?
  4. In Code übersetzen — bevorzugt C, aber jede Sprache geht
  5. Mit Musterlösung vergleichen — Passwort am Ende der Aufgabe

1 Pseudocode für ein Alltagsproblem ⏱️ 20 Min

Ziel: Sich daran gewöhnen, einen Algorithmus auf Papier zu denken — bevor man Code schreibt.

Aufgabenstellung:

Wähle eine dieser Alltagsaufgaben:

  • 🥞 Pfannkuchen backen
  • 📚 Ein bestimmtes Buch in der Bibliothek finden
  • 🦷 Zähne putzen (gründlich, alle Schritte)
  • ☕ Tee mit Milch und Zucker zubereiten
  • 🚇 Mit der U-Bahn von zu Hause zur HTW fahren

Schreibe den Pseudocode dafür — 5 bis 10 Schritte. Achte auf:

  • Endlich: hat ein klares Ende
  • Eindeutig: jeder Schritt ist klar (kein „mach's einfach schön")
  • Ausführbar: ein Roboter könnte es nachmachen
Beispiel — Pseudocode für „Glas Wasser holen":
1. Stehe vom Stuhl auf 2. Gehe in die Küche 3. Öffne den Schrank 4. Nimm ein leeres Glas heraus 5. Halte es unter den Wasserhahn 6. Drehe den Hahn auf 7. Warte bis das Glas voll ist 8. Drehe den Hahn zu 9. Trinke 🥤

Diskussion mit Sitznachbar:

Tausch deinen Pseudocode mit jemandem aus. Versucht, den jeweils anderen Algorithmus „im Kopf auszuführen". Funktioniert er? Welche Schritte sind unklar?

💡 Wichtige Erkenntnis: 90% der Bugs entstehen, weil man zu früh angefangen hat zu tippen. Pseudocode kostet 5 Minuten und spart 50.

2 Münzwechsel — der Greedy-Algorithmus ⏱️ 30 Min

Aus VL3: Du arbeitest an der Kasse. Ein Kunde soll Wechselgeld zurückbekommen — mit so wenigen Münzen wie möglich.

Problem:

  • Eingabe: Betrag in Cent (z.B. 287 für 2,87 €)
  • Ausgabe: Liste der zurückgegebenen Münzen
  • Verfügbare Münzen: 200, 100, 50, 20, 10, 5, 2, 1 Cent

Schritt 1 — Pseudocode:

Schreibe selbst den Pseudocode auf, bevor du den Code anschaust. Tipp: die Strategie heißt Greedy — „nimm immer das Größte zuerst".

// Schreibe hier deinen Pseudocode

Schritt 2 — Implementieren:

Setze deinen Pseudocode in Code um. Bevorzugt C, aber wenn du eine andere Sprache flüssiger sprichst — auch gut.

Teste mit:

  • 183 Cent → erwartet: 1 € + 50 ct + 20 ct + 10 ct + 2 ct + 1 ct (6 Münzen)
  • 500 Cent → erwartet: 2 € + 2 € + 1 € (3 Münzen)
  • 97 Cent → erwartet: 50 ct + 20 ct + 20 ct + 5 ct + 2 ct (5 Münzen)
🤔 Denkfrage: Funktioniert Greedy immer? Versuche es mit einer hypothetischen Münzsorte [1, 3, 4] und Betrag 6. Greedy würde geben: 4 + 1 + 1 = 3 Münzen. Aber die optimale Lösung ist: 3 + 3 = 2 Münzen. → Greedy ist nicht immer optimal, aber für unsere Euro-Münzen schon.
🔒 Musterlösung in C
Falsches Passwort!
#include <stdio.h>

void wechselgeld(int cent) {
    int muenzen[] = {200, 100, 50, 20, 10, 5, 2, 1};

    for (int i = 0; i < 8; i++) {
        while (cent >= muenzen[i]) {
            printf("%d Cent\n", muenzen[i]);
            cent -= muenzen[i];
        }
    }
}

int main() {
    int betrag;
    printf("Betrag in Cent: ");
    scanf("%d", &betrag);
    wechselgeld(betrag);
    return 0;
}

Erklärung: Äußere for-Schleife geht durch alle Münzsorten von groß nach klein. Innere while-Schleife nimmt so viele Stück dieser Sorte, wie noch in den Restbetrag passen.

3 Summe 1 bis N — drei Wege, drei Geschwindigkeiten ⏱️ 35 Min

Aus VL3: die berühmte Aufgabe vom kleinen Gauß. Berechne die Summe 1 + 2 + 3 + … + N.

Aufgabe:

Implementiere zwei Versionen, die dasselbe Ergebnis liefern, aber unterschiedlich vorgehen:

  1. Version A — Schleife: addiere jede Zahl einzeln
  2. Version B — Gauß-Formel: n * (n+1) / 2

Schritt 1 — beide Versionen schreiben

Schreibe beide Funktionen. Beide sollen für n=100 dasselbe Ergebnis liefern (5050).

Schritt 2 — Laufzeit messen

Nutze clock() aus <time.h>, um die Dauer zu messen — bei n = 1.000.000.000 (eine Milliarde).

Messen mit clock():
#include <time.h>

clock_t start = clock();
// ... Algorithmus läuft ...
clock_t ende = clock();
double sek = (double)(ende - start) / CLOCKS_PER_SEC;
printf("Dauer: %.6f Sekunden\n", sek);
💡 Was machen diese Funktionen wirklich?
  • <time.h> — die Standard-C-Bibliothek für alles rund um Zeit (Datum, Uhrzeit, Stoppuhr).
  • clock() — fragt das Betriebssystem: „wie viele CPU-Ticks hat mein Programm seit dem Start verbraucht?". Ein „Tick" ist eine kleine, fixe Zeiteinheit der CPU.
  • clock_t — der Datentyp, in dem diese Tick-Zahl steckt (intern meist ein long). Wir können damit rechnen wie mit jedem Integer.
  • CLOCKS_PER_SEC — eine vom Compiler vorgegebene Konstante, die sagt, wie viele Ticks pro Sekunde gezählt werden (typisch 1.000.000 auf macOS/Linux).
  • ende - start — Differenz: wie viele Ticks ist mein Algorithmus gelaufen?
  • (double)(ende - start) — Cast zu double! Ohne das würde C eine Ganzzahl-Division machen und Nachkommastellen abschneiden (siehe Aufgabe 5).
  • / CLOCKS_PER_SEC — Ticks geteilt durch Ticks-pro-Sekunde = Sekunden.

Kurzformel: Sekunden = Ticks ÷ Ticks-pro-Sekunde.

🤔 Denkfrage 1: Was passiert, wenn du den Cast (double) weglässt? Probier's aus — und du verstehst sofort, warum Aufgabe 5 wichtig ist.
🤔 Denkfrage 2: Wieviel schneller ist B als A bei n = 1.000.000.000? Schätze, bevor du misst — und vergleiche dann.

Schritt 3 — Reflexion

Schreibe in 1-2 Sätzen auf:

  • Welcher Algorithmus war schneller?
  • Warum?
  • Was hat das mit der „Wahl der Programmiersprache" zu tun?
🔒 Musterlösung in C
Falsches Passwort!
#include <stdio.h>
#include <time.h>

long long summe_schleife(long long n) {
    long long summe = 0;
    for (long long i = 1; i <= n; i++) {
        summe += i;
    }
    return summe;
}

long long summe_gauss(long long n) {
    return n * (n + 1) / 2;
}

int main() {
    long long N = 1000000000LL;  // 1 Milliarde

    clock_t s1 = clock();
    long long r1 = summe_schleife(N);
    double t1 = (double)(clock() - s1) / CLOCKS_PER_SEC;

    clock_t s2 = clock();
    long long r2 = summe_gauss(N);
    double t2 = (double)(clock() - s2) / CLOCKS_PER_SEC;

    printf("Schleife: %lld in %.6f s\n", r1, t1);
    printf("Gauß:     %lld in %.6f s\n", r2, t2);
    return 0;
}

Typische Ergebnisse: Schleife ~ 2-3 Sekunden, Gauß < 0,000001 Sekunden. Faktor: ~ 3.000.000×. Gleiche Sprache, gleicher Compiler, gleicher Rechner — nur die Idee ist besser.

Wichtig: long long brauchen wir, weil das Ergebnis (5×10¹⁷) nicht in einen normalen int passt.

4 Palindrom-Check ⏱️ 25 Min

Aus VL3: ein Wort ist ein Palindrom, wenn es vorwärts und rückwärts gleich gelesen wird (ANNA, OTTO, RACECAR).

Aufgabe:

  • Eingabe: ein Wort als String
  • Ausgabe: 1 wenn Palindrom, 0 sonst

Schritt 1 — Pseudocode

Schreibe den Algorithmus auf Papier. Tipp aus VL3: vergleiche das erste mit dem letzten, dann das zweite mit dem vorletzten, …

Schritt 2 — Implementieren

Teste mit: ANNA, OTTO, HALLO, RACECAR, HTW.

💡 In C: ein String ist ein Array von char. strlen(wort) aus <string.h> gibt dir die Länge.
🤔 Smart-Tipp: sobald ein Paar nicht passt, kannst du sofort abbrechen und 0 zurückgeben. Du musst nicht alle Buchstaben prüfen.
🔒 Musterlösung in C
Falsches Passwort!
#include <stdio.h>
#include <string.h>

int ist_palindrom(char wort[]) {
    int n = strlen(wort);
    int links = 0;
    int rechts = n - 1;

    while (links < rechts) {
        if (wort[links] != wort[rechts]) {
            return 0;          // sofort abbrechen
        }
        links++;
        rechts--;
    }
    return 1;                  // alle Paare passten
}

int main() {
    char *tests[] = {"ANNA", "OTTO", "HALLO", "RACECAR", "HTW"};
    for (int i = 0; i < 5; i++) {
        printf("%-10s: %s\n", tests[i],
               ist_palindrom(tests[i]) ? "Palindrom ✓" : "kein Palindrom");
    }
    return 0;
}

Idee: zwei Variablen wandern aufeinander zu. Sobald ein Paar nicht passt → raus.

5 Datentypen-Praxis: %d, %f und Ganzzahl-Division ⏱️ 25 Min

Aus VL2: verschiedene Datentypen, verschiedene Format-Specifier. Hier die typischen Stolperfallen.

Aufgabe — Untersuche, was passiert:

Schreibe ein Programm casting.c, das folgende Berechnungen ausgibt:

  1. 30 / 7 als %d (int)
  2. 30.0 / 7 als %f (double)
  3. (double)30 / 7 als %f
  4. 30 / 7 als %fAchtung!

Vorhersage vor dem Tippen:

Schreibe für jede Zeile vorher auf, was du erwartest. Dann probier's aus. Wer hat recht?

💡 Hintergrund:
  • int / int in C → das Ergebnis ist int (Rest wird abgeschnitten)
  • int / double oder double / intdouble
  • %d erwartet int, %f erwartet double — niemals mischen!
🤔 Klausur-Frage: warum kommt bei Zeile 4 (30/7 als %f) so eine seltsame Zahl raus? Hinweis: printf liest die falschen Bytes als double.
🔒 Musterlösung + Erklärung
Falsches Passwort!
#include <stdio.h>

int main() {
    printf("30 / 7 (als %%d):    %d\n", 30 / 7);
    printf("30.0 / 7 (als %%f):  %f\n", 30.0 / 7);
    printf("(double)30 / 7:     %f\n", (double)30 / 7);
    printf("30 / 7 (als %%f):    %f  ← undefined behavior!\n", 30 / 7);
    return 0;
}

Erwartete Ausgabe:

30 / 7 (als %d):    4
30.0 / 7 (als %f):  4.285714
(double)30 / 7:     4.285714
30 / 7 (als %f):    0.000000  ← oder Müll, je nach Compiler

Lehre: in C musst du immer wissen, welchen Typ deine Werte haben. Das ist anders als z.B. in Python, wo der Interpreter das automatisch macht — aber dafür ist C schneller und deterministischer.

6 Algorithmus erkennen — Theorie-Quickie ⏱️ 20 Min

Ziel: aus einer Alltagsbeschreibung den passenden Algorithmus erkennen — die Brille der Algorithmen-Welt aufsetzen.

Welcher Algorithmus passt?

Zu jedem Szenario: welcher Algorithmus aus VL3 passt? (Münzwechsel/Greedy · Gauß-Formel · Palindrom-Check · keiner)

  1. An der Supermarkt-Kasse berechnest du, wie viel Wechselgeld du dem Kunden gibst — mit so wenig Münzen wie möglich. → ?
  2. Dein Lehrer fragt: „Was ist 1 + 2 + 3 + … + 1000?" — du brauchst eine Sekunde. → ?
  3. Du prüfst, ob das Wort „LAGERREGAL" rückwärts gleich ist. → ?
  4. Du suchst die billigste Route in Google Maps von A nach B. → ?
  5. Beim Zähneputzen folgst du immer derselben Reihenfolge: erst oben links, dann oben rechts, dann unten. → ?

Bonus-Frage:

Für Szenario 4 (Google Maps): das ist ein Graph-Algorithmus — dazu kommen wir später. Aber kannst du raten, was die Knoten und Kanten in Google Maps sind?

🔒 Auflösung
Falsches Passwort!
  1. Münzwechsel / Greedy — genau der Algorithmus aus Aufgabe 2.
  2. Gauß-Formel — n · (n+1) / 2 = 1000 · 1001 / 2 = 500.500.
  3. Palindrom-Check — LAGERREGAL ist ein Palindrom (lies rückwärts).
  4. Graph-Algorithmus (kommt später, in VL10/11) — kein bisheriger.
  5. Algorithmus, aber kein neuer — eine fixe Reihenfolge ohne Eingabe ist eher ein „Skript" als ein interessanter Algorithmus.

Bonus: in Google Maps sind Knoten = Kreuzungen, Kanten = Straßenabschnitte (mit Länge/Zeit als Gewicht). Der Algorithmus heißt Dijkstra — wir lernen ihn in VL11.

7 Bonus: FizzBuzz — der Klassiker ⏱️ 25 Min · BONUS

FizzBuzz ist eine berühmte Aufgabe in Bewerbungsgesprächen. Sie testet, ob du in Algorithmen denken kannst.

Aufgabe:

Schreibe ein Programm, das die Zahlen von 1 bis 100 ausgibt — mit folgender Regel:

  • Wenn die Zahl durch 3 teilbar ist → gib „Fizz" aus statt der Zahl
  • Wenn die Zahl durch 5 teilbar ist → gib „Buzz" aus statt der Zahl
  • Wenn durch 3 UND 5 teilbar → gib „FizzBuzz" aus
  • Sonst → gib einfach die Zahl aus
Erwartete Ausgabe (Anfang):
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
...
💡 Hilfreich: der Modulo-Operator % gibt dir den Rest einer Division. 15 % 3 ergibt 0 → also durch 3 teilbar.
🤔 Reihenfolge zählt: in welcher Reihenfolge prüfst du die Bedingungen? Was passiert, wenn du zuerst auf „durch 3" prüfst und dann auf „durch 3 und 5"?
🔒 Musterlösung
Falsches Passwort!
#include <stdio.h>

int main() {
    for (int i = 1; i <= 100; i++) {
        if (i % 15 == 0) {           // erst die spezifischste Bedingung!
            printf("FizzBuzz\n");
        } else if (i % 3 == 0) {
            printf("Fizz\n");
        } else if (i % 5 == 0) {
            printf("Buzz\n");
        } else {
            printf("%d\n", i);
        }
    }
    return 0;
}

Pointe: wir prüfen i % 15 zuerst, weil 15 = 3 × 5. Wenn du das nicht machst und mit i % 3 startest, kriegst du nie „FizzBuzz" — sondern immer nur „Fizz".

Reflexion: derselbe Algorithmus in Python wäre fast identisch — nur mit weniger Klammern. Die Idee ist die Sprache.

✅ Was du nach diesem Lab können solltest

🎯 Reflexion am Ende

Bevor du gehst, beantworte für dich:

  1. Welche Aufgabe war für dich am leichtesten? Warum?
  2. Welche am schwersten? Warum?
  3. Hast du einmal Pseudocode geschrieben, bevor du getippt hast? Hat es geholfen?
  4. Welche Algorithmen hast du heute praktisch angewandt — auch wenn nicht im Code?

→ Diese Reflexion ist freiwillig, aber sie zementiert das Gelernte.

🔑 Passwort-Übersicht

Passwörter werden im Lab durch die Lehrkraft bekannt gegeben — bitte erst probieren, dann vergleichen.

AufgabeThemaPasswort
Aufgabe 1Pseudocode-Übungkeine Lösung — eigene Antwort
Aufgabe 2Münzwechsel (Greedy)wechsel
Aufgabe 3Summe 1..N (Gauß)gauss
Aufgabe 4Palindrom-Checkanna
Aufgabe 5Datentypen-Praxiscast
Aufgabe 6Algorithmus erkennenalgorithmus
Aufgabe 7FizzBuzz (Bonus)fizzbuzz
← Startseite

© 2026 HTW Berlin · Prof. Dr. Alexandra Mikityuk