Aufgabenblock — AFB III
Begründen statt nur ausführen: Behauptungen widerlegen, Algorithmen entwerfen und implementieren, Verfahren beurteilen, Fehler finden und Formeln allgemein zeigen. Jede Aufgabe hat ein prüfbares Kernergebnis — formuliere die Begründung trotzdem in ganzen Sätzen, bevor du die Musterlösung aufklappst.
Mia behauptet: „Auf einer sortierten Reihung ist die binäre Suche immer schneller als die lineare Suche.“ Widerlegen Sie die Behauptung mit der sortierten Reihung gerade = {2, 4, 6, …, 62} (31 Elemente).
Geben Sie für Ihr Gegenbeispiel die Suche nach dem Wert 2 an: Wie viele Elemente sieht die binäre Suche an?
Musterlösung anzeigen
Musterlösung: Gesucht wird der Wert 2, das erste Element. Die lineare Suche findet ihn nach genau einem Vergleich. Die binäre Suche beginnt in der Mitte: mitte = 15, 7, 3, 1, 0 — erst im fünften Schritt ist gerade[0] = 2 erreicht.
In diesem Fall ist die lineare Suche also schneller; die Behauptung ist widerlegt. Richtig ist: Im ungünstigsten Fall (und im Mittel bei großen \(n\)) braucht die binäre Suche viel weniger Schritte (\(\lfloor\log_2 n\rfloor+1\) statt \(n\)).
Aus den Punktzahlen eines Wettbewerbs soll der zweitgrößte verschiedene Wert ermittelt werden, ohne die Reihung zu sortieren und mit nur einem Durchlauf.
Entwerfen Sie einen Algorithmus als Struktogramm. Welchen Wert liefert er für die abgebildete Reihung?
x > max: zweit ← max, max ← x. Sonst, wenn x < max und x > zweit: zweit ← x. Gleiche Werte wie max ignorieren.Musterlösung anzeigen
Musterlösung:
Ablauf: max/zweit = 14/−∞ → 27/14 → 27/14 (9) → 27/14 (27 gleich max, ignoriert) → 27/21 → 27/21 (18). Ergebnis 21. Ohne die Bedingung punkte[i] < max würde die zweite 27 als „zweitgrößter“ Wert gespeichert. Der Algorithmus braucht höchstens \(2(n-1)\) Vergleiche — linear, statt quadratisch wie Sortieren mit Selectionsort.
Bevor ein Programm binär sucht, soll es prüfen, ob die Reihung aufsteigend sortiert ist (gleiche Nachbarn sind erlaubt). Implementieren Sie die Methode boolean istSortiert(int[] a) in Java so, dass sie beim ersten falsch geordneten Nachbarpaar abbricht.
Wie viele Vergleiche zwischen Nachbarn führt Ihre Methode für die abgebildete Reihung aus?
a[i − 1] und a[i] für i ab 1 prüfen; beim ersten Paar mit a[i] < a[i − 1] sofort false zurückgeben.Warum so? Sortiert heißt: kein Paar steht falsch herum. Ein einziges falsches Paar reicht als Gegenbeweis.false.Musterlösung anzeigen
Musterlösung:
public static boolean istSortiert(int[] a) {
for (int i = 1; i < a.length; i++) {
if (a[i] < a[i - 1]) {
return false; // erstes falsches Paar
}
}
return true; // kein falsches Paar gefunden
}
Für die Reihung werden (2,5), (5,5), (5,9) und (9,4) geprüft: 4 Vergleiche, Ergebnis false. Bei einer sortierten Reihung der Länge \(n\) sind es \(n-1\) Vergleiche. Wichtig: return true steht nach der Schleife; ein else return true in der Schleife würde schon nach dem ersten Paar antworten. Die Bedingung < (nicht <=) lässt gleiche Nachbarn zu.
Eine Staffel-Liste ist bereits alphabetisch geordnet und soll nun nach Punkten aufsteigend sortiert werden. Bei gleicher Punktzahl soll die alphabetische Reihenfolge erhalten bleiben.
| Index | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| Name | Ben | Emil | Jana | Lotta |
| Punkte | 7 | 9 | 12 | 12 |
Die Liste wurde vor dem Sortieren versehentlich umgestellt zu Jana 12 · Emil 9 · Lotta 12 · Ben 7. Beurteilen Sie, ob Selectionsort oder Insertionsort für diese Anforderung geeignet ist. An welchem Index steht Jana, nachdem Selectionsort die umgestellte Liste nach Punkten sortiert hat?
Musterlösung anzeigen
Musterlösung: Selectionsort: D1 vertauscht Jana ↔ Ben → Ben 7 · Emil 9 · Lotta 12 · Jana 12. Die weiteren Durchläufe ändern nichts. Jana steht an Index 3, also hinter Lotta, obwohl sie vorher vor ihr stand. Das weite Vertauschen über andere Elemente hinweg macht Selectionsort instabil.
Insertionsort verschiebt nur, solange das linke Element echt größer ist, und überholt gleich große Elemente nie: Ergebnis Ben · Emil · Jana · Lotta. Insertionsort (ebenso Bubblesort mit >) ist stabil und erfüllt die Anforderung; Selectionsort ist ungeeignet.
Eine unsortierte Reihung enthält 1000 Seriennummern. Es sollen \(k\) Suchanfragen beantwortet werden. Variante A sucht jedes Mal linear. Variante B sortiert einmal mit Selectionsort und sucht dann binär. Gerechnet wird jeweils im ungünstigsten Fall mit der Anzahl der Vergleiche.
Erörtern Sie, wann welche Variante sinnvoll ist. Ab welcher Anzahl \(k\) von Suchanfragen braucht Variante B insgesamt weniger Vergleiche?
Musterlösung anzeigen
Musterlösung: Variante A: \(1000\) Vergleiche pro Suche, also \(1000k\). Variante B: Sortieren \(\frac{1000\cdot999}{2} = 499\,500\), dann je Suche \(\lfloor\log_2 1000\rfloor + 1 = 10\): \(499\,500 + 10k\).
\(499\,500 + 10k < 1000k \iff 990k > 499\,500 \iff k > 504{,}5\). Ab 505 Suchanfragen ist B günstiger (504 550 gegen 505 000 Vergleiche).
Abwägung: Bei wenigen Anfragen oder häufig wechselnden Daten ist A besser, weil nach jeder Änderung neu sortiert werden müsste. Bei vielen Anfragen auf festen Daten gewinnt B deutlich. Mit einem schnelleren Sortierverfahren als Selectionsort läge die Grenze viel niedriger.
Bei einem Wettkampf interessieren nur die drei schnellsten Zeiten (die kleinsten Werte). Sie sollen an den Anfang der Reihung mit \(n = 10\) Zeiten gebracht werden; der Rest darf unsortiert bleiben.
Verändern Sie Selectionsort so, dass es genau das leistet. Wie viele Vergleiche braucht Ihr Verfahren für \(n = 10\)?
i = 0, 1, 2. Vergleiche: \(9 + 8 + 7\).Musterlösung anzeigen
Musterlösung: Die äußere Schleife läuft nur bis i < 3 statt bis i < n − 1:
for (int i = 0; i < 3; i++) { // statt i < zeit.length - 1
int min = i;
for (int j = i + 1; j < zeit.length; j++) {
if (zeit[j] < zeit[min]) {
min = j;
}
}
int h = zeit[i]; zeit[i] = zeit[min]; zeit[min] = h;
}
Vergleiche: \(9 + 8 + 7 = 24\) statt \(\frac{10\cdot9}{2} = 45\). Allgemein für die \(k\) kleinsten Werte: \((n-1) + (n-2) + \dots + (n-k)\), bei festem \(k\) also nur linear in \(n\). Insertionsort oder Bubblesort (Größte nach hinten) eignen sich dafür nicht so direkt.
In einer binären Suche steht die Schleifenbedingung while (links < rechts) statt while (links <= rechts). Sonst ist alles korrekt.
Analysieren Sie die fehlerhafte Version, indem Sie jeden der fünf Werte suchen. Wie viele der fünf enthaltenen Werte findet sie nicht?
links == rechts gilt.Warum so? Ein Grenzfehler in der Bedingung zeigt sich nur in bestimmten Fällen — systematisches Testen aller Werte deckt ihn auf.Musterlösung anzeigen
Musterlösung: 5, 12 und 17 werden gefunden. Bei 8 und 23 schrumpft der Suchbereich auf ein Element (links = rechts = 1 bzw. = 4); die Bedingung links < rechts ist dann falsch, und die Methode gibt −1 zurück, obwohl der Wert genau dort steht. Also werden 2 Werte nicht gefunden.
Korrektur: while (links <= rechts) — ein Bereich aus einem einzigen Element muss noch geprüft werden. Solche Fehler findet man mit Tests am Rand (erstes, letztes Element) und bei Bereichen der Länge 1.
Zeigen Sie, dass Selectionsort für \(n\) Werte genau \(\frac{n(n-1)}{2}\) Vergleiche braucht. Ab welchem \(n\) sind es erstmals mehr als eine Million Vergleiche?
Musterlösung anzeigen
Musterlösung: Im Durchlauf mit Index \(i\) (von 0 bis \(n-2\)) wird das aktuelle Minimum mit allen \(n-1-i\) Elementen rechts davon verglichen. Das ergibt \(S=(n-1)+(n-2)+\dots+1\). Schreibt man die Summe zusätzlich rückwärts darunter und addiert, entsteht \(n-1\)-mal der Wert \(n\): \(2S=n(n-1)\), also \(S=\frac{n(n-1)}{2}\).
\(n = 1414\): \(\frac{1414\cdot1413}{2}=998\,991\); \(n = 1415\): \(\frac{1415\cdot1414}{2}=1\,000\,405\). Also ab \(n = 1415\). Die Anzahl hängt nicht von der Vorsortierung ab, weil jede Minimumsuche den ganzen Rest ansieht.
Eine Wetterstation speichert ein Jahr lang stündlich die Luftfeuchte in double[][] feuchte = new double[365][24]; (ein double belegt 8 Byte). Jan schlägt vor, zum Sortieren jedes Tages eine Kopie des ganzen Jahres anzulegen und dort zu sortieren.
Bewerten Sie den Vorschlag unter dem Gesichtspunkt Speicherbedarf. Wie viele Byte belegt die Reihung feuchte (ohne Verwaltungsdaten)?
Musterlösung anzeigen
Musterlösung: \(365\cdot24\cdot8 = 70\,080\) Byte (≈ 68 KiB). Eine Kopie des ganzen Jahres pro Tag würde denselben Speicher noch einmal belegen, bei 365 Kopien gleichzeitig sogar etwa 25 MB — ohne jeden Nutzen.
Bewertung: Der Vorschlag ist ungünstig. Die drei behandelten Sortierverfahren arbeiten in der Reihung selbst und brauchen nur eine Hilfsvariable zum Tauschen. Soll die Originalreihenfolge erhalten bleiben, genügt eine Kopie eines Tages (feuchte[t], 24 Werte, 192 Byte). Sinnvoll: Speicher sparen, wenn die Daten es erlauben.
Eine Reihung von Startnummern ist bis auf ein vertauschtes Nachbarpaar sortiert:
Entscheiden Sie begründet, welches der drei Sortierverfahren hier am wenigsten Vergleiche braucht (Bubblesort mit Abbruchbedingung). Wie viele Vergleiche braucht Insertionsort?
Musterlösung anzeigen
Musterlösung: Selectionsort braucht immer \(\frac{10\cdot9}{2}=45\) Vergleiche. Bubblesort mit Abbruch: Durchlauf 1 (9 Vergleiche) tauscht 16 und 15, Durchlauf 2 (8 Vergleiche) bestätigt die Ordnung → 17. Insertionsort: Jedes Element außer 15 braucht einen Vergleich (8), die 15 vergleicht mit 16 (verschieben) und 14 (stopp) → 10 Vergleiche, eine Verschiebung.
Entscheidung: Insertionsort. Es nutzt vorhandene Ordnung am besten aus und kommt bei fast sortierten Daten mit etwa \(n\) Vergleichen aus.
