MINT lernen

Übung AFB III

Zehn Aufgaben zum Verallgemeinern und Beurteilen — widerlegen, entwerfen, beweisen, Fehler finden und rekursive Verfahren begründet auswählen.

Dein Fortschritt:
0 / 0 Aufgaben
3

Aufgabenblock — AFB III

Begründen statt nur ausführen: Behauptungen widerlegen, rekursive Methoden entwerfen und implementieren, Verfahren beurteilen, Fehler finden und Aussagen allgemein zeigen. Jede Aufgabe hat ein prüfbares Kernergebnis — formuliere die Begründung trotzdem in ganzen Sätzen, bevor du die Musterlösung aufklappst.

A1
Immer n · log₂ n?
AFB III

Tom behauptet: „Mergesort braucht für \(n\) Elemente immer genau \(n\cdot\log_2 n\) Vergleiche.“ Widerlegen Sie die Behauptung mit der bereits sortierten Reihung 1, 2, …, 8.

Geben Sie für Ihr Gegenbeispiel die tatsächliche Anzahl der Vergleiche an.

Strategie: Ein einziges Gegenbeispiel mit einer anderen Vergleichszahl genügt.Warum so? Eine Aussage mit „immer genau“ ist falsch, sobald ein Fall abweicht.
Lösungsskizze: Ebene 1: 4 · 1, Ebene 2: 2 · 2, Ebene 3: 1 · 4 Vergleiche — beim Mischen sortierter Hälften wird nur die linke Hälfte verglichen.
Musterlösung anzeigen

Musterlösung: Für \(n = 8\) wäre \(n\log_2 n = 24\). Bei der sortierten Reihung ist beim Mischen jedes linke Element kleiner als jedes rechte; es werden nur die Elemente der linken Hälfte verglichen, der Rest der rechten wird ohne Vergleich angehängt. Ebene 1: \(4\cdot1\), Ebene 2: \(2\cdot2\), Ebene 3: \(1\cdot4\) — zusammen 12 Vergleiche.

Die Behauptung ist widerlegt. Richtig ist: \(n\log_2 n\) ist eine obere Schranke; der ungünstigste Fall liegt bei \(n\log_2 n - n + 1 = 17\), der günstigste bei \(\frac{n}{2}\log_2 n = 12\).

A2
Schnell potenzieren
AFB III

Die Potenz \(x^n\) soll nach Teile und herrsche berechnet werden: \(x^n = x^{n/2}\cdot x^{n/2}\) für gerades \(n\), \(x^n = x^{(n-1)/2}\cdot x^{(n-1)/2}\cdot x\) für ungerades \(n\), \(x^0 = 1\). Entwerfen Sie eine rekursive Methode static long potenz(long x, int n), die jedes Teilergebnis nur einmal berechnet.

Geben Sie an, wie viele Multiplikationen Ihre Methode für \(n = 1000\) ausführt.

Strategie: Ein rekursiver Aufruf mit \(n / 2\), Ergebnis in einer Variablen merken, dann ein- oder zweimal multiplizieren.Warum so? Zwei Aufrufe potenz(x, n / 2) * potenz(x, n / 2) würden dieselbe Arbeit doppelt machen.
Lösungsskizze: Exponenten 1000, 500, 250, 125, 62, 31, 15, 7, 3, 1, 0; gerade → 1 Multiplikation, ungerade → 2.
Musterlösung anzeigen

Musterlösung:

static long potenz(long x, int n) {
    if (n == 0) {
        return 1;
    }
    long h = potenz(x, n / 2);
    if (n % 2 == 0) {
        return h * h;
    }
    return h * h * x;
}

Exponenten der Aufrufkette: 1000, 500, 250 (gerade, je 1), 125 (ungerade, 2), 62 (1), 31, 15, 7, 3, 1 (ungerade, je 2), 0 (keine). Summe \(1 + 1 + 1 + 2 + 1 + 2 + 2 + 2 + 2 + 2 = \) 16 Multiplikationen statt 999 — höchstens \(2(\lfloor\log_2 n\rfloor + 1)\).

A3
fib(40) rekursiv?
AFB III

Eine App soll fib(40) mit der baumrekursiven Methode aus dem Unterricht berechnen. Für die Anzahl der Aufrufe gilt \(A(n) = 2\,\text{fib}(n+1) - 1\), und es ist \(\text{fib}(41) = 165\,580\,141\). Beurteilen Sie diesen Ansatz im Vergleich zur iterativen Fassung.

Geben Sie die Anzahl der Aufrufe in Millionen an (auf eine Nachkommastelle).

Mio.
Strategie: Aufwand in Zahlen ausdrücken und mit der Schleife vergleichen.Warum so? Ein Urteil braucht einen Maßstab — hier die 40 Durchläufe der iterativen Lösung.
Lösungsskizze: \(2\cdot165\,580\,141 - 1 = 331\,160\,281\); iterativ: 40 Durchläufe.
Musterlösung anzeigen

Musterlösung: \(A(40) = 2\cdot165\,580\,141 - 1 = 331\,160\,281\approx\) 331,2 Mio. Aufrufe. Dieselben Teilprobleme werden immer wieder neu berechnet (z. B. fib(38) zweimal, fib(37) dreimal …), der Aufwand wächst exponentiell. Die Rekursionstiefe ist mit 40 dagegen harmlos.

Die iterative Fassung braucht 40 Schleifendurchläufe. Urteil: Der baumrekursive Ansatz ist für die App ungeeignet — die Rekursion ist hier kein Teile und herrsche, weil sich die Teilprobleme überschneiden.

A4
Die hängende Suche
AFB III

In einer rekursiven binären Suche steht statt return binSuche(a, x, mitte + 1, rechts); die Zeile return binSuche(a, x, mitte, rechts);. Überprüfen Sie die Methode mit a = {2, 4, 6, 8}, x = 8 und dem Startaufruf binSuche(a, 8, 0, 3).

Geben Sie den Wert von links an, bei dem die Rekursion hängen bleibt.

Strategie: Verfolgen Sie links, rechts und mitte Aufruf für Aufruf.Warum so? Eine Rekursion endet nur, wenn jeder Selbstaufruf den Bereich echt verkleinert.
Lösungsskizze: (0, 3): mitte 1 → (1, 3): mitte 2 → (2, 3): mitte 2, a[2] = 6 < 8 → wieder (2, 3).
Musterlösung anzeigen

Musterlösung: Aufrufe: (0, 3) mit mitte = 1 (4 < 8) → (1, 3) mit mitte = 2 (6 < 8) → (2, 3) mit mitte = 2 (6 < 8) → (2, 3) → … Ab links = 2, rechts = 3 bleibt der Bereich gleich, weil mitte = (2 + 3) / 2 = 2 = links. Die Abbruchbedingung wird nie erreicht; es folgt ein StackOverflowError.

Korrektur: mitte + 1 — das Element a[mitte] ist nach dem Vergleich erledigt. Der Fehler tritt immer auf, wenn ein Bereich aus zwei Elementen nach rechts weitergesucht wird.

A5
Palindrome erkennen
AFB III

Ein Palindrom liest sich vorwärts wie rückwärts. Implementieren Sie eine rekursive Methode static boolean istPalindrom(String s), die erstes und letztes Zeichen vergleicht und dann den Rest ohne diese beiden Zeichen prüft.

Geben Sie an, wie viele Aufrufe Ihre Methode für "reliefpfeiler" (13 Zeichen) benötigt.

Strategie: Abbruch bei höchstens einem Zeichen (wahr) oder bei verschiedenen Randzeichen (falsch).Warum so? Jeder Aufruf macht das Problem um zwei Zeichen kleiner — der Basisfall muss Länge 0 und 1 abdecken.
Lösungsskizze: Längen 13, 11, 9, 7, 5, 3, 1 — beim Aufruf mit Länge 1 endet die Rekursion mit true.
Musterlösung anzeigen

Musterlösung:

static boolean istPalindrom(String s) {
    if (s.length() <= 1) {
        return true;
    }
    if (s.charAt(0) != s.charAt(s.length() - 1)) {
        return false;
    }
    return istPalindrom(s.substring(1, s.length() - 1));
}

„reliefpfeiler“: Längen 13, 11, 9, 7, 5, 3, 1 — 7 Aufrufe, Ergebnis true. Achtung: istPalindrom("Rentner") liefert false schon im ersten Aufruf, weil 'R' != 'r'; wer Groß- und Kleinschreibung ignorieren will, ruft die Methode mit s.toLowerCase() auf.

A6
Median aus drei
AFB III

Quicksort wählt als Pivot den Median aus erstem, mittlerem und letztem Element des Bereichs. Zeigen Sie, dass Quicksort damit auf der sortierten Reihung 1, 2, …, 15 jeden Bereich exakt halbiert, und geben Sie die Rekursionstiefe an (Aufrufe mit einem Element zählen mit).

Strategie: In einem sortierten Bereich ist das mittlere Element der Median der drei.Warum so? Liegt das Pivot genau in der Mitte, entstehen zwei gleich große Teile — dann wächst die Tiefe nur logarithmisch.
Lösungsskizze: Bereichsgrößen 15 → 7 → 3 → 1.
Musterlösung anzeigen

Musterlösung: Im sortierten Bereich gilt erstes ≤ mittleres ≤ letztes; der Median ist also das mittlere Element und damit der Median des ganzen Bereichs. Genau die Hälfte der übrigen Werte ist kleiner, die andere größer: 15 = 7 + 1 + 7. Die Teile bleiben sortiert (die Zerlegung tauscht kleinere Werte nur mit sich selbst), also wiederholt sich das: 7 = 3 + 1 + 3, 3 = 1 + 1 + 1.

Offene Aufrufe auf einem Weg: Bereiche der Größe 15, 7, 3, 1 — Rekursionstiefe 4 \(= \log_2 16\), statt 15 mit dem letzten Element als Pivot.

A7
Speicher für eine Million Zahlen
AFB III

Ein Messgerät liefert \(10^6\) Werte vom Typ int (je 4 Byte), die sortiert werden sollen. Der Arbeitsspeicher ist knapp. Bewerten Sie Mergesort und Quicksort (Pivot = Median aus drei) hinsichtlich des zusätzlichen Speichers.

Geben Sie den Speicher der Hilfsreihung beim letzten Mischen von Mergesort in MB an (1 MB = \(10^6\) Byte).

MB
Strategie: Zwei Arten von Zusatzspeicher unterscheiden: Hilfsreihung und Aufrufstapel.Warum so? Ein Urteil über Speicher braucht beide Posten mit Größenordnung.
Lösungsskizze: Mergesort: \(10^6\cdot4\) Byte = 4 MB plus Tiefe 21; Quicksort: keine Hilfsreihung, Tiefe meist einige Dutzend, ungünstig bis \(10^6\).
Musterlösung anzeigen

Musterlösung: Mergesort braucht beim letzten Mischen eine Hilfsreihung für alle \(10^6\) Werte: 4 MB — so viel wie die Daten selbst. Der Aufrufstapel ist mit \(\log_2 10^6 + 1\approx21\) Rahmen unbedeutend. Quicksort sortiert in-place; nur der Aufrufstapel wächst, im Mittel auf wenige Dutzend Rahmen. Mit Median-aus-drei ist der ungünstige Fall (Tiefe \(10^6\)) sehr unwahrscheinlich, aber nicht ausgeschlossen.

Bewertung: Bei knappem Speicher ist Quicksort vorzuziehen; wer eine Laufzeitgarantie braucht und die 4 MB übrig hat, nimmt Mergesort.

A8
Aufrufe allgemein zählen
AFB III

Das rekursive Maximum nach Teile und herrsche teilt jeden Bereich mit mindestens zwei Elementen in zwei nicht leere Teile und ruft sich für beide auf; Bereiche mit einem Element sind Basisfälle. Beweisen Sie, dass für \(n\) Elemente genau \(2n - 1\) Aufrufe entstehen.

Geben Sie die Anzahl der Aufrufe für \(n = 1000\) an.

Strategie: Zählen Sie Basisfälle und teilende Aufrufe getrennt. Wie viele Teillösungen gibt es vor und nach einer Zusammenführung?Warum so? Eine Invariante („jede Zusammenführung verringert die Zahl der Teillösungen um eins“) liefert den Beweis unabhängig von der Aufteilung.
Lösungsskizze: Jedes Element wird genau einmal Basisfall: \(n\). Jeder teilende Aufruf macht aus zwei Teillösungen eine: \(n - 1\) davon.
Musterlösung anzeigen

Musterlösung: Die Bereiche der Basisfälle zerlegen die Reihung lückenlos in einzelne Elemente, also gibt es genau \(n\) Basisfall-Aufrufe. Jeder teilende Aufruf verbindet zwei Teillösungen zu einer; aus \(n\) Teillösungen wird am Ende eine, dafür sind genau \(n - 1\) Zusammenführungen nötig — egal wie ungleich geteilt wird. Summe: \(n + (n - 1) = 2n - 1\).

Für \(n = 1000\): 1999 Aufrufe (davon 999 mit Vergleich). Alternativ per Induktion: \(A(1) = 1\), \(A(n) = 1 + A(p) + A(q)\) mit \(p + q = n\) ergibt \(1 + (2p - 1) + (2q - 1) = 2n - 1\).

A9
Erst sortieren, dann suchen?
AFB III Mix

Ein Archiv mit \(10^6\) unsortierten Einträgen soll durchsucht werden. Variante 1: jedes Mal linear suchen (ungünstigster Fall \(10^6\) Vergleiche). Variante 2: einmal mit Mergesort sortieren (rund \(n\log_2 n\) Vergleiche), dann rekursiv binär suchen (höchstens 20 Vergleiche). Entscheiden Sie begründet, ab wie vielen Suchen sich Variante 2 lohnt.

Geben Sie die kleinste Anzahl an Suchen an, ab der Variante 2 im ungünstigsten Fall weniger Vergleiche braucht.

Strategie: Gesamtaufwand beider Varianten als Funktion der Anzahl \(k\) der Suchen aufstellen und gleichsetzen.Warum so? Eine Entscheidung braucht eine nachvollziehbare Grenze — hier den Punkt, an dem sich der Sortieraufwand amortisiert.
Lösungsskizze: \(n\log_2 n\approx19{,}93\) Mio.; \(k\cdot10^6 > 19{,}93\cdot10^6 + 20k\).
Musterlösung anzeigen

Musterlösung: Variante 1: \(k\cdot10^6\). Variante 2: \(10^6\cdot\log_2 10^6 + 20k\approx19\,931\,569 + 20k\). Variante 2 ist günstiger, wenn \(k\cdot(10^6 - 20) > 19\,931\,569\), also \(k > 19{,}93\) — ab 20 Suchen.

Entscheidung: Wird das Archiv regelmäßig durchsucht und selten geändert, lohnt das einmalige Sortieren schon nach wenigen Tagen. Bei ständig neuen Einträgen müsste man die Sortierung erhalten (z. B. durch Einfügen an der richtigen Stelle).

A10
Rekursion vermeiden?
AFB III Mix

In einem Forum heißt es: „Rekursion sollte man immer vermeiden, weil sie den Stapel sprengt.“ Nehmen Sie Stellung und vergleichen Sie dazu die Rekursionstiefe der linearen Summe summe(a, i) (ein Element je Aufruf, Abbruch bei i == a.length - 1) mit der Summe nach Teile und herrsche für \(n = 10\,000\).

Geben Sie die Rekursionstiefe der Summe nach Teile und herrsche für \(n = 10\,000\) an.

Strategie: Tiefe beider Varianten ausrechnen, dann Argumente für und gegen die These sammeln.Warum so? Eine Stellungnahme wägt ab und kommt zu einem begründeten, differenzierten Ergebnis.
Lösungsskizze: Linear: Tiefe 10 000. Halbieren: \(\lceil\log_2 10\,000\rceil + 1 = 14 + 1\).
Musterlösung anzeigen

Musterlösung: Die lineare Summe hat Tiefe \(10\,000\) — damit kann der Stapel tatsächlich überlaufen. Die Summe nach Teile und herrsche halbiert: Bereichsgrößen 10 000, 5000, 2500, …, 1 — Tiefe \(\lceil\log_2 10\,000\rceil + 1 = \) 15. Das ist völlig unkritisch.

Stellungnahme: Die These ist zu pauschal. Problematisch ist Rekursion, die das Problem nur um ein Element verkleinert (Tiefe \(n\)) oder überlappende Teilprobleme mehrfach löst (fib). Teile und herrsche hat logarithmische Tiefe und liefert mit Mergesort und Quicksort die schnellsten Sortierverfahren. Rekursion gezielt einsetzen statt immer vermeiden.