Allgemeine Formulierung des Suchproblems Suchproblem: • In einem Behälter A befinden sich viele Elemente. Prüfe, ob ein Element e in A existiert, das eine bestimmte Eigenschaft P(e) erfüllt. • Behälter steht dabei ganz allgemein für unterschiedliche Datenstrukturen wie: • Array • Datei • Menge • Liste • Baum • Graph • Stack • Queue DigInf 05/06 1 Allgemeine Suche in Behältern • Entferne der Reihe nach Elemente aus dem Behälter, bis dieser leer ist oder ein Element mit der gesuchten Eigenschaft gefunden wurde. • Programmähnlich: • Prüfen, ob Behälter leer ist: istLeer • Ergreifen und Entfernen eines Elements aus dem Behälter: nimmEines DigInf 05/06 2 Allgemeine Suche in Behältern Lineare Suche in Java: DigInf 05/06 3 Allgemeine Suche in Behältern • Mit diesem Fragment lassen sich alle Container durchsuchen, die das folgende Interface realisieren: • Konkretisierung für Array A (auf das i-te Element wird mit A[i] zugegriffen, wahlfreier Zugriff möglich): • Hinweis: Anzahl der Zugriffe proportional mit der Elementanzahl! DigInf 05/06 4 Binäre Suche • Voraussetzung: • Ordnung auf dem Element-Datentyp, • sortierter Behälter • Beispiel Array A: • Idee: Weitersuchen in der richtigen Hälfte (a la Telefonbuch). • Verallgemeinerung: Wir suchen ein Element in einem beliebigen Teil des Arrays, der durch Min und Max eingegrenzt wird, anfangs Min = 0, Max = N-1. Dabei gilt stets die folgende Invariante: • Mit anderen Worten: Wenn das gesuchte Element überhaupt vorhanden ist, dann liegt es auch im noch zu untersuchenden Bereich. DigInf 05/06 5 Binäre Suche – Auf dem Weg zum Algorithmus Funktionsweise: Wenn Max<Min breche ab, x ist nicht vorhanden, ansonsten wähle irgendeinen Index m zwischen Min und Max: Falls x = A[m] gilt, sind wir fertig, m wird returniert. Falls x < A[m], suche weiter im Bereich Min bis m-1, setze also Max auf m-1 Falls x > A[m], suche weiter im Bereich m+1 bis Max, setze also Min auf m+1 Der Wert m wird dabei am Besten mittig zwischen Min und Max gewählt, also m = (Min + Max) / 2 Wenn Min ... Max aus N Elementen besteht, können wir den Bereich höchstens log2(N) mal halbieren. Um 1000 Elemente zu durchsuchen, genügen also log2(1000) = 10 Vergleiche. DigInf 05/06 6 Binäre Suche – Auf dem Weg zum Algorithmus Illustration: DigInf 05/06 7 Binäre Suche – Formulierung als rekursive Methode DigInf 05/06 8 Lineare Suche versus binäre Suche Binäre Suche ist deutlich effizienter, setzt allerdings auch eine geeignete Strukturierung der Daten voraus. Im Einzelnen: • Im Behälter sind die Elemente an Positionen gespeichert. • Man kann über die Position direkt auf das einzelne Element zugreifen. • Auf dem Elementtyp ist eine Ordnung definiert. • Die Elemente sind gemäß dieser Ordnung an den Positionen platziert. Der deutliche Effizienzunterschied kann bei häufigen Suchen rechtfertigen, dass man • alle Elemente in ein Array kopiert, • das Array sortiert, • ab dann im sortierten Array binär sucht. DigInf 05/06 9 Komplexität von Algorithmen am Beispiel des Suchens • Unter der Komplexität eines Algorithmus versteht man grob seinen Bedarf an Ressourcen in Abhängigkeit vom Umfang der Eingabedaten. Wichtigste Ressourcen sind dabei Laufzeit und Speicherplatz. • Laufzeit hängt im wesentlichen davon ab, wie oft die Schleifen durchlaufen werden. • Wir unterscheiden drei Fälle: • Best case: Ein Element x mit P(x) wird beim ersten Durchlauf gefunden. • Average case: Element wird nach der halben Maximalzahl von Schleifendurchläufen gefunden. • Worst case: Element wird beim letzten Durchlauf gefunden oder kommt gar nicht vor. DigInf 05/06 10 Komplexität von Algorithmen am Beispiel des Suchens Tabelle der Laufzeitverhalten und Abschätzungen DigInf 05/06 11 Komplexitätsklassen Sind f, g: Nat Nat Funktionen, so heißt f höchstens von der Ordnung g, falls eine Konstante C existiert, sodass für alle großen N gilt: f(N) C * g(N). Man schreibt auch f(N) = O(g(N)) und nennt diese Schreibweise ONotation. Diese Definition unterscheidet nicht zwischen Algorithmen, deren Aufwand nur um einen konstanten Faktor differiert. Insbesondere gilt O(log(N)) = O(log2(N)), denn log2(N) = log2(10) * log (N). Offensichtlich gilt: O(log(N)) < O(N) < O(N2) Wenn O(f(N)) < O(g(N)), so gilt O(f(N) + g(N)) = O(g(N)). Insbesondere: Die polynomialen Komplexitätsklasen sind nur von der höchsten Potenz bestimmt, d.h.: O(ck * Nk + ... + c1 * N + c0) = O(Nk) DigInf 05/06 12 Komplexitätsklassen Wertverläufe der Bearbeitungszeit (Betrachtung der Größenordnung), oberste Zeile entspricht Länge der Eingabe, Tabelleneinträge entsprechen Bearbeitungszeit DigInf 05/06 13 Komplexitätsklassen • Pentium PC, Annahme: Zeitbedarf für eine Komplexitätseinheit = eine Mikrosekunde • Frage: Welche Datenmengen lassen sich in vorgegebener Zeit verarbeiten? DigInf 05/06 14 Einfache Sortierverfahren Viele Sortierverfahren empfinden alltägliche Strategien nach. Beispiel: Aufnehmen von Spielkarten • Insertion Sort: einzeln aufnehmen und einsortieren • Bubble Sort: alle aufnehmen, benachbarte Karten vertauschen • Selection Sort: beim Aufnehmen offener Karten, immer nur die niedrigste aufnehmen DigInf 05/06 15 Objekte und Schlüssel • In der Praxis werden in der Regel keine Zahlen sortiert, sondern Objekte. • Sortierkriterium ist dabei meist eine ausgezeichnete Menge von Objektattributen. Wenn nach diesen Attributen gesucht werden soll, spricht man von Schlüsseln. • Beispiel: Objekte zu der Klasse DigInf 05/06 16 Objekte und Schlüssel Für das Sortieren ist es wichtig, dass die Schlüssel von Objekten verglichen werden können, zum Beispiel für den Schlüssel „Name, Vorname“ für Studenten: DigInf 05/06 17 Rahmenbedingungen für Sortieralgorithmen Für die Diskussion von Sortieralgorithmen nehmen wir an, dass einzelne Zeichen zu sortieren sind. Ausgangssituation: Sortierziel: Weitere Annahmen und Voraussetzungen: • Zeichen sind in einem Array gespeichert • Prozedur Swap DigInf 05/06 18 Rahmenbedingungen für Sortieralgorithmen • Die Verwendung von Swap erhöht die Lesbarkeit der folgenden Algorithmen. • Sie garantiert die folgende Invariante: • Das Array A ist jederzeit eine Permutation des ursprünglichen Arrays. DigInf 05/06 19 Sortieren mit BubbleSort • Idee: benachbarte Felder in falscher Reihenfolge werden vertauscht. • Mehrere Durchgänge, am Ende des ersten Durchgangs steht das größte Element ganz rechts. • Im zweiten Durchgang kommt das zweitgrößte Element an seiner Position an. DigInf 05/06 20 Sortieren mit BubbleSort DigInf 05/06 21 Sortieren mit BubbleSort DigInf 05/06 22 Sortieren mit BubbleSort Optimierung Das Beispiel zeigt, dass schon nach 10 (statt der schlimmstenfalls nötigen 14) Durchläufen das Sortierziel erreicht ist. Eine entsprechende Optimierung führt zu: DigInf 05/06 23 Sortieren mit BubbleSort – Laufzeit des optimierten Algorithmus DigInf 05/06 24 Sortieren mit BubbleSort – Laufzeit des optimierten Algorithmus DigInf 05/06 25 SelectionSort • Idee: In jedem Schritt wird das kleinste (größte) der noch ungeordneten Elemente gesucht und am rechten Ende der bereits sortierten Elemente eingefügt. • In einem Array mit dem Indexbereich [Lo...Hi] sei k die erste Position und i die Position des kleinsten Elements im noch unsortierten Bereich. • Damit ergibt sich folgende Situation vor einem Sortierschritt • Wenn wir nun A[k] und A[i] vertauschen, dann ist der sortierte Bereich um ein Element vergrößert: DigInf 05/06 26 SelectionSort • Wiederholung des Tauschens bis k = Hi. • Anwendung auf das Standardbeispiel (k=3) DigInf 05/06 27 SelectionSort • minPos ermittelt die Position des kleinsten Elements im unsortierten Rest. DigInf 05/06 28 SelectionSort - Laufzeitbetrachtung • • • • äußere Schleife wird N-1 mal durchlaufen. minPos(A,k,N) erfordert bis zu N-k Vergleiche Also: O(N2) Allerdings ist das C kleiner als bei BubbleSort (nur genau ein Swap pro Durchgang) • Nachteil: Sortieren dauert immer gleich lang, egal, wie gut das Array vorsortiert ist. DigInf 05/06 29