MINT lernen

Übung AFB II

Zehn Aufgaben zum Verknüpfen — Aufrufe verfolgen, Terminierung prüfen, Zeichenketten umkehren und Laufzeiten von Mergesort und Quicksort herleiten.

Dein Fortschritt:
0 / 0 Aufgaben
2

Aufgabenblock — AFB II

Zehn Aufgaben aus allen Unterkapiteln: Methoden analysieren, Ausgaben beim Ab- und Aufstieg ermitteln, Rekursion und Iteration vergleichen, Vergleichszahlen herleiten — mit gestuften Tipps, wenn du nicht weiterkommst.

A1
Eine unbekannte Methode
AFB II
static int m(int a, int b) {
    if (b == 0) {
        return a;
    }
    return m(b, a % b);
}

Analysieren Sie den Aufruf m(84, 36): Geben Sie den Rückgabewert und die Anzahl der Aufrufe an und nennen Sie, was die Methode berechnet.

Ansatz: Verfolgen Sie die Parameter: Aus (a, b) wird (b, a % b).
Rechenweg: 84 % 36 = 12 → m(36, 12); 36 % 12 = 0 → m(12, 0) liefert 12.
Lösung: a) 12 b) 3
Vollständige Lösung
Aufrufe: m(84, 36) → m(36, 12) → m(12, 0). Der dritte Aufruf erfüllt die Abbruchbedingung und liefert 12; der Wert wird unverändert nach oben durchgereicht. Die Methode berechnet den größten gemeinsamen Teiler nach dem euklidischen Algorithmus. Weil der Selbstaufruf die letzte Aktion ist (return m(…)), ist sie endrekursiv und lässt sich direkt als Schleife schreiben.
A2
Ausgabe vor und nach dem Aufruf
AFB II
static void p(int n) {
    if (n > 0) {
        System.out.print(n);
        p(n - 1);
        System.out.print(n);
    }
}

Ermitteln Sie die vollständige Ausgabe des Aufrufs p(3).

Ansatz: Die erste Ausgabe steht vor dem Selbstaufruf (Abstieg), die zweite danach (Aufstieg).
Rechenweg: Abstieg: 3, 2, 1 werden sofort ausgegeben; p(0) macht nichts; beim Aufstieg folgen 1, 2, 3.
Lösung: 321123
Vollständige Lösung
Beim Abstieg druckt jeder Aufruf seinen Wert, bevor er wartet: 321. p(0) erfüllt die Bedingung n > 0 nicht und kehrt sofort zurück. Beim Aufstieg setzt jeder wartende Aufruf hinter dem Selbstaufruf fort — der zuletzt begonnene zuerst: 123. Gesamt: 321123. Anweisungen nach dem Selbstaufruf laufen also in umgekehrter Reihenfolge.
A3
Terminiert das?
AFB II Trick
static int f(int n) {
    if (n == 0) {
        return 0;
    }
    return 1 + f(n - 2);
}

Untersuchen Sie die Methode: Geben Sie f(10) und die Anzahl der Aufrufe an. Für welche Eingaben endet die Rekursion nicht?

Ansatz: Der Parameter sinkt in Zweierschritten. Trifft er immer die 0?
Rechenweg: 10 → 8 → 6 → 4 → 2 → 0: sechs Aufrufe, fünfmal wird 1 addiert.
Lösung: a) 5 b) 6
Vollständige Lösung
f(10) = 1 + 1 + 1 + 1 + 1 + 0 = 5 nach 6 Aufrufen. Für ungerade n wird die 0 übersprungen (…, 3, 1, −1, −3, …): Die Abbruchbedingung wird nie erreicht, die Aufrufe laufen bis zum StackOverflowError. Dasselbe gilt für negative n. Abhilfe: Abbruch bei n <= 0 oder die Voraussetzung „n gerade und ≥ 0“ festlegen.
A4
Rekursiv oder mit Schleife?
AFB II Mix

Die Summe einer Reihung wird einmal mit einer Schleife und einmal rekursiv berechnet:

static int summe(int[] a, int i) {
    if (i == a.length) {
        return 0;
    }
    return a[i] + summe(a, i + 1);
}

Vergleichen Sie beide Fassungen für eine Reihung mit 6 Elementen und dem Startaufruf summe(a, 0): Wie viele Aufrufe braucht die rekursive, wie viele Schleifendurchläufe die iterative Fassung?

Ansatz: Der Aufruf mit i == a.length zählt mit, obwohl er kein Element mehr addiert.
Rechenweg: i = 0, 1, …, 5 addieren je ein Element; i = 6 liefert 0. Die Schleife läuft für i = 0 … 5.
Lösung: a) 7 b) 6
Vollständige Lösung
Rekursiv: Aufrufe für i = 0 bis 6, also 7, alle gleichzeitig offen (Tiefe 7). Iterativ: 6 Durchläufe in einem einzigen Methodenaufruf. Die Zahl der Additionen ist gleich; die rekursive Fassung braucht aber je Element einen Rahmen auf dem Aufrufstapel. Umformung: Schleifenvariable → Parameter i, verneinte Schleifenbedingung → Abbruchbedingung i == a.length.
A5
Eine Zeichenkette umkehren
AFB II
static String umkehren(String s) {
    if (s.length() <= 1) {
        return s;
    }
    return umkehren(s.substring(1)) + s.charAt(0);
}

Stellen Sie die Aufrufkette von umkehren("Regal") mit den Rückgabewerten dar und geben Sie das Ergebnis ein.

Ansatz: Jeder Aufruf hängt sein erstes Zeichen hinten an das umgekehrte Reststück.
Rechenweg: umkehren("l") = "l"; "al" → "l" + "a"; "gal" → "la" + "g"; "egal" → "lag" + "e"; "Regal" → "lage" + "R".
Lösung: lageR
Vollständige Lösung
umkehren("Regal") → umkehren("egal") → umkehren("gal") → umkehren("al") → umkehren("l") = "l". Aufstieg: "la", "lag", "lage", "lageR". Ergebnis lageR — das große R steht jetzt hinten, Groß- und Kleinschreibung bleiben erhalten.
A6
Aufrufe der binären Suche
AFB II

Die rekursive binäre Suche aus dem Unterricht (Abbruch ohne Treffer bei links > rechts, sonst ein Blick auf a[mitte] und höchstens ein Selbstaufruf) wird auf eine sortierte Reihung mit 100 Elementen angewendet. Leiten Sie her, wie viele Aufrufe von binSuche höchstens entstehen.

Ansatz: Jeder Aufruf mit nicht leerem Bereich halbiert ihn. Wie oft kann man 100 halbieren, bevor der Bereich leer ist?
Rechenweg: Höchstens \(\lfloor\log_2 100\rfloor + 1 = 7\) Aufrufe sehen ein Element an; ohne Treffer folgt ein Aufruf mit leerem Bereich.
Lösung: 8
Vollständige Lösung
Bereichsgrößen im ungünstigsten Fall: 100 → 50 → 25 → 12 → 6 → 3 → 1 → 0. Die sieben Aufrufe mit nicht leerem Bereich sehen je ein Element an (\(2^6 = 64 \le 100 < 128\), also \(\lfloor\log_2 100\rfloor + 1 = 7\)). Wird \(x\) nicht gefunden, kommt der achte Aufruf mit links > rechts hinzu: höchstens 8 Aufrufe — und genauso groß ist die Rekursionstiefe.
A7
Mergesort auf sortierten Daten
AFB II

Mergesort sortiert die schon aufsteigend sortierte Reihung 1, 2, …, 32. Berechnen Sie die Anzahl der Vergleiche.

Ansatz: Beim Mischen ist jedes linke Element kleiner als jedes rechte. Wie viele Vergleiche kostet dann ein Mischvorgang?
Rechenweg: Mischen zweier Hälften mit je \(p\) Elementen kostet \(p\) Vergleiche; jede Ebene also \(\frac{n}{2} = 16\); es gibt \(\log_2 32 = 5\) Ebenen.
Lösung: 80
Vollständige Lösung
Auch sortierte Daten werden vollständig geteilt und gemischt. Beim Mischen werden nur die \(p\) Elemente der linken Hälfte verglichen, dann ist sie leer und die rechte wird ohne Vergleich angehängt. Je Ebene \(\frac{n}{2} = 16\) Vergleiche, 5 Ebenen: \(16\cdot5 = \) 80 — das ist \(\frac{n}{2}\log_2 n\), der beste Fall. Zum Vergleich: Insertionsort bräuchte nur 31.
A8
Quicksort am Rand
AFB II

Quicksort mit dem letzten Element als Pivot erhält die sortierte Reihung 1, 2, …, 50. Bestätigen Sie durch Rechnung, dass Quicksort hier quadratisch arbeitet: Geben Sie die Anzahl der Vergleiche und die Rekursionstiefe an.

Ansatz: Das Pivot ist jedes Mal das Maximum seines Bereichs.
Rechenweg: Vergleiche 49 + 48 + … + 1; offene Aufrufe quicksort(a, 0, 49), (0, 48), …, (0, 0).
Lösung: a) 1225 b) 50
Vollständige Lösung
Jedes Zerlegen lässt das Pivot am rechten Rand stehen; der linke Teil ist nur um eins kleiner, der rechte leer. Vergleiche: \(49 + 48 + \dots + 1 = \frac{50\cdot49}{2} = \) 1225 \(= \frac{n(n-1)}{2}\) — quadratisch wie Selectionsort. Rekursionstiefe: Die Aufrufe für die Bereiche 0…49, 0…48, …, 0…0 sind gleichzeitig offen: 50.
A9
Eine Rekursionsgleichung aufstellen
AFB II

Mergesort teilt ohne Vergleiche und mischt zwei Hälften mit höchstens \(n\) Vergleichen (vereinfacht). Stellen Sie die Rekursionsgleichung \(T(n)\) mit \(T(1) = 0\) auf und berechnen Sie \(T(64)\).

Ansatz: Zwei Teilprobleme halber Größe plus der Aufwand fürs Mischen.
Rechenweg: \(T(n) = 2\,T\!\left(\frac{n}{2}\right) + n\): T(2) = 2, T(4) = 8, T(8) = 24, T(16) = 64, T(32) = 160, T(64) = 384.
Lösung: 384
Vollständige Lösung
\(T(n) = 2\,T\!\left(\frac{n}{2}\right) + n\), \(T(1) = 0\). Einsetzen: \(T(2) = 2\cdot0 + 2 = 2\), \(T(4) = 2\cdot2 + 4 = 8\), \(T(8) = 24\), \(T(16) = 64\), \(T(32) = 160\), \(T(64) = 2\cdot160 + 64 = \) 384. Das ist genau \(n\log_2 n = 64\cdot6\).
A10
Wie viele Aufrufe braucht fib?
AFB II

Für die Anzahl der Aufrufe von fib(n) (Abbruch bei n <= 1) gilt \(A(n) = 1 + A(n-1) + A(n-2)\) mit \(A(0) = A(1) = 1\). Weisen Sie durch Einsetzen nach, wie viele Aufrufe fib(10) braucht.

Ansatz: Rechnen Sie die Werte von unten nach oben aus — wie bei einer Tabelle.
Rechenweg: A(2) = 3, A(3) = 5, A(4) = 9, A(5) = 15, A(6) = 25, A(7) = 41, A(8) = 67, A(9) = 109.
Lösung: 177
Vollständige Lösung
Tabelle: A(2) = 3, A(3) = 5, A(4) = 9, A(5) = 15, A(6) = 25, A(7) = 41, A(8) = 67, A(9) = 109, A(10) = 1 + 109 + 67 = 177. Es gilt \(A(n) = 2\,\text{fib}(n+1) - 1\) (hier \(2\cdot89 - 1\)). Dieselben Teilprobleme werden immer wieder berechnet — die iterative Fassung braucht nur 10 Durchläufe. Anders als bei Teile und herrsche überschneiden sich hier die Teilprobleme.