Sortierverfahren Klaus Becker 2012 2 Sortierverfahren 3 Teil
Sortierverfahren Klaus Becker 2012
2 Sortierverfahren
3 Teil 1 Einen Datenbestand durchsuchen
4 Einen Datenbestand durchsuchen Krause, Stefanie, Brandenburgische Str. 20, 74343, Sachsenheim Aachen, Anke, Reeperbahn 11, 18201, Bad Doberan Brandt, Mandy, Scharnweberstrasse 84, 68199, Mannheim Almenhof Aachen, Dirk, Schmarjestrasse 80, 33619, Bielefeld Innenstadt Möller, Jens, Schoenebergerstrasse 47, 08313, Bernsbach Aachen, Anne, Gotzkowskystraße 77, 75179, Pforzheim Nordweststadt Herzog, Marco, Scharnweberstrasse 90, 61130, Nidderau Abend, Kerstin, Lietzensee-Ufer 71, 91217, Hersbruck Schmitz, Andreas, Meininger Strasse 84, 66539, Neunkirchen Ludwigsthal Abend, Kristin, Marseiller Strasse 44, 21775, Steinau Ebersbacher, Michelle, Alt-Moabit 10, 06691, Zeitz Abend, Niklas, Knesebeckstraße 24, 45770, Marl Koertig, Christine, Hardenbergstraße 82, 66887, Niederalben Abend, Stephanie, Albrechtstrasse 42, 55469, Oppertshausen Schmidt, Vanessa, Paderborner Strasse gesucht: 44, 86359, Gersthofen Katja Herrmann Meister, Stephan, Fasanenstrasse 17, 22605, Hamburg Othmarschen aus Queidersbac. . . h Abend, Annett, Ufnau Strasse 29, 87640, Biessenhofen gesucht: Katja Herrmann Abend, Alexander, Budapester Straße aus 12, 18273, Gleviner Burg Queidersbac. . . h
5 Algorithmische Suche - unsortierte Liste Aufgabe: (a) Entwickle einen Algorithmus, mit dem man in der gezeigten Datenliste nach einer Person mit einem vorgegebenen Nachnamen suchen kann. Wenn der übergebene Name in der Datenliste vorkommt, dann soll der Index des zugehörigen Datensatzes zurückgegeben werden. Kommt der Name mehrfach vor, dann soll der Index des ersten passenden Datensatzes zurückgegeben werden. Kommt der Name in der Datenliste nicht vor, dann soll der Index -1 zurückgegeben werden. Krause, Stefanie, Brandenburgische Str. 20, 74343, Sachsenheim Brandt, Mandy, Scharnweberstrasse 84, 68199, Mannheim Almenhof Möller, Jens, Schoenebergerstrasse 47, 08313, Bernsbach Herzog, Marco, Scharnweberstrasse 90, 61130, Nidderau Schmitz, Andreas, Meininger Strasse 84, 66539, Neunkirchen Ludwigsthal Ebersbacher, Michelle, Alt-Moabit 10, 06691, Zeitz (b) Implementiere den Algorithmus und teste ihn. Koertig, Christine, Hardenbergstraße 82, 66887, Niederalben (siehe auch I: 1. 19. 1. 1. 1) Schmidt, Vanessa, Paderborner Strasse 44, 86359, Gersthofen unsortierte Daten Meister, Stephan, Fasanenstrasse 17, 22605, Hamburg Othmarschen. . .
6 Algorithmische Suche - sortierte Liste Aufgabe: (a) Betrachte verschiedene Szenarien. Wann kann man jeweils die Suche abbrechen, wenn man die Daten des Datenbestandes der Reihe nach durchläuft? (b) Passe den Suchalgorithmus entsprechend an. Implementiere und teste ihn anschließend. (siehe auch I: 1. 19. 1. 1. 1) Aachen, Anke, Reeperbahn 11, 18201, Bad Doberan Aachen, Dirk, Schmarjestrasse 80, 33619, Bielefeld Innenstadt Aachen, Anne, Gotzkowskystraße 77, 75179, Pforzheim Nordweststadt Abend, Kerstin, Lietzensee-Ufer 71, 91217, Hersbruck Abend, Kristin, Marseiller Strasse 44, 21775, Steinau Abend, Niklas, Knesebeckstraße 24, 45770, Marl Abend, Stephanie, Albrechtstrasse 42, 55469, Oppertshausen Abend, Annett, Ufnau Strasse 29, 87640, Biessenhofen sortierte Daten Abend, Alexander, Budapester Straße 12, 18273, Gleviner Burg. . .
7 Algorithmische Suche - sortierte Liste Aufgabe: Die folgende (in Python implementierte) Funktion benutzt ein Suchverfahren, das den Namen "binäre Suche" hat. (a) Beschreibe den hier zu Grunde liegenden Algorithmus in eigenen Worten. Verdeutliche ihn auch anhand von Beispielen. (b) Teste die Funktion mit dem gegebenen Datenbestand. (siehe auch I: 1. 19. 1. 1. 1) binäre Suche def suchen(name, liste): index. Ergebnis = -1 gefunden = False links = 0 rechts = len(liste)-1 while not gefunden and links <= rechts: mitte = (links + rechts) // 2 if name == liste[mitte][0]: gefunden = True index. Ergebnis = mitte elif name < liste[mitte][0]: rechts = mitte-1 else: links = mitte+1 return index. Ergebnis
8 Algorithmische Suche - sortierte Liste Aufgabe: Das Suchverfahren "binäre Suche" kann man auch mit einem rekursiven Algorithmus beschreiben. Entwickle diesen Algorithmus (oder recherchiere ihn im Internet). Implementiere den Algorithmus anschließend und teste ihn.
Lineare Suche - unsortierte Liste 9 Suche: Ko Gr, Lo, Br, Ergebnis: 5 Mi, Sc, Ko, Fr, Be, Ke, Na, Eb, Kr, Suche: Ga Gr, Lo, Br, Wi, Sc, He, Ergebnis: -1 Mi, Sc, Ko, Fr, Be, Ke, Na, def suchen(name, liste): gefunden = False index. Ergebnis = -1 i=0 while i < len(liste) and not gefunden: if name == liste[i][0]: gefunden = True index. Ergebnis = i else: i = i+1 return index. Ergebnis Eb, Kr, Wi, Sc, He,
Lineare Suche - sortierte Liste 10 Suche: Ko Be, Br, Eb, Ergebnis: 7 Fr, Gr, He, Ko, Kr, Lo, Mi, Na, Suche: Ga Be, Br, Eb, Sc, Wi, Ergebnis: -1 Fr, Gr, He, Ko, Kr, Lo, Mi, Na, Suche: Ta Be, Sc, Wi, Ergebnis: -1 Fr, Gr, He, Ko, Kr, Lo, Mi, def suchen(name, liste): index. Ergebnis = -1 i=0 while i < len(liste) and name > liste[i][0]: i = i+1 if name == liste[i][0]: index. Ergebnis = i return index. Ergebnis Na, Sc, Wi,
Binäre Suche - sortierte Liste 11 Suche: Ga Be, Br, Eb, Ga, Fr, Gr, He, Ke, Ko, Kr, Lo, Mi, Na, Sc, Wi, Ga, Be, Br, Eb, Fr, Gr, He, Ko, Ga, Be, Br, Eb, Fr, Gr, He, Ke, Ko, def suchen(name, liste): index. Ergebnis = -1 gefunden = False links = 0 Kr, Mi, Na, Sc, Wi, rechts. Lo, = len(liste)-1 while not gefunden and links <= rechts: mitte = (links + rechts) // 2 if name == liste[mitte][0]: gefunden = True Kr, Lo, Mi, Na, Sc, Wi, index. Ergebnis = mitte elif name < liste[mitte][0]: rechts = mitte-1 else: links =Mi, mitte+1 Kr, Lo, Na, Sc, Wi, return index. Ergebnis
Binäre Suche - sortierte Liste 12 Suche: Ta Be, Br, Eb, Ta, Fr, Gr, He, Ko, Kr, Lo, Mi, Na, Sc, Wi, Ta, Be, Br, Eb, Fr, Gr, He, Ke, def suchen(name, liste): index. Ergebnis = -1 gefunden = False links = 0 Be, Eb, Fr, Gr, He, Ke, rechts. Br, = len(liste)-1 while not gefunden and links <= rechts: mitte = (links + rechts) // 2 if name == liste[mitte][0]: gefunden = True Be, Br, Eb, Fr, Gr, He, Ke, index. Ergebnis = mitte elif name < liste[mitte][0]: rechts = mitte-1 else: links =Eb, mitte+1 Be, Br, Fr, Gr, He, Ke, return index. Ergebnis Ko, Kr, Lo, Mi, Na, Ta, Ko, Kr, Lo, Mi, Na, Sc, Wi, Ko, Kr, Lo, Mi, Na, Sc, Wi,
13 Teil 2 Einen Datenbestand sortieren
14 Das Sortierproblem Krause, Stefanie, Brandenburgische Str. 20, 74343, Sachsenheim Aachen, Anke, Reeperbahn 11, 18201, Bad Doberan Brandt, Mandy, Scharnweberstrasse 84, 68199, Mannheim Almenhof Aachen, Dirk, Schmarjestrasse 80, 33619, Bielefeld Innenstadt Möller, Jens, Schoenebergerstrasse 47, 08313, Bernsbach Aachen, Anne, Gotzkowskystraße 77, 75179, Pforzheim Nordweststadt Herzog, Marco, Scharnweberstrasse 90, 61130, Nidderau Abend, Kerstin, Lietzensee-Ufer 71, 91217, Hersbruck Schmitz, Andreas, Meininger Strasse 84, 66539, Neunkirchen Ludwigsthal Abend, Kristin, Marseiller Strasse 44, 21775, Steinau . . . Das Sortierproblem besteht darin, Verfahren zu entwickeln, die eine sortierte Anordnung von Datensätzen automatisiert erzeugen. Die einzige Voraussetzung, die Daten erfüllen müssen, besteht darin, dass man sie nach einem Kriterium vergleichen kann. Es soll also möglich sein, bei zwei Daten D 1 und D 2 (der Ausgangsdatenliste) zu entscheiden, ob (nach dem vorgegebenen Vergleichskriterium) D 1 kleiner als D 2 oder D 2 kleiner als D 1 ist oder ob D 1 und D 2 bzgl. des Vergleichskriterium gleichwertig sind.
15 Das Sortierproblem - formal betrachtet Gegeben ist eine Liste von Datensätzen [D 0, D 1, . . . , Dn-2, Dn-1]. Die Datensätze sollen über eine Ordnungsrelation < verglichen werden können. Es soll also möglich sein, Vergleiche wie D 0 < D 1 eindeutig zu entscheiden. Mit der Ordnungsrelation ≤ erfassen wir die Möglichkeit, dass zwei Datensätze in dem zu vergleichenden Merkmal übereinstimmen können. Es soll D 1 ≤ D 0 gelten, wenn D 0 < D 1 nicht gilt. Gesucht ist eine neue Anordnung der Datensätze [Dp(0), Dp(1), . . . , Dp(n-2), Dp(n-1)] mit der Eigenschaft, dass Dp(0) ≤ Dp(1) ≤. . . ≤ Dp(n-2) ≤ Dp(n-1) gilt. Die neue Anordnung der Datensätze wird hier durch eine bestimmte Anordnung (man sagt auch Permutation) p(0), p(1), . . . , p(n-2), p(n-1) der Indexwerte 0, 1, . . . , n-2, n-1 beschrieben.
16 Entwicklung von Sortierverfahren Aufgabe ist es im Folgenden, ein (oder mehrere) Sortierverfahren zu entwickeln. Für die Entwicklung von Sortierverfahren spielt die Komplexität der Daten keine zentrale Rolle. Wir gehen daher im Folgenden von einfachen Daten (hier Zahlen) aus. 25 17 32 56 25 19 8 66 29 6 20 29 6 8 17 19 20 25 25 29 29 32 56 66
Vorgehen wie ein Computer 17 Ein Computer hat (in der Regel) keinen Überblick über alle Daten. Er kann zudem (in der Regel) nicht so intelligent wie ein Mensch agieren. Um die Arbeitsweise eines Computers zu simulieren, legen wir Regeln fest, die beim Sortieren beachtet werden müssen. Zunächst einmal werden alle Karten umgedreht. Der "Sortierer" hat keine Gesamtsicht auf alle Daten. 25 17 32 56 25 19 8 66 29 6 20 29
Spielregeln 18 Regel 1: Karten umdrehen: 25 17 32 56 25 19 8 66 29 6 20 29 Regel 2: 2 Karten vergleichen und in Abhängigkeit des Ergebnisses weiteragieren: ? < 25 17 32 56 25 19 8 66 29 6 20 29 Regel 3: Karten markieren (es dürfen auch mehrere unterscheidbare Marken benutzt werden): 25 17 32 56 25 19 8 66 29 6 20 29
Spielregeln 19 Regel 4: Eine Karte an eine andere Stelle verschieben: 25 17 32 56 19 8 66 29 6 20 29 25 Regel 5: Karten austauschen (d. h. zwei Karten geeignet verschieben): 25 17 32 56 19 8 66 29 Weitere (sinnvolle) Regeln können nach Bedarf eingeführt werden. 6
20 Teil 3 Sortieralgorithmen
21 Sortierverfahren Es gibt zahlreiche Verfahren, um Datensätze zu sortieren. Wir werden uns im Folgenden auf 4 Standardverfahren konzentrieren.
22 Selectionsort (Sortieren d. Auswählen) Wie sortieren Kartenspieler ihre Spielkarten? Auf You. Tube wird ein Verfahren gezeigt, das Selectionsort genannt wird. Schaue dir das Video genau an. Kannst du die Grundidee des benutzten Sortierverfahrens beschreiben?
23 Selectionsort (Sortieren d. Auswählen) Idee: Suche jeweils das kleinste Element im unsortierten Bereich und lege es am Ende des sortierten Bereichs ab. 25 6 6 8 17 32 56 25 19 8 66 29 6 20 29 25 17 32 56 25 19 8 66 29 20 29 25 17 32 56 25 19 66 29 20 29 25 32 56 25 19 66 29 20 29 17
Selectionsort (Sortieren d. Auswählen) 24 25 6 6 8 17 32 56 25 19 8 66 29 6 20 29 25 17 32 56 25 19 8 66 29 20 29 25 17 32 56 25 19 66 29 20 29 25 32 56 25 17 Realisierung mit 2 Listen 19 66 29 20 29 ALGORITHMUS selectionsort Übergabe: Liste L unsortierter Bereich ist die gesamte Liste L der sortierte Bereich ist zunächst leer SOLANGE der unsortierte Bereich noch Elemente hat: suche das kleinste Element im unsortierten Bereich entferne es im unsortierten Bereich füge es im sortierten Bereich an letzter Stelle an Rückgabe: sortierter Bereich
Selectionsort (Sortieren d. Auswählen) 25 25 6 6 8 17 32 56 25 19 8 66 29 6 20 29 25 17 32 56 25 19 8 66 29 20 29 25 17 32 56 25 19 66 29 20 29 25 32 56 25 17 Realisierung mit 2 Listen 19 66 29 20 29 ALGORITHMUS selectionsort Übergabe: Liste L unsortierter Bereich ist die gesamte Liste L der sortierte Bereich ist zunächst leer SOLANGE der unsortierte Bereich noch Elemente hat: suche das kleinste Element im unsortierten Bereich entferne es im unsortierten Bereich füge es im sortierten Bereich an letzter Stelle an Rückgabe: sortierter Bereich
26 Insertionsort (Sortieren d. Einfügen) Auf You. Tube gibt es eines interessantes Video zum Sortieren durch Einfügen. Schaue dir das Video genau an. Wie gehen die Tänzer beim Sortieren vor? Kannst du die Grundidee des benutzten Sortierverfahrens beschreiben?
27 Insertionsort (Sortieren d. Einfügen) Idee: Füge jeweils das erste Element des unsortierten Bereichts an der richtigen Stelle im sortierten Bereich ein. 25 17 32 56 25 19 8 66 29 6 20 29 19 8 66 29 6 20 29 17 25 32 56 17 25 25 32 56
Insertionsort (Sortieren d. Einfügen) 28 25 17 32 56 25 19 8 66 29 6 20 29 17 25 32 56 Realisierung mit 2 Listen ALGORITHMUS insertionsort 17 25 25 32 56 19 Übergabe: Liste 8 66 29 6 20 29 sortierter Bereich besteht aus dem ersten Element der Liste unsortierter Bereich ist die Restliste (ohne das erste Element) SOLANGE der unsortierte Bereich Elemente hat: entferne das erste Element aus dem unsortierten Bereich füge es an der richtigen Stelle im sortierten Bereich ein Rückgabe: sortierter Bereich
Insertionsort (Sortieren d. Einfügen) 29 25 17 32 56 25 19 8 66 29 6 20 29 17 25 32 56 25 19 8 66 29 6 20 29 ALGORITHMUS insertionsort Realisierung mit 1 Liste Übergabe: Liste 17 25 25 32 56 19 sortierter besteht 8 66 Bereich 29 6 20 aus 29 dem ersten Element der Liste unsortierter Bereich ist die Restliste (ohne das erste Element) SOLANGE der unsortierte Bereich Elemente hat: füge das erste Element des unsortierten Bereichs an der richtigen Stelle im sortierten Bereich ein verkleinere den unsortierten Bereich Rückgabe: sortierter Bereich
30 Bubblesort (Sortieren d. Aufsteigen) Auf You. Tube gibt es eines interessantes Video zum Sortieren. Schaue dir das Video genau an. Wie gehen die Lego-Arbeiter beim Sortieren vor? Kannst du die Grundidee des benutzten Sortierverfahrens beschreiben?
31 Bubblesort (Sortieren d. Aufsteigen) Idee: Durchlaufe (mehrfach) den gesamten Bereich. Vergleiche jeweils zwei benachbarte Element und vertausche sie, wenn die Reihenfolge nicht stimmt. 25 17 32 56 25 19 8 66 29 6 20 29 17 25 32 25 56 19 8 66 29 6 20 29 25 32 25 19 8 56 29 6 20 29 66 . . . 17
Bubblesort (Sortieren d. Aufsteigen) 32 ALGORITHMUS bubblesort Übergabe: Liste unsortierter Bereich ist zunächst die gesamte Liste 25 17 32 56 25 19 SOLANGE der unsortierte Bereich mehr als ein Element hat: 66 29 6 20 29 8 duchlaufe den unsortierten Bereich von links nach rechts wenn Elemente in der falschen 29 dabei 6 zwei 20 benachbarte 29 Reihenfolge vorliegen: 17 25 32 56 25 19 8 66 17 25 32 56 25 19 8 17 25 32 25 56 19 25 32 25 19 8 vertausche die beiden Elemente 29 6 20 29 verkürze den unsortierten Bereich durch Weglassen des letzten Elements 66 29 6 20 29 8 66 29 6 20 29 56 29 6 20 29 66 Rückgabe: überarbeitete Liste . . . 17 Realisierung mit 1 Liste
Quicksort (Sortieren d. Zerlegen) 33 Eine Person (z. B. die erste in der Reihe) wird als Vergleichsperson ausgewählt. Im vorliegenden Beispiel ist das Anton 1. 74 Britta Carlo Durs Georg Fiona Greta Fabian Jana Hannah Igor Eli 1. 64 1. 85 1. 89 1. 78 1. 67 1. 61 1. 92 1. 78 1. 58 1. 70 1. 74 Alle anderen ordnen sich links bzw. rechts von der Vergleichsperson ein, je nachdem, ob sie kleiner oder größergleich als die Vergleichsperson sind. Anton Britta Fiona Greta Hannah Igor 1. 64 1. 67 1. 61 1. 58 1. 70 1. 74 Carlo Durs Georg Fabian Jana Eli 1. 85 1. 89 1. 78 1. 92 1. 78 1. 74
Quicksort (Sortieren d. Zerlegen) 34 Anton bleibt jetzt auf seiner Position. Er nimmt nicht mehr an weiteren Spielrunden teil. Dasselbe Spiel wird jetzt im linken und im rechten Bereich durchgeführt: Eine Person (z. B. die erste in der Reihe) wird wiederum als Vergleichsperson ausgewählt. Britta 1. 64 Fiona Greta Hannah Igor 1. 67 1. 61 1. 58 1. 70 Anton Carlo 1. 74 1. 85 Durs Georg Fabian Jana Eli 1. 89 1. 78 1. 92 1. 78 1. 74 Wie geht das Spiel weiter? Wann ist das Spiel beendet? Welche Extremfälle können während des Spiels auftreten? Funktioniert das Sortierspiel dann auch noch?
35 Quicksort (Sortieren d. Zerlegen) Idee: Ein Element der Liste (z. B. das erste Element) wird ausgewählt. Die Restliste wird dann gemäß dieses Elements in zwei Teillisten aufgeteilt: die Liste der Elemente der Restliste, die kleiner als das ausgewählte Element sind sowie die Liste der Elemente der Restliste, die nicht kleiner (d. h. größer oder gleich) als das ausgewählte Element sind. Dieser Vorgang wird mit den Teillisten völlig analog fortgesetzt. 25 17 32 56 25 19 8 66 29 6 20 29 56 25 66 29 Zerlegen 17 19 8 6 20 25 32 Zerlegen . . . Zusammens. 6 8 17 19 20 25 25 29 29 32 56 66 Zusammensetzen 6 8 17 29 19 20 25 25 29 29 66
Quicksort (Sortieren d. Zerlegen) 36 ALGORITHMUS quicksort Übergabe: Liste L wenn die Liste L mehr als ein Element hat: # zerlegen wähle als Pivotelement p das erste Element der Liste aus erzeuge Teillisten K und G aus der Restliste (L ohne p) mit: - alle Elemente aus K sind kleiner als das Pivotelement p - alle Elemente aus G sind größergleich als das Pivotelement p # Quicksort auf die verkleinerten Listen anwenden KSortiert = quicksort(K) GSortiert = quicksort(G) # zusammensetzen LSortiert = KSortiert + [p] + GSortiert Rückgabe: LSortiert Realisierung mit mehreren Listen
Quicksort (Sortieren d. Zerlegen) - alt. 37 Eine Person (z. B. die erste in der Reihe) wird als Vergleichsperson ausgewählt. Suche von links eine Person, die größergleich als die ausgewählte Person ist. Suche von rechts eine Person, die kleinergleich als die ausgewählte Person ist. Anton Britta Carlo Durs Georg Fiona Greta Fabian Jana Hannah Igor Eli 1. 74 1. 64 1. 85 1. 89 1. 78 1. 67 1. 61 1. 92 1. 78 1. 58 1. 70 1. 75 Anton Eli 1. 74 1. 75 Wenn "links < rechts": Personen tauschen ihre Position in der Reihe. Igor 1. 70 usw. Britta Carlo Durs Georg Fiona Greta Fabian Jana Hannah 1. 64 1. 85 1. 89 1. 78 1. 67 1. 61 1. 92 1. 78 1. 58
38 Quicksort (Sortieren d. Zerlegen) - alt. ALGORITHMUS quicksort Übergabe: Liste L wenn die Liste L nicht leer ist: # zerlegen wähle als Pivotelement p (z. B. ) das erste Element der Liste aus tausche Elemente innerhalb L so, dass eine Zerlegung entsteht mit: -L=K+G alle Elemente aus K sind kleinergleich als das Pivotelement p alle Elemente aus G sind größergleich als das Pivotelement p K und G enthalten mindestens ein Element # Quicksort auf die verkleinerten Listen anwenden quicksort(K) quicksort(G) Rückgabe: L Realisierung mit 1 Liste
39 Quicksort (Sortieren d. Zerlegen) - alt. def quicksort(L, anfang, ende): if L != []: pivot = L[anfang] links = anfang rechts = ende while links <= rechts: while L[links] < pivot: links = links+1 while L[rechts] > pivot: rechts = rechts-1 if links <= rechts: if links < rechts: h = L[links] = L[rechts] = h links = links+1 rechts = rechts-1 if anfang < rechts: L = quicksort(L, anfang, rechts) if links < ende: L = quicksort(L, links, ende) return L >>> Quicksort: [21, 9, 20, 45, 5, 58, 40, 17] Quicksort: [17, 9, 20, 5] Quicksort: [5, 9] Quicksort: [20, 17] Quicksort: [45, 58, 40, 21] Quicksort: [21, 40] Quicksort: [58, 45] Implementierung mit 1 Liste
40 Quicksort (Sortieren d. Zerlegen) - alt. Quicksort Pivot: 29 Quicksort Pivot: 19 Quicksort Pivot: 8 Quicksort Pivot: 17 Quicksort Pivot: 20 Quicksort Pivot: 25 Quicksort Pivot: 41 Quicksort Pivot: 29 Quicksort Pivot: 32 Quicksort Pivot: 56 Quicksort Pivot: 60 [29, 17, 56, 6, 60, 41, 8, 25, 30, 20, 32, 19] [19, 17, 20, 6, 25, 8] [41, 60, 30, 56, 32, 29] [19, 17, 20, 6, 25, 8] [8, 17, 6] [20, 25, 19] [8, 17, 6] [17, 8] [8] [17] [20, 25, 19] [25, 20] [20] [25] [41, 60, 30, 56, 32, 29] [29, 32, 30] [56, 60, 41] [29, 32, 30] [30] [32] [56, 60, 41] [60, 56] [56] [60]
41 Teil 4 Laufzeitverhalten
42 Zielsetzung Algorithmen stellen oft nur einen Zwischenschritt beim Problemlösen dar. In der Praxis ist man meist an Programmen - also Implementierungen von Algorithmen in einer Programmiersprache - interessiert, die von Rechnern real ausgeführt werden können. Dabei spielt der Ressourcenverbrauch (Laufzeitverhalten und Speicherplatzbelegung) der Programme eine zentrale Rolle. Erwünscht sind solche Programme, die mit möglichst wenig Ressourcen auskommen. Wir werden uns im Folgenden auf die Ressource "Rechenzeit" konzentrieren, da Speicherplatz heute für viele Problemlösungen genügend zur Verfügung steht. Laufzeitmessungen liefern wichtige Informationen über die Einsetzbarkeit von Programmen. Programme, bei denen man sehr lange auf Ergebnisse warten muss, sind - je nach Anwendungsgebiet - oft nicht praxistauglich. Ziel der folgenden Untersuchungen ist es, das Laufzeitverhalten von Sortieralgorithmen zu ermitteln und die Ergebnisse genauer zu analysieren.
Laufzeitmessungen 43 from time import * print("Start") t 1 = clock() # Ausführung des Programms. . . t 2 = clock() t = t 2 - t 1 print("Stopp") print("Rechenzeit: ", t) from time import * from random import randint Muster Beispiel # Sortieralgorithmus def selectionsort(L): . . . # Initialisierung der Liste print("Aufbau der Testliste") anzahl = 100 L = [] for i in range(anzahl): L = L + [randint(1, 10*anzahl)] print(L) print("Start des Sortiervorgangs") # Sortierung der Liste t 1 = clock() L_sortiert = selectionsort(L) t 2 = clock() t = t 2 - t 1 print("Ende des Sortiervorgangs") # Ausgabe print(L_sortiert) print("Rechenzeit: ", t)
44 Systematische Laufzeitmessungen from time import * from random import randint # Sortieralgorithmus. . . # Initialisierung der Anzahl der Listenelemente anzahl = 1000 while anzahl < 10000: # Erzeugung der Liste L = [] for i in range(anzahl): L = L + [randint(1, 10*anzahl)] # Bestimmung der Rechenzeit t 1 = clock() L_sortiert = selectionsort(L) t 2 = clock() t = t 2 - t 1 # Ausgabe des Messergebnisses print("Anz. . . : ", anzahl, "Rechenzeit: ", t) # Erhoehung der Anzahl anzahl = anzahl + 1000 Beispiel >>> Anzahl der Listenelemente: 1000 Rechenzeit: 0. 208472864568 Anzahl der Listenelemente: 2000 Rechenzeit: 0. 82472800314 Anzahl der Listenelemente: 3000 Rechenzeit: 1. 83589394742 Anzahl der Listenelemente: 4000 Rechenzeit: 3. 3119754047 Anzahl der Listenelemente: 5000 Rechenzeit: 5. 27956234661 Anzahl der Listenelemente: 6000 Rechenzeit: 7. 45063213341 Anzahl der Listenelemente: 7000 Rechenzeit: 9. 99217191012 Anzahl der Listenelemente: 8000 Rechenzeit: 13. 1027999369 Anzahl der Listenelemente: 9000 Rechenzeit: 16. 5563961341 Wir bestimmen die Laufzeit t(n) systematisch in Abhängigkeit der Listenlänge n für wachsende Listenlängen (z. B. n = 1000, 2000, . . . ).
45 Laufzeitmessungen - Selectionsort Aufgabe: Führe selbst systematische Laufzeitmessungen durch (u. a. für Selectionsort und Quicksort). Versuche, anhand der Messwerte Gesetzmäßigkeiten aufzudecken (z. B. : Wie ändert sich t(n), wenn man n verdoppelt? ) Benutze die Gesetzmäßigkeiten, um Vorhersagen zu treffen (z. B. : Wie lange dauert es, um. . . Listenelemente zu sortieren? ). Überprüfe deine Vorhersagen.
46 Laufzeitmessungen - Selectionsort Anzahl der Listenelemente: 1000 Rechenzeit: 0. 204296356101 *2 Anzahl der Listenelemente: 2000 Rechenzeit: 0. 809496178984 Anzahl der Listenelemente: 3000 *2 Rechenzeit: 1. 83842583345 Anzahl der Listenelemente: 4000 Rechenzeit: 3. 23999810032 Anzahl der Listenelemente: 5000 Rechenzeit: 5. 16765678319 Anzahl der Listenelemente: 6000 Rechenzeit: 7. 2468548377 Anzahl der Listenelemente: 7000 Rechenzeit: 9. 91592506869 Anzahl der Listenelemente: 8000 Rechenzeit: 12. 9162480148 Anzahl der Listenelemente: 9000 Rechenzeit: 16. 3896752241. . . *2
47 Laufzeitmessungen - Quicksort Anzahl der Listenelemente: 1000 Rechenzeit: 0. 0178980848125 *2 # ALGORITHMUS quicksort(L) wenn die Liste L mehr als ein Element hat: Anzahl der Listenelemente: 2000 # zerlegen Rechenzeit: 0. 0625291761942 wähle als Pivotelement p das erste Element d. Liste aus Anzahl der Listenelemente: 3000 *2 Rechenzeit: 0. 133521718542 erzeuge Teillisten K und G aus "L ohne p" mit - alle Elemente aus K sind kleiner als das Pivotelement p - alle Elemente aus G sind nicht kleiner als p Anzahl der Listenelemente: 4000 # Quicksort auf die verkleinerten Listen anwenden Rechenzeit: 0. 176368784301 KSortiert = quicksort(K) Anzahl der Listenelemente: 5000 GSortiert = quicksort(G) Rechenzeit: 0. 351487409713 # zusammensetzen Anzahl der Listenelemente: 6000 Rechenzeit: 0. 330103965727 Anzahl der Listenelemente: 7000 Rechenzeit: 0. 58496680444 Anzahl der Listenelemente: 8000 Rechenzeit: 0. 854964248249 Anzahl der Listenelemente: 9000 Rechenzeit: 0. 942683218119. . . *2 LSortiert = KSortiert + [p] + GSortiert # Rückgabe: LSortiert
48 Laufzeitmessungen - Quicksort Anzahl der Listenelemente: 1000 Rechenzeit: 0. 00662849607981 *2 # ALGORITHMUS quicksort(L) wenn die Liste L mehr als ein Element hat: Anzahl der Listenelemente: 2000 # zerlegen Rechenzeit: 0. 0137794049244 wähle als Pivotelement p das erste Element d. Liste aus Anzahl der Listenelemente: 3000 *2 Rechenzeit: 0. 0227299838387 tausche Elemente innerhalb L so, dass eine Zerlegung L = K + [p] + G entsteht mit - alle Elemente aus K sind kleiner als Pivotelement p Anzahl der Listenelemente: 4000 - alle Elemente aus G sind nicht kleiner als p Rechenzeit: 0. 031230226188 # Quicksort auf die verkleinerten Listen anwenden Anzahl der Listenelemente: 5000 quicksort(K) Rechenzeit: 0. 0419377323096 quicksort(G) Anzahl der Listenelemente: 6000 Rechenzeit: 0. 0518194351517 Anzahl der Listenelemente: 7000 Rechenzeit: 0. 0589655947893 Anzahl der Listenelemente: 8000 Rechenzeit: 0. 069147894495 Anzahl der Listenelemente: 9000 Rechenzeit: 0. 0784993623491. . . *2 # Rückgabe: L
49 Schwierigkeiten bei Laufzeitmessungen Start Stopp Rechenzeit: Start Stopp Rechenzeit: Start Stopp Rechenzeit: 3. 27038296767 3. 23336066455 Selectionsort wiederholte Durchführung einer Laufzeitmessung 3. 25390210208 3. 2653359575 3. 24174441165 3. 25976206473 3. 2584529598 3. 26073537279 3. 23565201723 3. 23315561056 Selectionsort wiederholte Durchführung einer Laufzeitmessung Start Stopp Rechenzeit: Start Stopp Rechenzeit: Start Stopp Rechenzeit: 3. 2807468547 3. 41092736647 3. 4124093984 5. 37245627587 6. 69305316737 6. 70120168904 6. 67988976253 6. 67656727321 6. 70371150523 4. 73779544607
50 Problematik von Laufzeitmessungen werden in der Praxis durchgeführt, um das Laufzeitverhalten eines Programms unter Realbedingungen zu ermitteln. Aus systematisch durchgeführten Laufzeitmessungen kann man oft Gesetzmäßigkeiten erschließen, wie die Laufzeit von den zu verarbeitenden Daten abhängt. Bei Laufzeitmessungen muss man aber sehr genau darauf achten, dass sie unter gleichen Bedingungen erfolgen. Ein Wechsel des Rechners führt in der Regel zu anderen Ergebnissen. Auch Änderungen in der Implementierung wirken sich in der Regel auf die Messergebnisse aus. Selbst bei ein und demselben Rechner und derselben Implementierung können sich die Bedingungen ändern, da oft mehrere Prozesse gleichzeitig ablaufen. Ergebnisse von Laufzeitmessungen sind also kaum auf andere Systeme (andere Rechner, andere Programmiersprachen) übertragbar. Um diese Schwierigkeit zu überwinden, soll im Folgenden ein anderer Weg zur Beschreibung der Berechnungskomplexität beschritten werden.
51 Teil 5 Aufwandsanalyse
52 Zielsetzung Experimentell bestimmte Laufzeiten haben den Nachteil, dass sie vom benutzen Rechner und den Bedingungen auf dem Rechner (Prozesse, die dort laufen) abhängen. Solche Rechenzeiten sind also nur bedingt aussagekräftig. Erschwerend kommt manchmal hinzu, dass Rechenzeiten bei manchen Problemstellungen so groß werden, dass man sie bis heute noch nicht ermitteln konnte. Neben experimentellen Methoden benutzt man in der Informatik daher auch mathematische Methoden zur Einschätzung des Laufzeitverhaltens. Man versucht dabei, das Laufzeitverhalten abzuschätzen und abstrahierend zu beschreiben.
53 Kostenabschätzung def selectionsort(L): n = len(L) i = 0 while i < n-2: minpos = i m = i while m < n: if L[m] < L[minpos]: minpos = m m = m+1 h = L[i] = L[minpos] = h i = i+1 return L Bei der Ausführung des Algorithmus (bei vorgegebenen Problemgröße) spielt es eine Rolle, wie viele Operationen ausgeführt werden müssen. Operationen sind im vorliegenden Algorithmus u. a. das Ausführen eines Vergleichs und das Ausführen einer Zuweisung. Als Kostenmaß zur Beschreibung des zeitlichen Aufwands könnte man also die Gesamtanzahl der auszuführenden Vergleiche und Zuweisungen wählen. Bei der Festlegung eines Kostenmaßes müssen Annahmen über den Aufwand der verschiedenen auszuführenden Operationen gemacht werden. Zwei ganz unterschiedliche Wege kann man dabei bestreiten. Ein Weg besteht darin, unterschiedliche Aufwände von Operationen möglichst genau zu erfassen und im Kostenmaß zu berücksichtigen. Ein anderer Weg beschränkt sich darauf, dominante Operationen auszuwählen und die Kosten nur grob zuzuschätzen. Wir werden hier nur den zweiten Weg beschreiten.
54 Kostenabschätzung def selectionsort(L): n = len(L) i = 0 while i < n-2: minpos = i m = i while m < n: if L[m] < L[minpos]: minpos = m m = m+1 h = L[i] = L[minpos] = h i = i+1 return L Aufgabe: Wie viele Vergleiche von Listenelementen werden bei der Ausführung des Algorithmus / Programms durchgeführt? Ergänze die Funktionsdefinition um einen geeigneten Zähler.
Kostenanalyse - Selectionsort 55 | 25 32 56 25 19 8 66 29 6 ^ 20 29 | 17 32 56 25 19 8 ^ 66 29 25 20 29 | 32 56 25 19 17 ^ 66 29 25 20 29 25 19 ^ 32 66 29 25 20 29 56 32 66 29 25 20 ^ 29 32 66 29 25 ^ 25 29 66 29 56 25 ^ 29 29 ^ 56 32 29 ^ 29 | 56 32 ^ 66 6 17 6 8 17 | 56 6 8 17 19 | 25 6 8 17 19 20 | 56 6 8 17 19 20 25 | 32 6 8 17 19 20 25 25 | 66 6 8 17 19 20 25 25 29 . . . Selectionsort Vergleiche: 11 Problemgröße: n Datensätze Kostenmaß: Anzahl der Vergleiche Kostenfunktion: n -> (n-1)+(n-2)+. . . +1
Kostenanalyse - Insertionsort 56 25 | 17 32 56 25 19 8 66 29 6 20 29 17 25 | 32 56 25 19 8 66 29 6 20 29 17 25 32 | 56 25 19 8 66 29 6 20 29 17 25 32 56 | 25 19 8 66 29 6 20 29 17 25 25 32 56 | 19 8 66 29 6 20 29 17 19 25 25 32 | 8 66 29 6 20 29 . . . 56 Vergleiche: 1 Insertionsort Aufgabe: Bestimme jeweils die Anzahl der benötigten Vergleiche. Bei welcher Ausgangssituation sind die Kosten besonders ungünstig / günstig? Wie lautet in diesen beiden Fällen die Kostenfunktion? Problemgröße: n Datensätze Kostenmaß: Anzahl der Vergleiche Kostenfunktion: n -> ?
Kostenanalyse 57 best case (bester Fall): der Fall, in dem bei der Ausführung des Algorithmus die wenigsten Kosten anfallen worst case (schlechtester Fall): der Fall, in dem bei der Ausführung des Algorithmus die meisten Kosten anfallen Die Anzahl der Datensatzvergleiche bei der Ausführung eines Sortieralgorithmus hängt manchmal nicht nur von der Problemgröße n (d. h. der Anzahl der Listenelemente) ab, entscheidend ist auch, wie stark die Liste bereits vorsortiert ist. average case (durchschnittlicher Fall): eine Mittelung der Kosten über alle Fälle 12 | 17 32 35 35 40 51 66 70 82 88 91 Vergleiche: 1 12 17 | 32 35 35 40 51 66 70 82 88 91 Vergleiche: 1 12 17 32 | 35 35 40 51 66 70 82 88 91 Vergleiche: 1 Kostenfunktion: n -> n-1 Insertionsort . . . 91 | 88 82 70 66 51 40 35 35 32 17 12 Vergleiche: 1 88 91 | 82 70 66 51 40 35 35 32 17 12 Vergleiche: 2 82 88 91 | 70 66 51 40 35 35 32 17 12 Vergleiche: 3 . . . Insertionsort Kostenfunktion: n -> 1+ 2 +. . . + (n-1)
58 Kostenmodellierung als Problem # ALGORITHMUS quicksort(L) wenn die Liste L mehr als ein Element hat: # zerlegen wähle als Pivotelement p das erste Element d. Liste aus erzeuge Teillisten K und G aus "L ohne p" mit tausche Elemente innerhalb L so, dass eine Zerlegung L = K + [p] + G entsteht mit - alle Elemente aus K sind kleiner als das Pivotelement p - alle Elemente aus G sind nicht kleiner als p # Quicksort auf die verkleinerten Listen anwenden KSortiert = quicksort(K) GSortiert = quicksort(G) # zusammensetzen LSortiert = KSortiert + [p] + GSortiert - alle Elemente aus K sind kleiner als Pivotelement p - alle Elemente aus G sind nicht kleiner als p # Quicksort auf die verkleinerten Listen anwenden quicksort(K) quicksort(G) # Rückgabe: LSortiert Problemgröße: n Datensätze Kostenmaß: ? ? ? Kostenmaß: Anzahl der Vergleiche Genauere Analysen zeigen, dass bei längeren Listen die Verwaltung und die Verarbeitung dieser Listen sehr aufwendig ist und bei der Kostenmodellierung nicht vernachlässigt werden können.
Kostenanalyse - Quicksort 59 21 ^ 9 20 17 ^ 9 45 5 58 40 17 Vergleiche: 8 20 5 | 45 ^ 58 40 21 Vergleiche: 4(+2) + 4 5 ^ 9 | 20 ^ 17 | 21 ^ 40 | 58 ^ 46 Vergleiche: 2(+1) + 2 5 | 9 | 17 | 20 | 21 | 40 | 46 | 58 Quicksort - günstiger Fall Tbest(n) ≈ log 2(n)*n 58 ^ 5 9 17 20 21 40 45 ^ 5 9 17 20 21 40 | 58 40 ^ 5 9 17 20 21 | 45 21 ^ 5 9 17 20 | 40 21 Vergleiche: 45 Vergleiche : 10 Vergleiche: 9 58 Vergleiche: 8 45 Vergleiche: 7 . . . Quicksort - ungünstiger Fall Tworst(n) = n(+2) + (n-1)(+2) +. . . + 3(+2) + 2
60 Fachkonzept Kostenfunktion Die Problemgröße ist eine präzise Beschreibung des Umfangs der zu verarbeitenden Daten, von dem das Zeitbzw. Speicherverhalten von Lösungalgorithmen maßgeblich beeinflusst wird. Ein Kostenmaß legt fest, in welchem Maße welche Operationen bei der Aufwandsbestimmung berücksichtigt werden. Eine Kostenfunktion ordnet der Problemgröße n die vom Algorithmus benötigten Gesamtkosten T(n) bzgl. des vorgegebenen Kostenmaßes zu. Bei der Beschreibung der Zeitkomplexität mit Hilfe einer Kostenfunktion werden in der Regel eine Reihe von Vereinfachungen vorgenommen sowie Annahmen gemacht. Die Festlegung einer Kostenfunktion kann somit als eine Form der Modellierung angesehen werden, weil hier ein Berechnungsmodell entwickelt werden muss, das den Berechnungsaufwand vereinfachend beschreibt. Wie bei jeder Modellierung kann das entwickelte Modell mehr oder auch weniger geeignet sein, um die zu beschreibende Wirklichkeit darzustellen. Bei der Modellierung der Zeitkomplexität kommt es darauf an, "richtige" Annahmen über den Aufwand bestimmter, im Algorithmus vorkommender Operationen zu machen. Dass das manchmal schwierig ist, zeigen die Implementierungen des Quicksort. Algorithmus.
61 Kostenanalyse - Ergebnisse Selectionsort: Tbest(n) = (n-1) + (n-2) +. . . + 1 = n*(n-1)/2 = n 2/2 - n/2 Tworst(n) = (n-1) + (n-2) +. . . + 1 = n 2/2 - n/2 Taverage(n) = n 2/2 - n/2 Insertionsort: Tbest(n) = n-1 Tworst(n) = 1 + 2 +. . . + (n-1) = n 2/2 - n/2 Taverage(n) ≈ c*n 2 Quicksort: Tbest(n) ≈ log 2(n)*n Tworst(n) = n(+2) + (n-1)(+2) +. . . + 3(+2) + 2 = a*n 2 + b*n + c Taverage(n) ≈ c*n*log 2(n)
62 Kostenanalyse Aufgabe: Führe eine Kostenanalyse für den Sortieralgorithmus Bubblesort durch.
63 Teil 6 Asymptotisches Wachstumsverhalten
64 Vergleich von Kostenfunktionen Oft gibt es zu einem Problem mehrere Lösungsalgorithmen mit unterschiedlichen Kostenfunktionen. Diese will man natürlich vergleichen. Bevorzugt werden die Algorithmen, die geringsten Kosten verursachen. Nur, wie vergleicht man Kostenfunktionen? Selectionsort: Quicksort: Tbest(n) = (n-1) + (n-2) +. . . + 1 Tbest(n) ≈ log 2(n)*n Tworst(n) = (n-1) + (n-2) +. . . + 1 Tworst(n) = a*n 2 + b*n + c Taverage(n) = (n-1) + (n-2) +. . . + 1 Taverage(n) ≈ c*n*log 2(n)
65 Vergleich von Kostenfunktionen Algorithmen sind in der Regel so konzipiert, dass sie eine Lösung für beliebige Problemgrößen liefern. Beim Vergleich zugehöriger Kostenfunktionen tritt die Schwierigkeit auf, dass globale Aussagen oft nicht möglich sind. Es kann vorkommen, dass in einem Bereich die eine Kostenfunktion günstiger ist, in einem anderen Bereich die andere Kostenfunktion. T 1(n) = 0. 01*n 2 T 2(n) = 100*n*log 10(n)
66 Vergleich von Kostenfunktionen Oft ist der Problembereich, für den Algorithmen benötigt werden, nicht klar vorgegeben. Man benötigt dann ein Vergleichsverfahren für Kostenfunktionen, das auch mit Situationen wie in der Abbildung klarkommt. Eine Grundidee des in der Informatik gängigen Vergleichsverfahrens besteht darin, dass kleine Problemgrößen meist von geringerem Interesse sind als große Problemgrößen. Bei kleinen Problemgrößen unterscheiden sich Laufzeiten von Algorithmen z. B. nur im Millisekundenbereich, während die Unterschiede bei großen Problemgrößen im Sekunden-, Minuten-, Stunden-, Tage- oder gar Jahrebereich liegen können. Verbesserungen von Algorithmen zeigen sich in der Regel insbesondere bei großen Problemgrößen. Mathematisch präzisiert man diese Idee, indem man das Wachstumsverhalten von Kostenfunktionen vergleicht. Dabei idealisiert man, indem man das Grenzwertverhalten für gegen unendlich strebende Problemgrößen betrachtet. T 1(n) = 0. 01*n 2 T 2(n) = 100*n*log 10(n) T 2(n) / T 1(n) -> 0
67 Vergleich von Kostenfunktionen Eine (Kosten-) Funktion f wächst schneller als eine (Kosten-) Funktion g, wenn der Quotient f(n)/g(n) mit wachsendem n gegen unendlich strebt. Eine (Kosten-) Funktion f wächst langsamer als eine (Kosten-) Funktion g, wenn der Quotient f(n)/g(n) mit wachsendem n gegen 0 strebt. Eine (Kosten-) Funktion f wächst genauso schnell wie eine (Kosten-) Funktion g, wenn der Quotient f(n)/g(n) mit wachsendem n gegen eine Konstante c strebt. Selectionsort: Tselectionsort(n) = (n-1) + (n-2) +. . . + 1 = n*(n-1)/2 = n 2/2 - n/2 Quicksort - average: Tquicksort(n) ≈ c*n*log 2(n) Beispiel: Tselectionsort(n) wächst genauso schnell wie T(n) = n 2. Tselectionsort(n) / n 2 = 1/2 - 1/(2 n) -> 1/2 Beispiel: Tquicksort(n) wächst langsamer als Tselectionsort(n). Tquicksort(n) / Tselectionsort(n) = c*n*log 2(n) / (n*(n-1)/2) = 2*c*log 2(n) / (n-1) -> 0
Wachstumsprototypen 68 Prototyp Grundeigenschaft f(n) = log(n) logarithmisches Wachstum: Wenn n sich verdoppelt, dann wächst f(n) um einen konstanten Betrag. f(n) = n lineares Wachstum: Wenn n sich verdoppelt, dann verdoppelt sich f(n) ebenfalls. f(n) = n*log(n) logarithmisch-lineares Wachstum f(n) = n 2 quadratisches Wachstum: Wenn n sich verdoppelt, dann vervierfacht sich f(n) = n 3 kubisches Wachstum: Wenn n sich verdoppelt, dann verachtfacht sich f(n) = nk polynomiales Wachstum Wenn n sich verdoppelt, dann vervielfacht sich f(n) mit 2 k. f(n) = bn exponentielles Wachstum: Wenn n sich um 1 erhöht, dann vervielfacht sich f(n) mit b.
69 Wachstumsprototypen aus: P. Breuer: Praktische Grenzen der Berechenbarkeit. siehe: http: //informatik. bildungrp. de/fileadmin/user_upload/informatik. bildungrp. de/Weiterbildung/pdf/WB-X-6 -Praktische. Grenzen. pdf
70 Wachstumsklassen Eine (Kosten-) Funktion f wächst nicht schneller als eine (Kosten-) Funktion g, wenn f genauso schnell oder langsamer als g wächst. Die Klasse aller Funktionen, die nicht schneller wachsen als eine vorgegebene (Kosten-) Funktion f, wird mit O(f) bezeichnet. Man liest das so: "Groß O von f". Beispiel: Tselectionsort(n) gehört zur Klasse O(n 2). Tselectionsort(n) gehört zur Klasse der Funktionen, die nicht schneller als quadratisch wachsen. Beispiel: Tquicksort(n) gehört zur Klasse O(n*log 2(n)). Tquicksort(n) gehört zur Klasse der Funktionen, die nicht schneller als logarithmisch-linear wachsen.
71 Teil 7 Die Komplexität des Sortierproblems
72 Komplexität von Problemen Die (Zeit-)Komplexität eines Problems beschreibt man durch Komplexitätsklassen, die untere Schranken für die Komplexität der Algorithmen, die das Problem lösen, bilden. Der Algorithmus selectionsort hat - im günstigsten wie auch ungünstigsten Fall - eine quadratische Zeitkomplexität. Die zur Beschreibung des Laufzeitverhalten gewählte Kostenfunktion gehört zur Klasse O(n 2) der asymptotisch quadratisch wachsenden Funktionen. Der Algorithmus quicksort hat - im günstigsten (und auch durchschnittlichen) Fall - eine logischmisch-lineare Zeitkomplexität. Die zur Beschreibung des Laufzeitverhalten gewählte Kostenfunktion gehört hier zur Klasse O(n*log 2(n)). Es gibt eine Vielzahl an weiteren Sortieralgorithmen. Die "schnelleren" dieser Algorithmen haben alle eine logischmisch-lineare Zeitkomplexität. Es stellt sich die Frage, ob es nicht noch schnelleren Sortieralgorithmen gibt - z. B. solche mit einer linearen Zeitkomplexität - und, ob es auch eine Art untere Schranke für die Zeitkomplexität gibt, die von keinem Sortieralgorithmus unterschritten werden kann. Diese Fragen betreffen die Komplexität des Sortierproblems. Zur Beschreibung der Komplexität eines Problems muss man folglich Aussagen über alle möglichen Algorithmen zur Lösung des Problems machen, indem man zeigt, dass ein bestimmter Ressourcenverbrauch bei all diesen Algorithmen erforderlich ist und von keinem Algorithmus unterschritten werden kann. Die Schwierigkeit beim Nachweis solcher Aussagen besteht darin, dass man den Nachweis über alle denkbaren - d. h. bis jetzt gefundenen und auch noch nicht bekannten - Algorithmen führen muss.
73 Problem - Sortieren Gegeben ist eine Liste L = [a, b, c, d] mit 4 Elementen (z. B. Zahlen) und eine Vergleichsoperation, über die Listenelemente sortiert werden sollen. Bei einer Liste mit 4 Elementen gibt es 24 verschiedene Möglichkeiten, wie die Listenelemente nach der Sortierung angeordnet sein können: {abcd, abdc, acbd, acdb, adbc, adcb, bacd, badc, bcad, bcda, bdac, bdca, cabd, cadb, cbad, cbda, cdab, cdba, dabc, dacb, dbac, dbca, dcab, dcba} mögliche Ausführungen von Selectionsort als Baumdiagramm {abcd, abdc, acbd, acdb, adbc, adcb, bacd, badc, bcad, bcda, bdac, bdca , cabd, cadb, cbad, cbda, cdab, cdba, dabc, dacb, dbac, dbca, dcab, dcba } [a, b, c, d] a < b? T: {abcd, abdc, acbd, acdb, adbc, adcb, cabd, cadb, cdab, dabc, dacb, dcab } [a, b, c, d] a < c? T: {abcd, abdc, acbd, acdb, adbc, adcb, dabc, dacb } [a, b, c, d] a < d? T: {abcd, abdc, acbd, acdb, adbc, adcb } [a, b, c, d] b < c? . . . F: . . . | 25 ^ 32 56 27 25 | 32 56 27 ^ 25 27 | 56 32 ^ 25 27 32 | 56 Selectionsort
74 Problem - Sortieren {abcd, abdc, acbd, acdb, adbc, adcb, bacd, badc, bcad, bcda, bdac, bdca , cabd, cadb, cbad, cbda, cdab, cdba, dabc, dacb, dbac, dbca, dcab, dcba } [a, b, c, d] a < b? T: {abcd, abdc, acbd, acdb, adbc, adcb, cabd, cadb, cdab, dabc, dacb, dcab } [a, b, c, d] a < c? T: {abcd, abdc, acbd, acdb, adbc, adcb, dabc, dacb } [a, b, c, d] a < d? T: {abcd, abdc, acbd, acdb, adbc, adcb } [a, b, c, d] b < c? T: {abcd, abdc, adbc} [a, b, c, d] b < d? T: {abcd, abdc} [a, b, c, d] c < d? T: {abcd} [a, b, c, d] F: {abdc} [a, b, d, c] F: {adbc} [a, d, c, b] c < b? T: {} [a, d, c, b] F: {adbc} [a, d, b, c] F: {acbd, acdb, adcb} | 25 ^ 32 56 27 25 | 32 56 27 ^ 25 27 | 56 32 ^ 25 27 32 | 56 Selectionsort
Problem - Sortieren 75. . . F: {dabc, dacb} [d, b, c, a] b < c? T: {dabc} [d, b, c, a] b < a? T: {} c < a? T: {} F: {dabc} [d, a, c, c < b? T: {} F: {dabc} [d, a, F: {dacb} [d, b, c, a] c < a? T: {} b < a? T: {} F: {dacb} [d, a, c, c < b? T: {dacb} [d, a, F: {}. . . Selectionsort | 25 ^ 32 56 27 25 | 32 56 27 ^ 25 27 | 56 32 ^ 25 27 32 | 56 b] b, c] b] c, b] Wenn ein Sortieralgorithmus die Sortierung nur über Vergleiche von Listenelementen erzeugt, dann lässt sich die Ausführung des Algorithmus bei beliebigen Ausgangslisten über einen Entscheidungsbaum darstellen. Die "Tiefe des Baumes" (erkennbar an den Einrückungen) zeigt dabei, wie viele Entscheidungen im ungünstigsten Fall auszuführen sind. Im Fall des Algorithmus selectionsort beträgt die Tiefe des Baumes 6. Am Entscheidungsbaum zeigt sich also, wie gut oder schlecht ein Algorithmus ist. Wenn die Tiefe des Entscheidungsbaums groß bzw. klein ist, dann ist das worst-case-Verhalten des Algorithmus schlecht bzw. gut.
76 Problem - Sortieren {abcd, abdc, acbd, acdb, adbc, adcb, bacd, badc, bcad, bcda, bdac, bdca , cabd, cadb, cbad, cbda, cdab, cdba, dabc, dacb, dbac, dbca, dcab, dcba } a < b? T: {abcd, abdc, acbd, acdb, adbc, adcb, cabd, cadb, cdab, dabc, dacb, dcab } c < d? T: {abcd, acbd, acdb, cabd, cadb, cdab } a < c? T: {abcd, acbd, acdb} b < c? Am Entscheidungsbaum kann auch aufgezeigt T: {abcd} werden, wie gut das worst-case-Verhalten eines F: {acbd, acdb} Sortieralgorithmus überhaupt sein kann: Bei 24 b < d? T: {acbd} möglichen Anordnungen benötigt man F: {acdb} mindestens einen Entscheidungsbaum der Tiefe F: {cabd, cadb, cdab} 5, um alle Anordnungen durch Entscheidungen b < d? T: {cabd} erzeugen zu können. Das sieht man so ein: Durch F: {cadb, cdab} 1 Entscheidung erhält man eine Aufteilung der a < d? Anordnungen in 2 Teilmengen, durch 2, 3, 4, 5, . . . T: {cadb} F: {cdab} Entscheidungen in 4, 8, 16, 32, . . . Teilmengen. F: {abdc, adbc, adcb, dabc, dacb, dcab } Um alle 24 Anordnungen mittels Entscheidungen a < d? auseinander zu bekommen, sind daher T: {abdc, adbc, adcb} b < d? mindestens 5 Entscheidungen im T: {abdc} Entscheidungsbaum erforderlich. Das F: {adbc, adcb} Baumdiagramm zeigt, wie man mit 5 b < c? T: {abdc} geschachtelten Entscheidungen tatsächlich alle F: {adcb} Anordnungen erhält. F: . . .
77 Komplexität des Sortierproblems Die Betrachtungen oben verdeutlichen, dass es zu jedem vergleichsbasierten Sortieralgorithmus (für jede Listenlänge) einen zugehörigen Entscheidungsbaum gibt. Die Tiefe des Entscheidungsbaum (in Abhängigkeit der Listenlänge) legt das worst-case-Verhalten des Algorithmus fest. Um eine untere Schranke für das worst-case-Verhalten von Sortieralgorithmen zu gewinnen, schauen wir uns die Tiefe von "optimalen Entscheidungsbäumen" (in Abhängigkeit der Listenlänge) an. Um k verschiedene Anordnungen nur mit Entscheidungen zu erzeugen, benötigt man einem Entscheidungsbaum mit einer Tiefe der Größenordnung log 2(k). Wenn k = 2 m eine Zweierpotenz ist, dann reicht die Tiefe m. Ansonsten benötigt man als Tiefe den Exponenten von der von k aus nächst größeren Zweierpotenz. Wenn beispielsweise k = 24, dann benötigt man eine Tiefe log 2(32), also die Tiefe 5. Um eine Aussage über Listen beliebiger Länge treffen zu können, muss man wissen, wie viele Anordnungen jeweils möglich sind: Bei n Listenelementen gibt es n! = 1*2*. . . *n verschiedene mögliche Anordnungen der Listenelemente. Fasst man beide Ergebnisse zusammen, so erhält man folgende Aussage: Um eine Liste mit n Elementen zu sortieren, benötigt man einen Entscheidungsbaum, der eine Tiefe der Größenordnung log 2(n!) hat.
78 Komplexität des Sortierproblems Jeder vergleichsbasierte Sortieralgorithmus hat demnach ein worst-case-Verhalten, das durch die Funktion K(n) = log 2(n!) abgeschätzt werden kann. Mathematiker haben gezeigt, dass n! ≥ (n/2) gilt. Mit dieser Abschätzung erhält man log 2(n!) ≥ (n/2)*log 2(n/2). Hieraus ergibt sich, dass jeder vergleichsbasierte Sortieralgorithmus ein worst-case-Verhalten hat, das nach unten durch die Funktion K(n) = (n/2)*log 2(n/2) abgeschätzt werden kann. Betrachtet man - wie üblich - nur das asymptotische Verhalten, so zeigt dies, dass das worstcase-Verhalten eines beliebigen Sortieralgorithmus von der Ordnung n*log 2(n) sein muss. Ergebnis - Komplexität des Sortierproblems: Die Komplexität des Sortierproblems ist von der Ordnung n*log 2(n) - sofern ein vergleichsbasiertes Berechnungsmodell zu Grunde liegt.
- Slides: 78