Aufgaben zur Algorithmentheorie 22. März 2004 1 Einleitung Diese Aufgabensammlung wird für die Informatikstudenten der Deutschsprachigen Ingenieurausbildung der TU Budapest angefertigt, insbesondere als ein Hilfsmittel zu den Übungen Algorithmentheorie. Die Aufgabensammlung ist noch nicht fertig, also können Fehler durchaus vorkommen. Damit diese eliminiert werden können, werden auch die Leser geboten, gefundene Fehler den Autoren zu melden. 2 Inhaltsverzeichnis Aufgaben 4 1 Einleitende Aufgaben 4 2 Sortieren 5 3 Suchbäume 5 4 Hashing 6 5 Datenkomprimierung 7 6 Graphentheoretische Algorithmen 8 7 Die Sprache NP 10 Lösungen 13 1 Einleitende Aufgaben 13 2 Sortieren 18 3 Suchbäume 23 4 Hashing 29 5 Datenkomprimierung 30 6 Graphentheoretische Algorithmen 32 7 Die Sprache NP 42 3 Aufgaben 1 Einleitende Aufgaben 1.1. Untersuchen Sie, ob zwischen den folgenden Funktionen die Relationen , , erfüllt sind! , a) und b) und c) und 1.2. Gegeben sind 9 Geldstücke, die gleich aussehen. 8 haben das gleiche Gewicht, das neunte ist aber verfälscht, und hat ein a) kleineres b) anderes Gewicht. Wir können mit einer Waage Vergleiche machen. Geben Sie einen Algorithmus, der in möglichst wenig Vergleichen das falsche Geldstück findet! 1.3. Geben Sie einen Algorithmus, der die beiden Teilarrays "!#%$'&)( und "!*&+ 10 10 %$,-( des Arrays .!/%$,-( in 32 Schritten, mit 42 Arbeitsspeicher vertauscht. 1.4. Geben Sie einen effizienten Algorithmus, der alle Teilmengen der Menge 5 76896;:<:;:=6>@? ausschreibt; jede Teilmenge muss genau einmal ausgeschrieben werden, und die Kardinalität von zwei nacheinanderfolgenden Teilmengen darf höchstens um 1 verschieden sein. 10 0 A2 und 42 ? 1.5. Was bedeutet 1.6. Das Problem „Türme von Hanoi”. Es gibt eine Platte, auf der drei Nadeln befestigt sind. Man hat vierundsechzig Scheiben auf eine der Nadeln gesteckt, wobei die größte Scheibe auf der Platte ruht, und die übrigen, immer kleiner werdend, eine auf der anderen. Das Ziel ist, die Scheiben auf eine andere Nadel umzusetzen, wobei man nur jeweils eine Scheibe auf einmal umsetzen darf, und zwar so, dass sich nie eine kleinere Scheibe unter einer größeren befindet. Geben Sie einen Algorithmus, der dieses Problem in möglichst wenig Schritten löst! Wieviele Schritte braucht man dafür mindestens? 4 2 Sortieren 2.1. Sortieren Sie die Liste 4, 11, 9, 10, 5, 6, 8, 1, 2, 16 mit merge sort, quick sort und heap sort! 2.2. Wieviele Vergleiche sind benötigt, um das kleinste von Elementen zu finden? Und das zweitkleinste? Und das & -kleinste? 2.3. Geben Sie einen effizienten Algorithmus, der das zehntkleinste Element eines Heaps findet! 0 2.4. Beweisen Sie, dass die Konstruktion eines Heaps mindestens 2 Vergleiche benötigt! 0 2.5. 6 6;:;:;:=6 2 sind verschiedene ganze Zahlen. Wir möchten diese Zah len der Grösse nach ordnen – entweder steigend oder fallend. Statt den üblichen Vergleichen kann man solche Fragen stellen: Welches ist das mittlere von drei Elementen? 0 Beweise: der effizienteste Algorithmus braucht 32 Schritte! 3 Suchbäume 3.1. Seien Elemente im binären Suchbaum und & Elemente im binären Such0 baum . (Die Elemente sind aus dem Universum 6 2 ). Gebe einen Algorithmus an, der alle & Elemente in Grössenordnung ausgibt! Was ist die Kost der Methode? 3.2. Zeige, dass in einem binären Baum die Anzahl der Blätter genau mit eins grösser ist, als die Anzahl solcher Punkten, die Grad 2 haben! 3.3. Sei ein vollständiger binärer Baum mit Knoten. Die Elemente in seien die ganze Zahlen aus dem Intervall !# $ =( . Daraus folgt, dass genau eine Zahl nicht vorkommt. Finde diese Zahl so schnell, wie möglich! 3.4. Ein binärer Baum hat die Eigenschaft, dass ausser den Blättern jeder Knotenpunkt Grad 2 hat. Weiterhin gilt, dass 0 0 3 & 22 0 0 22 Beweise, dass der Baum vollständig ist! 3.5. In einem 2-3 Baum sind die Elemente aus dem Intervall !/$ ( . In der Wurzel befinden sich zwei Schlüsseln, der erste ist 17. Was kann der zweite sein? 5 3.6. Ein AVL-Baum kann mit Hilfe des inorder Durchlaufens als eine geordnete Liste interpretiert werden. Wie könnte man die Konkatenation von Listen mit AVL-Bäumen realisieren? Präziser: seien und zwei AVL-Bäume so, dass je der Schlüssel in kleiner ist als in . Vereinige und in einen AVL-Baum! 3.7. Punkte sind in dem 2-dimensionalen Koordinatensystem mit ihrer Koordinaten gegeben so, dass alle Koordinaten verschiedene Zahlen sind ( verschiedene Zahlen). Beweise, dass genau ein binärer Baum existiert so, dass die Knoten des Bäumes diese Punkten sind, und die ersten Koordinaten die SuchbaumEigenschaft, die zweiten Koordinaten die Heap-Eigenschaft erfüllen! 3.8. Konstruiere einen AVL-Baum aus den folgenden Zahlen: 6 '6896;6 96 '6 ! 0 5 6 2 6 76896;: : :6 ?)6 6 3.9. Seien die Punkte auf der Ebene 0 gegeben. Gebe einen Algorithmus mit 7 32 Kost an, der entscheidet, ob zwei Punkte näher als 2 existieren! 3.10. In einen 2-3 Baum wurden 1000 neue Elemente eingefügt. Dabei musste man keinen Knotenpunktschnitt machen. Beweisen Sie, dass der Baum schon am Anfang mindestens 2000 Elemente beinhaltete. 3.11. Entwerfen Sie eine Datenstruktur für die folgende Aufgabe! Natürliche Zahlen sollen gespeichert werden. Eine Zahl kann auch mehrmals vorkommen. Operationen: INSERT( ): DELETE( ): DELETEALL( ): NUMBER( ): ELEMENT( & ): ein neues Exemplar von soll gespeichert werden ein Exemplar von soll gelöscht werden alle Exemplare von sollen gelöscht werden gibt die Anzahl der Exemplare von gibt das & -grösste Element Die Anzahl der verschiedenen Elemente sei . (Beispiel: sind die gespeicherten Elemente 1, 1, 3, 3, 3, 8, dann ist , NUMBER(1)=2 und ELEMENT(4)=3.) 10 Alle Operationen sollen einen Zeitbedarf von 7 2 haben. 3.12. Aus den ganzen Zahlen & 68& 6<:;:;:<6 & wird mit dem üblichen Algorithmus ein AVL-Baum gebildet. (Die nächste Zahl wird immer eingefügt und, falls nötig, eine Drehung wird durchgeführt.) Beweise: beim Einfügen von mindestens 0 2 Zahlen ist keine Drehung nötig! 4 Hashing 4.1. Mit offener Addressierung wurden die Rekorden in einer Matrix der Grösse 6 0 mit Hilfe der ' 7 & 2 & 7 Hash-Funktion gespeichert. Die Schlüssel kommen wie folgt: ' 6896 '76 ;6 96 '6; 6 '6 . Wie sieht die Matrix am Ende aus, mit 1. linearem Sondieren? 2. quadratischem Sondieren? 4.2. In der Matrix ! $ <( wurden Elemente auf den ersten Plätzen mit Hilfe einer unbekannten Hash-Funktion gespeichert so, dass die Plätze mit 0 Index 2 frei sind. Was ist die maximale Anzahl der Kollisionen mit 1. linearem sondieren? 2. quadratischem Sondieren? 0 4.3. Warum ist & 2$ der Hashtabelle ist 7.) &' 0 72 keine geeignete Hashfunktion? (Die Grösse 4.4. Die Elemente 6 6<:;:;:<6 6 werden in einer Hashtabelle der Grösse gespeichert; lineares Sondieren wird benutzt. Beim Einfügen der ersten & Ele mente ( & ) gab es insgesamt Kollisionen. Wieviele Kollisionen wird es beim Einfügen von im schlimmsten Fall geben? 5 Datenkomprimierung 5.1. Kodiere das Wort MISSISSIPPIT mit Huffman-Kodierung! Wieviel wurde im Vergleich zu dem Uniform-Code erspart? Kodiere das Wort auch mit LZWVerfahren! 5.2. Für die Häufigkeiten sind zwei optimale Kodierungen bekannt: '6;'6<7'6;7 und '6'76;96;7 . Wieviel ist 6 6;6 , falls ? 5.3. Sei :<:;: und . Man nehme an, dass zur Verteilung 6;:;:;:=6 6 ein optimaler Huffman-Code bekannt ist. Füge ein bzw. " ! ! $ nach dem Codewort von ! Beweise, dass die so erhaltene Codemenge # %6 Verteilung optimal ist! für die 6<:;:;:<%6 # : ( die Häufigkeiten der Buchstaben von einem 5.4. Seien & :;:<' (* & ) ) elementigen Alphabet in einem Text. Man nehme an, dass in einer optimalen Huffman-Codemenge alle Folgen der Länge & vorkommen. Was kann der höchste Wert von sein? 7 5 5.5. Man kodiere einen Text über das Alphabet 6;? sowohl mit HuffmanKodierung als auch mit LZW-Kodierung. Im LZW-Wörterbuch seien alle Codes -Bit lang. Gibt es einen Text, dessen LZW-Code kürzer ist? 5.6. Ein Text besteht aus Buchstaben. Was ist die minimale Anzahl der Code-Wörter, die zur LZW-Kodierung nötig sind? 5.7. Die 6;76896 96 6 6 968 Folge ist der Lempel-Ziv-Welch Code eines Texts, der ausschliesslich aus und Symbolen besteht. Das Wörterbuch enthält am Anfang die Einträge und . Dekodiere das Wort! 5.8. Seien & :;:<:'( die Häufigkeiten der Buchstaben von einem ( &*) ) elementigen Alphabet in einem Text. Man nehme an, dass in einer optimalen Huffman-Codemenge alle Folgen der Länge & vorkommen. Was kann der höchste Wert von sein? 6 Graphentheoretische Algorithmen 0 6.1. Der schlichte gerichtete Graph 6"2 ist mit seiner Adjazenzmatrix 0 gegeben. Ein Knotenpunkt heisst Superquelle, falls 0 0 '2 $ 6 2 aber 6 92 . Gebe einen effizienten Algorithmus, der die Superquelle findet (falls eine beinhaltet). 6.2. Zeige, dass BFS und DFS genauso implementiert werden können, mit dem einzigen Unterschied, dass BFS eine FIFO-Liste und DFS eine LIFO-Liste verwendet. 6.3. sei ein binärer Baum, dessen Kanten von der Wurzel hinweg gerichtet sind. Der Baum wird ausgehend aus der Wurzel mit der Tiefensuche traversiert. Falls ein Knotenpunkt zwei Söhne hat, wird immer zuerst der linke Sohn besucht. Beweise: die Sortierung der Knotenpunkte mit wachsender Tiefennummer entspricht der Preorder-Reihenfolge. Die Sortierung mit wachsender Abschlussnummer entspricht der Postorder-Reihenfolge. beliebig. Beweise, dass es einen DAG mit 6.4. Sei , Knotenpunkten und Kanten gibt. 6.5. Gebe einen effizienten Algorithmus, der aus den Adjazenzlisten die „umgekehrten” Adjazenzlisten konstruiert! (Die umgekehrte Adjazenzliste beinhaltet jene Kanten, die in den Knotenpunkt eingehen.) 6.6. Beweise: die Reihenfolge, in der der gelernte Algorithmus die starken Komponenten ausgibt, entspricht einer topologischen Ordnung des reduzierten Graphen. 8 6.7. Beweise: die Methode von Borůvka benötigt höchstens 7 Iterationen. 6.8. Ein Fluss in einem Netzwerk ist ein blockierender Fluss, falls jeder 0 0 Weg eine gesättigte Kante (d.h.: 2 2 ) beinhaltet. Beweise: jeder maximale Fluss ist ein blockierender Fluss, aber umgekehrt gilt das nicht. 0 6.9. Gegeben ist der gerichtete Graph 6 2 mit positiven Gewichten an 0 den Kanten sowie der Knotenpunkt . Gebe einen Algorithmus mit 2 Zeitbedarf, der die Anzahl der kürzesten Wege für alle liefert. 6.10. Gebe einen effizienten Algorithmus, der einen längsten Weg in einem DAG liefert! 6.11. Gebe einen effizienten Algorithmus, der die Knotenpunkte eines schlichten, ungerichteten Graphen in zwei Teile spaltet so, dass mindestens die Hälfte der Kanten zwischen den beiden Teilen laufen. -Schachbretts stehen Pferde des Gegners. 6.12. Auf einigen Feldern eines Unser Pferd steht auf einem anderen Feld. Die gegnerischen Pferde machen keinen Schritt, nur wenn sie uns schlagen können. Gebe einen Algorithmus an, der 0 in 2 Zeit jene Felder bestimmt, die durch eine Reihe von Pferdschritten mit unserem Pferd erreichbar sind, ohne durch den Gegner geschlagen zu werden! 0 6.13. 6 2 sei ein schlichter, zusammenhängender, ungerichteter Graph. 0 0 -0 0 2 2 . Der KnotenFür jeden Knotenpunkt sei 92 $ 6 2$ 0 punkt ist ein Zentrum, falls 92 minimal ist. Gebe einen Algorithmus, der in 0 2>2 Zeit ein Zentrum findet! 0 6.14. 6 2 sei ein schlichter, zusammenhängender, ungerichteter Graph. 5 ist ein Artikulationspunkt, falls ? nicht mehr zusammenhängend 10 ist. Gebe einen Algorithmus, der in 2 Zeit einen Punkt findet, der kein Artikulationspunkt ist! 5 0 6 2 ein gerichteter Graph mit 6 46 6 6 6 ? . Die Kanten 6.15. Sei 0 0 0 und ihre Gewichte sind die folgenden: 6 2 , 6 2 , 46 2 , 0 0 0 0 0 0 46 2 , 6 2 , 6 2 , 6 2 , 6 2 , 6 2 , 0 0 0 -6 2 , -6 2 , -6 2 . a) Bestimmen Sie die Länge des kürzesten Weges aus zu allen anderen Knotenpunkten mit der Methode von Dijkstra! Geben Sie die -Werte und die Menge in jeder Iteration an! b) Man möchte das Gewicht einer Kante mit 1 verkleinern, aber so, dass die Entfernung der Knotenpunkte von Punkt nicht verändert wird. Welche Kanten sind dazu geeignet? 9 6.16. Eine Bitfolge der Länge soll in Blöcke zerteilt werden, so dass jeder Block mindestens 4 lang ist, und in jedem Block sollen die ersten zwei Bits identisch mit den letzten zwei Bits sein. Entwerfen Sie einen Algorithmus, der mit 0 2 Zeitbedarf entscheidet, ob so eine Zerteilung möglich ist! 6.17. Gegeben ist ein gerichteter Graph mit einer konservativen Gewichtfunktion auf den Kanten (also kann das Gewicht mancher Kanten negativ sein, aber es gibt 10 2 keinen negativen gerichteten Kreis). Geben Sie einen Algorithmus, der in Schritten einen (im gewichteten Sinne) kürzesten gerichteten Kreis liefert! 6.18. Der gerichtete Graph ist mit Adjazenzlisten gegeben. Die Kanten von sind mit den Zahlen 76896<:;:;: 68& gewichtet. Der Wert eines Weges sei das Maximum der Gewichte seiner Kanten. Geben Sie einen Algorithmus, der den Wert des 0 Weges mit minimalem Wert zwischen zwei Knotenpunkten in 7 & 2 Schritten liefert! 7 Die Sprache NP 7.1. Nehmen wir an, dass es eine Methode gibt, die in einem Schritt entscheidet, ob ein Graph mit 3 Farben färbbar ist, oder nicht. Gebe einen Algorithmus an, der als Eingabe einen Graphen erwartet und eine korrekte Färbung von mit 3 Farben in Polynomzeit ausgibt, falls mit 3 Farben färbbar ist! 7.2. Beweise, dass die folgende Sprache L in ist! 5 0 0 6 und es gibt eine Lösung für $ 2 ' ":: : 2 Achtung: die Eingabe enthält die Koeffizienten in binärer Darstellung! 7.3. Beweise, dass die folgende Sprache L in $ 0 68& 2 ist! ist ein bipartiter Graph und hat genau & vollständige Paarungen -vollständig ist! $ ist ein ungerichteter0 Graph und besitzt einen Weg, der aus 2A Kanten besteht Zeige, dass die folgende Sprache L in ist! 0 0 0 ist ein ungerichteter Graph, 6 $ 2 6 2 und höchstens 2 Kanten führen zwischen und Welche der folgenden Funktionen $ gehört zur ? 9? 7.4. Zeige, dass die folgende Sprache L 7.5. $ 7.6. 10 )2 0 32$ 1. 0 32$ der kleinste echte Teiler von 2. 0 3. 32$ 7.7. Beweise, dass & -FARBE ( & 7.8. Zeige, dass BIN - PACKING ) -vollständig ist! , falls alle Gewichte grösser als sind! 7.9. Sei in einer X 3 C Aufgabe die Grösse der Grundmenge und die Anzahl der 10 3-elementigen Teilmengen . Zeige eine Boole-sche Formel der Länge 2 , die genau dann erfüllbar ist, falls X 3 C lösbar ist! 7.10. Man soll mit 3 Farben färben so, dass zu jedem Punkt eine verbotene Farbe auch vorgeschrieben ist. Zeige, dass es in Polynomzeit entscheidbar ist, ob eine solche Färbung existiert! (Hinweis: benutze, dass das Problem 2- KNF ist!) 7.11. Zeige, dass die folgende Sprache $ 0 68& 2 -vollständig ist! ist ein ungerichteter Graph, & : besitzt eine Teilmenge, die aus min. & Punkten besteht und mit 2 Farben färbbar ist Hinweis: reduziere das MAX - UNABHÄNGIG Problem darauf! 7.12. Beweise, dass PARTITION RSP $ 0 ;6 : :: 6 TMSP $ 0 ;6 : :: 6 PARTITION $ 0 ;6 : :: 6 TMSP RSP! 6<:: :6 & 2 wofür und es gibt 6468& 5 7.13. Beweise, dass MAXKREIS WEG 7.15. 11 7.14. Beweise, dass HAMILTON ? und 6 76;: :: 6 2 wofür und es gibt 5 76;: :: 6 ? 2 wofür -vollständig ist! -vollständig ist! 5 und es gibt : : 76<:: :6 ? & : a) Beweise, dass die folgende Sprache in ist: 0 0 6 2 $ %2 6 hat einen spannenden Baum, sind. dessen Punkte mit Grad 1 b) Beweise, dass die folgende Sprache -vollständig ist: 0 0 6 2 $ 286 hat einen spannenden Baum, dessen Punkte mit Grad 1 genau sind. 7.16. Beweise, dass die folgende Sprache 5 0 0 68& 2 $ %2$ &6 und 7.17. Beweise, dass ben. BIN - PACKING ist in -vollständig ist: ist eine abdeckende Punktmenge. ? , falls alle Gewichte die Form ha- 7.18. Was ist die Komplexität des MAX - KLIKK Problems, falls jeder Punkt höch 0 stens Grad 7 2 hat? 7.19. Welche Funktionen sind in ? 0 a) c) 2 Anzahl der Färbungen von mit 3 Farben 2 Liste der einfachen Wegen von 2 Liste der Dreicken von 0 b) 0 12 Lösungen 1 Einleitende Aufgaben a) Sei 1.1. . Dann gilt also 10 7$ 4 7 2 . Daraus folgt auch, dass 0 2 7 0 A2 . 0 0 also " 2 . Daraus folgt auch, dass " A 2 . Weiterhin, 0 10 0 2 , also A2 , 2 und 0 A2 . b) 0 also . Daraus folgt, dass 2 , und auch umgekehrt, also auch die Relationen und sind in beiden Richtungen 0 0 32 und 7 32 . erfüllt. Aber 7 0 0 c) Sei 32 und 32 . Dann gilt , und somit 0 10 0 2 , also auch 2 und 2 . Die anderen Relationen sind nicht erfüllt. 7 1.2. a) Zuerst wird das Gewicht von drei-drei Geldstücken verglichen. Falls das eine leichter ist, ist das falsche Geldstück unter diesen drei Geldstücken, sonst ist es unter den restlichen drei. Auf jeden Fall haben wir mit einem Vergleich die Anzahl der möglichen falschen Geldstücken auf drei reduziert. Von den drei möglichen falschen Geldstücken werden zwei verglichen. Falls das eine leichter ist, ist es das falsche, sonst ist das dritte falsch. Also sind zwei Vergleiche hinreichend. Man kann auch leicht einsehen, dass zwei Vergleiche auch notwendig sind. 13 b) Eine Lösung mit drei Vergleichen wird dargestellt. Zuerst wird die Anzahl der möglichen falschen Geldstücke mit Hilfe von zwei Vergleichen auf zwei reduziert, und im letzten Vergleich wird das falsche von diesen zwei gefunden. Nummerieren wir die Geldstücke von 1 bis 9. Zuerst wird das Gewicht der Geldstücke 1, 2, 3 und 4, 5, 6 verglichen. Falls sie gleich schwer sind, ist das falsche Geldstück unter den drei restlichen. Dann werden zwei davon verglichen, z.B. Geldstück 7 und 8. Falls sie gleich schwer sind, ist Geldstück 9 das falsche. Falls sie verschieden sind, dann wissen wir, dass das falsche Geldstück entweder 7 oder 8 ist, also haben wir unser erstes Ziel erreicht: nach zwei Vergleichen ist die Anzahl der möglichen falschen Geldstücke zwei. Falls im ersten Vergleich z.B. 1, 2, 3 schwerer ist als 4, 5, 6, dann werden danach die Geldstücke 1, 4, 7 und 2, 5, 8 verglichen. Hierbei wissen wir, dass 7 und 8 gut sind. Falls 1, 4, 7 und 2, 5, 8 gleich schwer sind, ist entweder 3 oder 6 falsch, denn wir wissen aus dem ersten Vergleich, dass das falsche unter 1, 2, 3, 4, 5, 6 ist, und aus dem zweiten, dass es unter 3, 6, 9 ist, und der Durchschnitt dieser Mengen ist 3, 6. Also haben wir die Anzahl der möglichen falschen Geldstücken wieder mit zwei Vergleichen auf zwei reduziert. Falls z.B. 2, 5, 8 schwerer sind, ist entweder 2 oder 4 falsch. Erklärung: falls das falsche Geldstück leichter ist, ist es in 1, 4, 7 aber vorher war 4, 5, 6 leichter, also ist davon 4 das falsche. Falls aber das falsche Stück schwerer ist, dann ist es in 2, 5, 8, und bei der ersten Messung war es unter 1, 2, 3, also ist 2 das falsche. Also sind nach zwei Vergleichen wieder nur zwei Möglichkeiten übrig. Genauso, wenn bei dem zweiten Vergleich 1, 4, 7 schwerer sind, ist das falsche Element entweder 1 oder 5. Aus zwei Elementen ist das falsche wie folgt zu bekommen: seien und die ’verdächtigten’ Geldstücke. Dann ist und ein anderes Geldstück zusammenzumessen, wobei wir wissen, dass nicht verfälscht ist. Wenn und gleich schwer sind, ist das falsche, sonst . 1.3. Ein möglicher Algorithmus ist der folgende. Im Anfangsschritt wird das erste Element in den Speicher bewegt und seine Position aufgezeichnet. Die neue Position dieses Elements wird mit ermittelt und in den Speicher geschrieben und die zwei Elemente werden ausgetauscht. Falls sich in der neuen Position kein Element befunden hat, terminiert der Algorithmus. Die neue Position eines Elements wird nach folgendem Prinzip festgelegt: falls das Element sich in der ersten Teilarray befindet, wird seine Position mit & erhöht. 14 In dem anderen Fall wird die Position mit & erniedrigt. Der Prozess wird in Abbildung 1 veranschaulicht. Die neue Position des Elements wird also durch den 0 0 0 Formel 2 &'2 32 gegeben. 1 k k+1 n n−k 1 n−k+1 n Abbildung 1: Veranschaulichung der Methode 0 10 42 ArbeitsBei bewegten Elementen braucht die Methode 32 Schritte und speicher, da in jedem Schritt sich genau ein Element und seine Position im Speicher befindet. Sei der Anzahl der Schritte, nachdem der Algorithmus terminiert hat, . Da die 0 & 32 . Falls Methode in seiner Anfangsposition terminiert, gilt: und & teilerfremd sind, gilt: , also werden genau Elemente ausgetauscht. Falls und & nicht teilerfremd sind, muss der Algorithmus erweitert werden. Aus 0 0 der obigen Kongruenz folgt unmittelbar & . Dann gilt: 2 2 . Sei &-6 0 0 2 . Da und 6 2 , gilt es: , also muss die Methode 0 -, oder @68& 2 -mal ausgeführt werden, um alle Elemente auszutauschen. Damit es kein Element mehr als einmal bewegt wird, müssen die Anfangselemente 0 adequat ausgewählt werden. Falls die Methode für jede Position :;:<: 6 & 2 aufgerufen wird, ergibt es trivialerweise ein korrekter Vertausch des Teilarrays. 1.4. Sei eine Teilmenge mit einer – -Folge der Länge beschrieben. Falls das Element in der Teilmenge vorkommt, steht in Position eine , sonst eine . Die Folgen werden laut dem Gray-Code nacheinandergeordnet, was garantiert, dass die Hamming-Distanz zwei nacheinanderkommenden Folgen 1 ist. So wird die Kardinalität der Teilmengen, die die nacheinanderkommenden Folgen entsprechen ebenso um verschieden sein. Die Gray-Codefolge der Länge wird wie folgt generiert. Die Codefolge der Länge besteht aus einer und einer : 15 0 Aus einer Codefolge der Länge ) A2 wird eine Codefolge der Länge generiert, indem unter der ursprünglichen Codefolge dieselbe in umgekehrter Reihenfolge aufgeschrieben wird. In einer zusätzlichen Spalte wird in allen Zeilen der ursprünglichen Code die Zahl und in der umgekehrten die Zahl aufgeschrieben. Der Zeitbedarf für das Generieren der -ten Folge ist das Ausschreiben von zwei- mal ! Folgen der Länge und ebenso zweimal das Ausschreiben von ,! bits. Insgesamt werden also 3 bits ausgeschrieben. Ein Code kann in Schritten 10 ,72 in eine Teilmengen konvertiert werden. Das ergibt einen Zeitbedarf von Schritten. Die Optimalität dieser Schranke kann mit der Lösung folgender Rekur0 sion eingesehen werden. Bezeichne 2 den Zeitbedarf der Algorithmus für eine Codefolge mit der Länge . Es gilt: 0 0 A 2 3 2 9 0 4 A2 1.5. 10 0 0 10 1 2 A2 heisst nach der Definition, dass 6> ) ) 9 # 2 0 A2 :;:;: : 32 0 Das heisst, die Funktion 32 ist beschränkt. 0 Wenn 32 die Zahl der Schritte eines Algorithmus bezeichnet, dann ist dieses Problem mit konstanter Zahl von Schritten berechenbar (im schlechtesten Fall). 0 A2 heisst nach der Definition, dass 0 2 ': 0 Das bedeutet, dass die Funktion 2 gegen strebt. 1.6. Wir definieren den Algorithmus mit vollständiger Induktion bezüglich der Anzahl der Scheiben. Für eine Scheibe ist die Lösung trivial. Nun wird ein Verfahren gezeigt, das Scheiben auf eine andere Nadel umsetzt angenommen, daß 0 man 1 Scheiben umsetzen kann (sagen wir, in 1 A2 Schritten). Nennen wir die Nadeln , und . Alle Scheiben befinden sich auf Nadel das Ziel ist, alle auf zu packen. (Siehe Abbildung 2) 1. Setzen wir die obere Scheiben von 0 tionsannahme. ( A2 Schritte) 16 auf , . Das geht laut der Induk- 2. Setzen wir die zurückgebliebene -te Scheibe auf 3. Nun setzen wir die 1 Scheiben von auf . (Ein Schritt) 0 um. ( 1 A2 Schritte) Den Zeitbedarf des Algorithmus bekommt man also mit der folgenden Rekursion: 0 0 0 0 32 A2 . Aus A2 ist 2 für alle weitere Werte von eindeutig bestimmt. 0 Wir behaupten: 3 2 . Beweis mit vollständiger Induktion: Anfangsschritt: Für ist die Formel korrekt. 0 0 0 A 2 2= A2 Induktionsschritt: . %' Also ist die Formel richtig. Nun soll gezeigt werden, dass man mindestens so 0 viele Schritte braucht. Sei die minimale Anzahl der nötigen Schritten 32 für 0 Scheiben. A2 . Versuchen wir die Scheiben irgendwie auf eine andere Nadel umzusetzten. Betrachten wir den Augenblick, in dem wir die grösste Scheibe bewegen. Gebe es mehrere solche Fälle, sollen wir den ersten betrachten. Die grösste Scheibe kann nur auf eine leere Nadel umgesetzt werden, und nur dann, wenn sich keine weitere Scheiben über sie befinden (das folgt aus den Regeln), also müssen wir bereits alle andere Scheiben auf die dritte Nadel umgesetzt haben. Dazu waren minde0 stens A2 Schritte erförderlich. Die Bewegung der grössten Scheibe nimmt einen zusätzlichen Schritt in Anspruch. Schließlich muss man alle andere Scheiben von der dritten Nadel auf die Nadel umsetzten, auf die wir die grösste 0 Scheibe bereits umgesetzt haben. Dazu sind mindestens A2 Schritte nötig. 0 " A2' Schritte, um Ein beliebiger Algorithmus benötigt also mindestens 0 0 0 A2 . Für 32 das Problem für Scheiben zu lösen. Also ist 2 haben wir die gleiche Rekursion erhalten, also ist die untere Schranke , . Mit unserer Lösung wird das Problem in genau so vielen Schritten gelöst. Nach der Sage darf der oberste Priester des Tempels nur jeweils eine Scheibe pro Tag umsetzen. Das Ende der Zeit sei erreicht, wenn alle 64 Scheiben auf einer dieser Nadeln wieder nach den Regeln aufgebaut werden. Das heißt, das unsere Welt in 9 9 Tagen mit einem Donnerschlag samt Turm und Tempel untergeht... 17 A B C A B C A B C Abbildung 2: Türme von Hanoi, Veranschaulichung der Rekursion 2 Sortieren 2.1. 1. merge sort: Die wichtigsten Schritte der merge sort werden in einem Pseudodebugger dargestellt. Als erstes wird der Routine rekursiv für die immer kleinere (Teil)Arrays aufgerufen: 1: MSORT(4, 11, 9, 10, 5, 6, 8, 1, 2, 16) 2: MERGE( MSORT(4, 11, 9, 10, 5), MSORT(6, 8, 1, 2, 16) ) 3: MERGE( MERGE( MSORT(4, 11, 9), MSORT(10, 5) ), MERGE( MSORT(6, 8, 1), MSORT(2, 16) ) ) 4: MERGE( MERGE( MERGE ( MSORT(4, 11), MSORT(9) ), MERGE ( MSORT(10), MSORT(5) ) ), MERGE( MERGE ( MSORT(6, 8), MSORT(1) ), MERGE ( MSORT(2), MSORT(16) ) ) ) 5: MERGE( MERGE( MERGE ( MERGE( MSORT(4), MSORT(11) ), MSORT(9) ) MERGE ( MSORT(10), MSORT(5) ) ), MERGE( MERGE ( MERGE( MSORT(6), MSORT(8) ), MSORT(1) ), MERGE ( MSORT(2), MSORT(16) ) ) ) Dann werden die Arrays “von unten nach oben” zusammengekämmt. 6: MERGE( MERGE( MERGE ( MERGE( 4, 11 ), 9 ), MERGE ( 10, 5 ) ), MERGE( MERGE ( MERGE( 6, 8 ), 1 ), 18 MERGE ( 2, 16 ) ) ) 7: MERGE( MERGE( MERGE ( (4, 11), 9 ), (5, 10) ), MERGE( MERGE ( (6, 8), 1 ), (2, 16) ) ) 8: MERGE( MERGE( (4, (5, ), MERGE( (1, (2, ) ) 9, 11), 10) 6, 8), 16) 9: MERGE( (4, 5, 9, 10, 11), (1, 2, 6, 8, 16) ) 10: (1, 2, 4, 5, 6, 8, 9, 10, 11, 16) Dabei wurde benutzt, dass die Funktion ihr Argument als Ergebnis liefert, wenn es mit einer Zahl aufgerufen wird. 2. quick sort: Die wichtigsten Schritte der quick sort werden in einem Pseudodebugger dargestellt. Errichten der ersten Partition mit : 1: 1: 2: 3: 4: 5: QUICKSORT( PARTITION( PARTITION( PARTITION( PARTITION( QUICKSORT( (4, (4, (4, (4, (4, (4, 11, 9, 10, 5, 6, 8, 1, 2, 16) ) 11, 9, 10, 5, 6, 8, 1, 2, 16), (5)) 2, 9, 10, 5, 6, 8, 1, 11, 16), (5)) 2, 1, 10, 5, 6, 8, 9, 11, 16), (5)) 2, 1, 5, 10, 6, 8, 9, 11, 16), (5)) 2, 1) ), 5, QUICKSORT ( (10, 6, 8, 9, 11, 16) ) Nach dem Errichten der ersten Partition läuft die Sortierung folgendermassen weiter: 19 6: QUICKSORT( PARTITION( (4, 2, 1), (2) ), 5, PARTITION( (10, 6, 8, 9, 11, 16), (9) ) ) Errichten der Partition in .!/%$, ( mit : 7: PARTITION( (4, 2, 1), (2) ) 8: PARTITION( (1, 2, 4), (2) ) 9: QUICKSORT( 1 ), 2, QUICKSORT(4) Errichten der Partition in .! $ ( mit : 10: 11: 12: 13: 11, 16), (9) ) 11, 16), (9) ) 11, 16), (9) ) QUICKSORT( (10, 11, 16) ) PARTITION( PARTITION( PARTITION( QUICKSORT( (10, 6, 8, 9, (9, 6, 8, 10, (6, 8, 9, 10, (6, 8) ) , 9, ihr Argument als Mit Berücksichtigung, dass die Funktion Ergebnis liefert, falls sie mit einer Zahl aufgerufen wird und nach der Sor0 0 tieren der Arrays '6 72 und 96;776; ,2 ist das Ergebnis: (1, 2, 4, 5, 6, 8, 9, 10, 11, 16) 3. heap sort: Es wird ein binäres Heap konstruiert und mit heap sort sortiert. Die Schritte werden in einem Pseudodebugger dargestellt. 1: 2: 3: 4: 5: 6: 7: (4, (4, (4, (4, (4, (1, (1, 11, 9, 10, 5, 6, 8, 1, 2, 11, 9, 1, 5, 6, 8, 10, 2, 11, 6, 1, 5, 9, 8, 10, 2, 1, 6, 11, 5, 9, 8, 10, 2, 1, 6, 2, 5, 9, 8, 10, 11, 4, 6, 2, 5, 9, 8, 10, 11, 2, 6, 4, 5, 9, 8, 10, 11, 16) 16) 16) 16) 16) 16) 16) //Sickern(A[4]) //Sickern(A[3]) //Sickern(A[2]) //Sickern(A[1]) Wie bekannt, wird ein Heap aus einer ungeordneter Array von Elementen konstruiert, indem die Funktion Sickern auf alle Elemente von "! -( bis .!/<( aufgerufen wird. Hier wurden nur die Aufrufe dargestellt, die tatsächlich Elemente bewegt haben. Nachdem die Konstruktion fertig ist, entspricht der Array ein binäres Heap. 20 Falls diese Repräsentation adequat ist, ist die Sortieraufgabe erledigt, sonst kann die sortierte Liste mit zehnmaliger Anwendung der Heap-operation removeMin bekommen werden. Die sortierte Liste: 1, 2, 4, 5, 6, 8, 9, 10, 11, 16 2.2. Für das Finden des kleinsten Elementes unter Elemente sind Vergleiche nötig. Zuerst werden zwei beliebige Elemente verglichen und das grössere wird verworfen. Danach wird das kleinere mit einer beliebigen Element aus der Menge der restlichen Elemente verglichen. Dieses Verfahren wird wiederholt bis das kleinste Element gefunden ist. Weniger als Vergleiche reichen nicht aus,was folgendermassen eingesehen werden kann: Sei das Problem mit einer Graph dargestellt. Die Knotenpunkte repräsentieren die einzelnen Elemente und eine Kante zwischen zwei Knotenpunkte läuft, wenn wir die Elemente verglichen haben, also wenn wir wissen, welches der Elementen das kleinere ist. Falls dieser Graph nicht zusammenhängend ist, gibt es mindestens zwei Mengen von Elemente, und , wo über keinen 0 6 %6 2 Paar bewusst ist, ob oder das kleinere ist. Deshalb ist es nicht zu entscheiden, ob das kleinste Element in der Menge oder in ist. Ein Graph mit Punkte braucht mindestens Kanten, damit er zusammenhängend wird. Also reichen weniger Vergleiche nicht aus. Für das zweitkleinste unter Elementen zu finden werden Vergleiche in mehrere Iterationen durchgeführt. In der ersten werden aus den Elementen Paare gebildet und verglichen. In der nächsten Iteration werden die Paare aus den kleineren Elementen gebildet. Somit sind :;:;:) , insgesamt also Vergleiche nötig, um das kleinste Element zu finden. Diese Vergleichsiterationen können als Etagen eines Baumes geschaut werden, in dem die Knotenpunkte die verglichene Elemente repräsentieren und Kanten zwischen den verglichenen Elemente der jeweiligen Iteration und dem kleineren Element (der auch in der nächsten Iteration teilnimmt) laufen (s. Abb. 3). In diesem Baum ist das gesuchte zweitkleinste Element das kleinste unter denen, die mit dem Minimum verglichen sind, . Da der Baum insgesamt 7 Etagen hat, wird das kleinste unter 7 Elemen te gesucht, was 7 Vergleiche erfordert. Das ergibt einen Zeitbedarf von 9 Vergleiche. Das Auswählen des & -kleinsten Elementes geschieht nach dem Prinzip der Bürgerwahl, das in dem Vorlesungsskript [Vorlesungsskript] ausführlich beschrieben ist. 2.3. Wegen der Heap-Eigenschaft ist es garantiert, dass sich das zehntkleinste Element nicht tiefer als der -te Etage in dem Heap befindet. Der Zeitbedarf einer Methode, die die ersten Etagen des Heaps kopiert und in diesem kopierten Heap das zehntkleinste Element findet, ist unabhängig von der Grösse des ori ginalen Heaps, da der Heap-Operation mit der Tiefe des kopierten 21 Abbildung 3: für Aufgabe 2.2. 10 0 Heaps proportional ist. Somit wird das gesuchte Element in ,2 A2 Schritte gefunden, indem wir das Heap-Operation zehnmal auf das kopierte Heap anwenden. 0 2.4. Sei der Zeitbedarf für das Aufbauen des Heaps 2 . Da das Finden des Minimums unter Elmenten laut 2.2. Vergleiche benötigt und das kleinste 0 0 Element eines Heaps in einem Schritt gefunden werden kann, gilt 32 A2 0 0 0 3 2 2 2. 10 2.5. Es wird bewiesen, dass der Zeitbedarf der effizientesten Algorithmen 7 3 2 0 und 7 32 ist. Sei der Vergleich auf drei beliebige Elemente angewendet; das mittlere Element ist weder das Maximum noch das Minimum. Dieses Element wird weggeworfen und die zwei anderen werden mit einem neuen verglichen. Diese Prozedur findet die beiden Extrema in Schritte, allerdings ohne den Kenntnis, welches von denen das Maximum oder das Minimum sei. Eines der beiden wird willkürlich ausgewählt und in den weiteren Vergleichen immer als drittes Element festgelegt. Da dieses ein Extremum ist, wird das Ergebnis einer Vergleich zwei beliebiger anderen Elementen und dieses Elements dasselbe Ergebnis liefern, als ein “konventioneller” Vergleich der zwei Elemente. Abhängig davon, ob das Maximum oder das Minimum als drittes Element festgelegt wurde, wird das mittlere Element das grössere/kleinere der beiden sein. 0 Im weiteren werden die bekannten Sortierungsmethoden – mit Zeitschranke 7 3 2 – angewendet, um eine wachsende/fallende Sortierung zu generieren. Somit hat 10 0 die Methode eine obere Zeitschranke 7 2 7 2 . Für den Beweis der unteren Zeitschranke sei angenommen, dass es einen Algo10 rithmus schneller als 7 32 gibt, der auf solche Vergleiche basiert. In diesem Fall könnten aber die Sortierungen, die Vergleiche zweier Elementen verwenden, auch schneller sein, weil die Frage “Welches ist das mittlere von drei Elementen?” 10 mit A2 solchen Vergleichen beantwortet werden kann. 22 Da aber das schnellste Sortierverfahren, das auf Vergleiche basiert, eine Zeit0 schranke 7 2 hat, muss auch der Zeitbedarf aller Sortierungen, die auf sol0 che Vergleiche dreier Elemente basieren, auch 7 32 sein. 3 Suchbäume 3.1. Es kann leicht gezeigt werden, dass das inorder Durchfahren die Rekorde eines binären Suchbäumes eben in Grössenordnung zurückgibt. Die Kost dieser 0 Methode für Bäume mit Punkten ist 2 . Also traversieren wir beide Bäume; damit bekommen wir die Elemente der zwei Bäumen in Grössenordnung. Dann kämen wir die Ergebnisse der zwei Durchläufen zusammen! Das kann auch in 0 0 & 2 gemacht werden, also die Kost des Verfahrens ist & 2 . 3.2. Wir lösen die Aufgabe mit vollständiger Induktion. Sei die Anzahl der Knotenpunkten im binären Baum . Es ist trivial, dass in dem Fall die Differenz der Anzahl der Blätter und der Anzahl der Knotenpunkten mit Grad eins ist. Nehmen wir an, dass die Eigenschaft für jede & erfüllt ist. Betrachten wir $ ist! Zwei mögliche Fällen sollen , wo ein Blatt des Bäumes behandelt werden: 1. war adjazent zu einem Punkt mit Gradzahl 1 in . Mit der Entfernung von bleibt die Anzahl der Blätter und der Knotenpunkten mit Grad invariant, nämlich statt wird ein Blatt sein. 2. war adjazent zu einem Punkt mit Gradzahl 2 in . Die Entfernung von dekrementiert sowohl die Anzahl der Blätter als auch die Anzahl der Knotenpunkten mit Grad mit . Also die Differenz der Anzahl der Blätter und der Anzahl der Knotenpunkten mit Grad ist invariant, und wegen der Induktionsannahme erfüllt die Eigenschaft. Daraus folgt, dass sie auch für gelten muss. 3.3. Sei die Wurzel des Bäumes. Da die Suchbaumeigenschaft erfüllt und der Baum vollständig ist, folgt, dass # Elemente kleiner und ! Elemente grösser sind als . Deswegen kann nur 7! oder # sein. 1. ! , dann das fehlende Element ist im Intervall !#6;:;:;:=68# ( . 0 Die Aufgabe ist das fehlende Element aus diesem Intervall in & 92 zu finden. 23 1) 2) Abbildung 4: Die zwei möglichen Fälle in Aufgabe 3.2. 2. # , dann das fehlende Element ist im Intervall !* # 7 6;:<:;:<68 ( . 0 Die Aufgabe ist das fehlende Element aus diesem Intervall in 92 zu finden. Wir benutzen das selbe Entscheidungsverfahren rekursiv für die so ausgewählten Teilbäume mit dem dazugehörigen Suchintervall. In der letzten Iteration des Algorithmus erhält man ein zweielementiges Intervall und ein einziges Blatt als Teilbaum; das Ergebnis ist die Zahl, die nicht zu diesem Blatt gehört. Das Ver0 fahren besucht jede Etage des Bäumes genau einmal, das heisst, die Kost ist 2 00 A2< A2 (ein vollständiger binärer Baum mit Elemente besitzt 7 Etagen). Die Optimalität des Algorithmus kann mit dem Gegner-Prinzip gezeigt werden. 3.4. Bezeichne & die Anzahl der Punkten im binären Baum. Setzen wir vollständige Induktion ein! Für & ist die Behauptung trivial. Nehmen wir an, dass alle solche Bäume mit weniger als & Punkten vollständig sind. Sei ein binärer Baum, der die Eigenschaft erfüllt und & Knotenpunkten hat. Dann der linke Unterbaum und der rechte Unterbaum sind wegen der Induktionsbedingung vollständig.Falls die Unterbäume die Höhe und haben, gilt es für : # was bedeutet, dass 7 ! 7 auch für & vollständig ist. 24 0 A2 3.5. Es ist bekannt, dass in einem 2-3 Baum alle Wege von der Wurzel zu den Blättern gleich lang sind. Sei der Schlüssel in der Wurzel ! *>( ! Es ist klar, dass alle Elementen von !#76<:;:;: 6; ( enthält. Da ein Knotenpunkt mindestens zwei Söhne hat, kann dieser Teilbaum höchstens Etagen haben. Es folgt, dass die Teilbäume gehörend zu und " auch höchstens 5 Etagen hoch sein können. In diesen Bäumen müssen wir 162 Elemente speichern, also beinhaltet der grössere Teilbaum mindestens 81 Elemente. Aber in einem 2-3 Baum können höchstens 81 Elemente in 5 Etagen aufgenommen werden; das heisst beide Teilbäume 81-81 Elemente speichern, und zwar so, dass jeder innere Knotenpunkt die Gradzahl hat. Deswegen ist ' . 0 3.6. Nehmen wir an, dass jeder Knotenpunkt die Höhe seines Unterbäumes 2 beinhaltet. 0 Bezeichne ausserdem 2 die Höhe eines AVL-Bäumes! Es verletzt die Allge0 0 meinheit nicht, falls wir annehmen, dass 2 2 . Die Schritte des Algo rithmus sind die folgende (siehe Abbildung 5): (sei das ), dadurch 1. Wir finden und löschen das kleinste Element von erhalten wir den Baum . 2. Angefangen von dem Wurzel von und als nächster Kandidat immer den Rechtssohn des aktuellen Knotenpunktes betrachtet finden wir den ersten 0 0 2 2 oder befriedigt. Knotenpunkt , der 3. Vertauschen wir mit . Sei der Rechtsbaum von und der Linksbaum von der Unterbaum von . Der Baum, den wir so erhalten haben erfüllt offensichtlich gleichzeitig die Suchbaumund die AVL-Eigenschaft. Bezeichne die Gesamtzahl der Elementen in den 0 zwei Bäumen! Da beide Bäumen 7 32 hoch sind, ist die Kost der Methode 0 7 32 . 3.7. Konstruieren wir den Baum! Das Paar mit minimaler zweiter Koordinate muss in der Wurzel stehen, weil die zweiten Koordinaten die Heap-Eigenschaft 0 erfüllen und alle Koordinaten verschieden sind. Sei die Wurzel 6 2 . Da die ersten Koordinaten die Suchbaum-Eigenschaft erfüllen müssen, gehören die Paaren, deren erste Koordinate ist dem linken Teilbaum, die anderen dem 0 rechten Teilbaum. Die selbe Bedachten gelten für die Wurzeln von & 92 und 0 92 , deswegen sind die auch eindeutig bestimmt. Es folgt, dass in jedem Schritt, als wir die Wurzel eines Teilbäumes auf den aufeinanderfolgenden Etagen bestimmen, genau ein Koordinatenpaar die vordefinierten Bedingungen erfüllt. Die Konstruktion zeigt, dass genau ein solcher binärer Baum existiert. 25 s x x S1 S3 Abbildung 5: Wirkungsweise des Algorithmus in Aufgabe 3.6. 3.8. Die Aufgabe kann mit dem Verwenden des bekannten Einfügen-Algorithmus gelöst werden. Die Zahlen in der gegebenen Reihenfolge eingefügt erhält man den Baum auf Abbildung 6. Abbildung 6: Der AVL-Baum in Aufgabe 3.8. 0 3.9. Es soll bemerkt werden, dass zu jedem Punkt 6 2 höchstens A solche verscheidene Nachbaren existieren können, deren Entfernung von kleiner oder gleich ist (es folgt davon, dass die Koordinaten nur ganzzählige Werte 0 aufnehmen dürfen). Für jeden Punkt sollen wir in 7 2 entscheiden, ob mindestens eine der A Plätzen, die genügend nahe zu sind, von einem anderen Punkt besetzt ist. Das kann gemacht werden, falls wir vor der0 Suche aus den Koor0 dinatenpaaren einen Suchbaum in 32 Schritte mit 7 2 Etagen bilden (die Punkten können z.B. lexikographisch geordnet werden und der Suchbaum kann ein AVL- oder - Baum sein). 3.10. Da es keinen Knotenpunktschnitt geschah, können wir sicher sein, dass nur innere Knotenpunkte an der “vorletzten Stufe” (d.h. deren Zeiger auf Rekordelemente zeigen) durch das Einfügen modifiziert wurden. Weiterhin können wir sehen, dass die einzige, unter diesen Bedingungen erlaubte Insert-Operation das Zufügen eines dritten Rekordes zu einem inneren Knotenpunkt mit zwei Söhnen 26 ist. Nach dem Einfügen wird dieser Knotenpunkt schon Söhne haben, müssen also die anderen Einfügen bei einem anderen Knotenpunkt geschehen. 1000 Elemente einzufügen brauchen wir mindestens 1000, nicht zu trennende Knoten an der vorletzten Stufe – natürlich sind mit drei Elemente “gesättigten” Knoten auch erlaubt. Diese sich selbst speichern schon vor der Operation 2000 Elemente. 3.11. Die Datenstruktur: Man nehme einen Knotenpunkt in einem AVL-Baum zu jedem verschiedenen Element auf. In den Knotenpunkten speichert man zusätzlich die Anzahl der Elemente mit diesem Wert (im Weiteren count genannt). Ausserdem wird die Anzahl aller Elemente des Teilbäumes (inklusive Wurzel), dessen Wurzel der Knotenpunkt selbst ist, aufgenommen (hier sumcnt genannt). Das ist in unserem Fall genug, da wir Zahlen und keine zusammengesetzte Datenstrukturen speichern wollen. (Siehe Abbildung 7) Left Left Left Node’s Key Count SumCnt Right Node’s Key Count SumCnt Left Node’s Key Count SumCnt Right Right Left Node’s Key Count SumCnt Node’s Key Count SumCnt Right struct mynode { int value; int count; int sumcnt; mynode *left; mynode *right; } Left Node’s Key Count SumCnt Right Right Left Node’s Key Count SumCnt Right Abbildung 7: Ein spezieller AVL-Baum Die Operationen benutzen oder erweitern die bei den AVL-Bäumen gelernte Operationen. Es folgt eine kurze Beschreibung der Verwirklichung der Operationen: INSERT(i): Der Knotenpunkt gehörend zu wird gesucht; falls gefunden count++; falls nicht, dann wird ein neuer Knotenpunkt erstellt. Bei10 der Suche wird sumcnt von jedem besuchten Knotenpunkt mit 1 inkrementiert. 7 2 DELETE(i): Zuerst wird überprüft, ob das Element in dem Baum überhaupt vorkommt. Falls NUMBER(i) positiv ist, gehen wir wie folgt um: der Knoten punkt gehörend zu wird gesucht. Bei der Suche wird sumcnt von jedem be27 suchten Knotenpunkt mit dekrementiert. Wenn gefunden, count--. Im Fall 0 count==0 wird der Knotenpunkt gelöscht. 7 2 DELETEALL(i): Der Knotenpunkt gehörend zu wird gesucht. Bei der Suche wird sumcnt von jedem besuchten Knotenpunkt um NUMBER(i) dekremen10 tiert. Der Knotenpunkt wird gelöscht. 2 NUMBER(i): Der Knotenpunkt gehörend zu10 wird gesucht; falls er gefunden wurde, return count, sonst return 0. 7 2 ELEMENT(k): Der entsprechende Knotenpunkt wird mit der folgenden Funktion gesucht: int ELEMENT(int k) { mynode *this = &root; if (this->sumcnt < k) return -1; while (true) { if (this->left->sumcnt >= k) this = this->left else if (this->left->sumcnt + this->count >= k) return this->value else { this = this->right; k-=this->left->sumcnt + this->count; } } } Grundidee: Wir wissen über jeden Teilbaum, wie viele Elemente er beinhaltet (sumcnt). Wenn k kleiner ist als sumcnt des linken Unterbäumes eines Knotenpunktes, müssen wir im linken Unterbaum weitersuchen. Ist k größer als sumcnt des linken Teilbaumes aber nur maximal um count größer, haben wir den gesuchten Wert gefunden. Sonst müssen wir in dem rechten Unterbaum weitersuchen, genauer gesagt das k-left->sumcnt-count-te Element des rechten Unterbaumes suchen. 0 7 2 3.12. Ein AVL-Baum hat die Eigenschaft, dass für jeden Punkt des Baumes die Längen ihrer Unterbäume höchstens um unterscheiden. Falls während der Aufbau des Baumes diese Eigenschaft verletzt wird, kann sie mit Drehungen widerhergestellt werden. 28 Betrachten wir das Einfügen eines Elements in den Baum. Ein Einfügen wird als eine Eröffnung einer neuen Etage bezeichnet, falls der ganze Baum vor dem Ein fügen , nach dem Einfügen Etagen hat. Wird ein Element in dem Unterbaum eines Punktes eingefügt, der eine kürzere Länge als der andere Unterbaum hat, dann wird keine neue Etage eröffnet. Also ist auf diese Weise in dem Baum keine neue Etage zu eröffnen. Eine neue Etage ist nur so anzufangen, wenn vor dem Einfügen des eröffnenden Elements alle Unterbäume die gleiche Länge haben. Dann ist nach dem Einfügen keine Drehung erforderlich. Also ist der erste Punkt auf jede Etage während der Konstruktion ohne Drehung eingefügt worden. Es gibt 7 Etagen, also gibt es mindestens 0 2 solche Schritte während der Konstruktion, bei denen nicht gedreht wird. 4 Hashing 4.1. 1. mit linearem Sondieren: !*96 _6 '6 96 6 6 '6 _6 96 '76 ( _6 '76 ( 2. mit quadratischem Sondieren: !*96 '6 96 96 6 96 '6 _6 0 4.2. a) mit linearem Sondieren: Es sei &2 + . Ist der Platz + noch frei, dann können wir dieses Element problemlos speichern. Man sieht auch, dass keine Schlüssel-Kollisionen, die diesen Platz auffüllen könnten, existieren mit linearem 0 Sondieren; gebe es eine solche Kollision, dann wäre der Platz A2 nicht frei. 0 Die Stelle kann zweierlei eingefüllt werden: entweder mit & 2 0 – ist noch frei, keine Kollision, oder mit &=2 – ist schon besetzt und ist frei, eine Kollision. kann nicht schon eingefüllt sein; es würde uns zwingen, die Schlüssel auf den -ten Platz zu stellen, was nicht erlaubt ist. Daraus folgt, dass ein Elementpaar höchstens eine Kollision hervorrufen kann; für Elemente bedeutet das höchstens Kollisionen. b) mit dem selben Gedankenfolge kann es gezeigt werden, dass die maximale Anzahl der Kollisionen ist. 0 4.3. Eine Hashfunktion ist gut, falls die & 2 Werte in allen Restklassen ungefähr 0 im gleichen Verhältnis vorkommen. Dazu ist es notwendig, dass die & 2 Werte ein vollständiges Restklassensystem bilden (modulo Hashtabellengrösse). 29 Bei dieser Hashfunktion kommen aber einige Restklassen nie vor. Es is bekannt, 0 0 dass & 72 nur von & 72 abhängt. & &9 0 7 2 0 72 0 72 kommen nicht vor. Das heisst, die Restklassen 96 und 4.4. Beim Einfügen des Elements wird es höchstens & Kollisionen geben, denn es ausser diesem Element nur & andere in der Hashtabelle sind und lineares Sondieren benutzt wird (wegen dem linearen Sondieren ist es nicht möglich, dass es mehr als eine Kollision mit demselben Element gibt, falls es noch freie Plätze mindestens vorhanden sind). Da ) & , gibt es beim Ablegen des Elements noch einen freien Platz, die wegen dem linearen Sondieren höchstens nach & Kollisionen erreicht wird. & Kollisionen können tatsächlich vorkommen, zum Beispiel, wenn die Hashfunk tion immer dieselbe Addresse für die Elemente gibt. In diesem Fall wird es Kollisionen bei dem Einfügen der ersten & Elemente geben, also wird diese Voraussetzung auch erfüllt. 5 Datenkomprimierung 5.1. Die Häufigkeit der einzigen Buchstaben: Eine optimale Kodierung ist: 7 7 ' In den Uniform-Koden sind alle Kodwörter gleich lang, und weil wir einen Wort von verschiedenen Buchstaben kodieren müssen, sind alle Kodwörter Bit lang. Dass heisst die Länge der Uniform-Kode A ist. Die Länge des Wortes in der Huffman-Kode ist , also Bit wurde erspart. Bei der LZW Kodierung erhält man das folgende Wörterbuch: Das kodierte Wort ist 30 7 A 4 Abbildung 8: Die zu den optimalen Kodierungen gehörende Huffman-Bäume in Aufgabe 5.2. 5.2. Konstruieren wir die zwei Huffman-Bäume (siehe Abbildung 8)! Man sieht leicht, dass gelten muss, sonst wäre die Kodierung eindeutig. Es ist bekannt, dass , also . ist ein möglicher Wert von ; damit ist , eine Lösung. , , Es gilt auch, dass andere Lösungen existieren nicht. Nehmen wir indirekt an, dass ! Dann wäre aber und . Das würde bedeuten, dass @$ , was ein Widerspruch ist. $ ist ein optimaler Huffman-Code be5.3. Zur Verteilung 6<:;:;:<6% 6% # # entsprechenden kannt, und damit auch ein Huffman-Baum, der einen, ! gehörenden KoBlatt beinhaltet. Die Zufügung einer und einer zum ! dewort (mit dem Zweck, die zweite Verteilung zu kodieren) kann als die Beigabe und eines Sohnes zum Knotenpunkt im Huffman-Baum eines ! ! repräsentiert werden. Es soll gezeigt werden, dass der so erhaltene Baum jetzt 6 eine optimale Kodierung der Verteilung 6;:;:;:=6 beschreibt. Es ist aber 0 ! leicht: da :;:;: 72 , es existiert unbedingt ein Ablauf ! des Huffman-Code generierenden Algorithmus, der im ersten Schritt und ! zussamenknüpft - und der danach gebliebener Baum entspricht den originalen, also von hier ist der Algorithmus die vorgegebene Kodierung zu reproduzieren fähig. Damit wurde ein Huffman-Baum zur modifizierten Codemenge gegeben. 5.4. 5.5. Sei der Text eine -elementige -Folge. Mit Huffman-Code sind zur Kodierung Bits benötigt. Mit LZW-Kodierung kodieren wir in jedem Schritt mit mehr Charakter als bevor. Sei ! Wir suchen eine Länge, deren LZWCode schon günstiger ist, als die Huffman: 0 A2 ) 31 ) Also die LZW-Code z.B. einer 4 langen -Folge ist nur 7 A Bit lang. Es sollte noch überprüft werden, ob 16 verschiedene Koden auf Bit wirklich existieren, das kann aber leicht gesehen werden. 0 A2 5.6. Im 5.5. haben wir gesehen, dass zur Kodierung einer Elementigen Folge einer einzigen Buchstabe Code-Wörter genügend sind. Nach dem Funktionsweise der Algorithmus ist es auch klar, dass eine -Lange Charakterfolge mit weniger Code-Wörter nicht kodiert werden kann. Es ist auch klar, 0 + A2 . ) lange Charakterfolge einer Buchstabe dass eine ( auch mit Code-Wörter beschreibbar ist. Da , braucht man mindestens Code-Wörter. 5.7. 5.8. 6 Graphentheoretische Algorithmen 6.1. Der Ablauf des Algorithmus wird in zwei Phasen unterteilt. In der ersten werden aus einer Menge der Superquelle-Kandidaten Knotenpunkte ausgeschlossen. So wird sie auf eine leere oder einelementige Menge reduziert und in dem zweiten Fall der Kandidat überprüft, ob er wirklich eine Superquelle ist. Erste Phase: 0 Initialisierung: Bezeichne die Menge 2 . In jedem Schritt wird ein Paar 0 0 6 =2 6 2 untersucht und gemäss den folgenden Regeln modifiziert: 0 0 1. falls 6 2 entfernt, da in %2 , werden beide Knotenpunkte aus diesem Fall weder noch Superquelle sein kann; 0 0 %2 , wird aus entfernt, da in diesem Fall ; 2. falls 6 2 0 0 3. falls A6 2 2 , wird aus entfernt, da in diesem Fall keine Superquelle sein kann. 32 Somit wird in maximal Schritten auf eine leere oder einelementige Menge reduziert. Falls leer ist, terminiert der Algorithmus und es gibt keine Superquelle. Falls es noch einen Superquelle-Kandidat gibt, wird die zweite Phase des Algorithmus ausgeführt: 5 0 0 0 Sei ? . Es wird die Existenz aller Kanten der Form 6 2 und 6 2 30 0 0 0 2 überprüft. Falls 6 2 %2 oder 6 2 %2 , also es gibt einen Knotenpunkt , der aus nicht, oder, aus dem über eine Kante erreichbar ist, terminiert der Algorithmus und es gibt keine Superquelle; sonst ist eine Superquelle. 0 32 Schritte. Das ergibt eine Zeitschranke von Beide Phasen terminieren in 0 32 für den Algorithmus. 6.2. Beide Algorithmen werden in Pseudokode dargestellt. Anschliessend wird es erläutert, warum sie jeweils die entsprechende Suche realisieren. List L = {}; Tree T = {}; choose starting vertex x; search(x); while (L != {}) { ^^IEdge (v,w) = List.get(L); if w not yet visited { ^^I^^I^^I ^^I^^I^^I add (v,w) to T; search(w); } } search(Vertex v) { visit(v); foreach Edge (v,w) List.put(L, (v,w)); } Der Algorithmus nimmt an, dass der als Eingabe gegebener Graph zusammenhängend, oder im Falle eines gerichteten Graphen stark zusammenhängend ist. In dem Baum wird der traversierte Graph aufgebaut. In der Liste werden jene noch nicht besuchte Kanten gespeichert, die zu einem besuchten Knotenpunkt inzident sind. Aus dieser Liste entnimmt der Algorithmus die nächste Kante. 33 Als Initialisierung wird ein beliebiger Knotenpunkt als Anfangspunkt gewählt, und die Kanten, die zu inzident sind, werden in gespeichert. 0 In einem Schritt wird eine Kante von der Liste entfernt. Sei das die Kante -6 2 . 0 Falls noch nicht besucht worden ist, wird es zusammen mit 6 2 in den Baum aufgenommen und alle Kanten, die zu inzident sind, werden in die Liste eingefügt. Nach dem Ende der Suche wird der Graph ein BFS- oder DFS-Baum des traversierten Graphes sein, da jeder Knotenpunkt genau einmal in aufgenommen wurde. Das Unterschied zwischen der Breitsuche und der Tiefsuche liegt in der Realisation der Methoden List.get und List.put. In einer möglichen Realisation der Breitsuche nimmt List.get die nächste Kante von dem Anfang der Liste und fügt List.put Kanten an das Ende der Liste ein, also implementieren sie eine FIFOListe. Dementsprechend kann eine Tiefsuche durch eine LIFO-Liste mit derselben List.get Operation und eine List.put, die die Kanten an den Anfang der Liste einfügt, realisiert werden. Eine FIFO-Liste realisiert die Breitsuche wegen Folgendem: Wenn die Suche den 0 Knotenpunkt durch die Kante 6 2 besucht hat, werden danach sämtliche andere Knotenpunkte, die zu benachbart sind, besucht und erst danach die zu benachbarte Knotenpunkte, u.s.w. Zur Veranschaulichung wird ein Beispiel der FIFO-Liste vor und nach dem Besuchen des Knotenpunktes gezeigt: 0 0 0 0 0 vor Besuch des Knotenpunktes : -6 2 6 6 286 6 2 6 -6 82:;:;: 6 2 nach Besuch des Knotenpunktes : 0 0 0 0 0 0 0 6 286 6 2 6 -6 82:;:;: 6 2 6 6 286 6 2:;:;: 6 2 Genauso kann es eingesehen werden, dass eine LIFO-Liste die0 Tiefsuche realisiert: Wenn die Suche einen Knotenpunkt durch die Kante 6 2 als erstes besucht hat, werden danach nicht die zu , sondern zuerst die zu dem neu besuchten Knotenpunkt benachbarte Knotenpunkte besucht. Ebenso wird hier ein Beispiel der Liste vor und nach dem Besuchen des Knotenpunktes zur Veranschaulichung gezeigt: 0 0 0 0 0 vor Besuch des Knotenpunktes : -6 2 6 6 286 6 2 6 -6 82:;:;: 6 2 nach Besuch des Knotenpunktes : 0 0 0 0 0 0 0 6 286 6 2:;:;: 6 286 6 2 6 -6 2 6 6 2:;:<: -6 2 6.3. Es ist trivial, dass alle drei Algorithmen ohne Markierung der besuchten Knotenpunkte betrachtet identisch sind: sie implementieren eine linksrekursive Tiefensuche. Die Unterbäume des binären Baumes sind nach dem folgenden Aufrufschema besucht: traversiere(x) {traversiere(x.links); traversiere(x.rechts); 34 } Der Tiefennummer eines Knotenpunktes wird beim Eintritt in den Unterbaum, dessen Wurzel der Knotenpunkt ist, ausgeteilt – das ist identisch mit dem visit(x) Befehl des Preorder-Verfahrens, der vor dem Besuch der Söhne aufgerufen wird. Der Abschlussnummer für einen Knotenpunkt wird nach dem Besuch aller Söhne des Knotenpunktes ausgegeben, was dieselbe Effekt hat, als wäre ein visit(x) Befehl nach traversiere(x.rechts) – genau wie bei der Postorder Traversierung. Damit ist die Equivalenz der Algorithmen gezeigt. 6.4. Es wird ein konstruktiver Beweis angegeben. Seien die Knotenpunkte des zu konstruierenden Graphen mit den Zahlen 6;:;:;: 6 nummeriert.5 Sei der Graph vollständig und seien die Kanten wie folgt gerichtet: für 6 76;:;:;: 6 ? , falls 0 0 0 0 %2 und 76 2 %2 , also sind die Kanten von Knotenpunk, ist 6 2 ten mit kleineren Zahlen zu Knotenpunkten mit grösseren Zahlen gerichtet. ist ein DAG, weil die Knotenpunkte in wachsender Reihenfolge der Nummerierung eine gültige Stufenzerlegung angeben. Alle einfache Graphen mit weniger als Kanten können aus dem vollständigen Graphen mit dem Verlassen von Kanten bekommen werden. Der so erhaltene Graph wird ebenfalls ein DAG sein, da aus einem DAG Kanten entfernt, aber nicht dazugenommen worden sind. 6.5. Als erster Schritt werden leere Adjazenzlisten für jeden Punkt bis konstruiert. Es werden dann die Einträge der einzelnen Knotenpunkte in der originalen Adjazenzlisten sequentiell überprüft. Falls sich unter den Nachbarn des Knotenpunktes einen Eintrag befindet, wird einen Eintrag zu der Liste des Knotenpunktes zugefügt. Da die Bearbeitung eines Eintrags in konstant viele Schritte möglich ist und es Knotenpunkte und insgesamt Nachbarn in der Ad10 jazenzliste gibt, terminiert der Algorithmus in 2 Schritte. Da die Länge der 0 0 Ausgabe 1 2 ist, müssen jegliche Algorithmen eine Zeitschranke 2 haben, also ist der Algorithmus grössenordnungsmässig optimal. 6.6. Bemerkung: in der Aufgabe werden die Bezeichnungen der in der Vorlesung behandelten Methode, beschrieben in [Vorlesungsskript] verwendet. Sei angenommen, dass der Algorithmus die Komponenten in einer falschen Reihenfolge ausgibt. Ohne Beschränkung der Allgemeinheit wird es gesagt, dass die falsche0 Reihenfolge aus der Richtigen mit dem Vertauschen der Komponente 2 zu bekommen ist. In diesem Fall gibt es eine Kante in , die und aus in läuft (siehe Abbildung 9/b). In dem Graphen läuft deswegen aus der Komponente nach (siehe Abbildung 9/c). In diesem Fall aber wäre es bei der zweiten Tiefensuche in möglich gewesen, die Komponente aus durch 35 a e b c e Abbildung 9: die starken Komponenten von einem Graph, Aufgabe 6.6. zu erreichen. Da in der Tiefensuche von zuerst die Komponente besucht wurde, würden und eine starke Komponente bilden, was ein Widerspruch ist. Es werden also die Komponenten in der richtigen Reihenfolge ausgegeben. 6.7. Zur Erinnerung: Jeder Baum wählt die minimale benachbarte Kante aus. Am Anfang sind alle Punkte als einpünktige Bäume zu verstehen. Die schon genommenen Kanten werden blau gefärbt. Sei der blauen Kanten. der Graph Konstruieren wir den Hilfsgraphen folgenderweise: Zu jeder Komponente (Baum) von wird ein Punkt in aufgenommen. Die Kanten von sind die von den Komponenten ausgewählten Kanten in . Da der Algorithmus zu jeder Komponente von eine Kante gewählt hat, kann es in keinen isolierten Punkt geben, also bestehen alle Komponenten von mindestens aus 2 Punkten. Jede Komponente in wird nun zu einem Punkt zusammengezogen, in werden die entsprechenden Komponenten zu eine Komponente verschmolzen. Die Anzahl der Komponenten in wurde also mindestens halbiert in höchstens 7 Iterationen haben wir den aufspannenden Baum gefunden, da es dann nur eine Komponente in geben wird. 6.8. Es wird bewiesen, dass jeder maximale Fluss ein blockierender Fluss ist. Gebe es in dem betrachteten Netzwerk einen Fluss , der maximal, aber nicht blockierend ist. In diesem Fall gibt es einen Weg im Netzwerk, der kei0 0 0 ne gesättigte Kante enthält, also ) so, dass 29 2 2 . Dann können aber die Flusswerte aller Kanten in und somit auch der Flusswert von 36 mit vergrössert werden. Das widerspricht der Maximalität von , also ist jeder maximale Fluss auch ein blockierender Fluss. Die Ungültigkeit der umgekehrten Richtung lässt sich durch einen Beispiel zeigen: 3(3) s 3(3) 2(3) t s 1(1) 2(3) 3(3) 0(1) 3(3) 3(3) t 3(3) Abbildung 10: ein blockierender Fluss und ein Fluss mit einem grösseren Flusswert im selben Netzwerk In Abbildung 10 ist ein blockierender Fluss mit dem Flusswert , und ein Fluss mit dem Flusswert im selben Netzwerk dargestellt, also ist nicht jeder blockierender Fluss ein maximaler Fluss. 6.9. Wir modifizieren den Algorithmus von Dijkstra folgenderweise: Sei 0 2 die Anzahl der bis jetzt gefundenen kürzesten Wege. In jeder Iteration: 1. Wir wählen den Punkt aus den noch nicht fertigen Punkten aus, dessen Ab stand von den fertigen Punkten minimal ist: , ! )( minimal 2. Wir erweitern die Menge der fertigen Punkte mit diesem Punkt: 5 $ ? 3. Wir aktualisieren die -Werte aller noch nicht fertigen Punkte. Hier wird einer der folgenden drei Fälle eintreten: Entweder haben wir durch die Aufnahme von einen kürzeren Weg gefunden, in diesem Fall wird der -Wert des Punktes mit dem von überschrieben, da es nun genau ! ,( kürzeste Wege gibt, die alle durch gehen. Oder wir haben weitere gleich lange Wege gefunden (genau ! ,( Stück) und der -Wert des Punktes muss mit dem von inkrementiert werden. 37 Der dritte Fall (nämlich dass wir einen längeren Weg gefunden haben) ist uninteressant. Formal ausgedrückt: $ wenn ! ,( "! 6 ( wenn ! ,( "! 6 ( 4. Auch ! ( , dann ! (3$ ! ( , dann ! 3 ( $ ! ,( ! ( ! )( muss (wie im Algorithmus von Dijkstra) aktualisiert werden. Falls alle Punkte fertig sind, entsprechen die -Werte der Anzahl der kürzesten 10 2 wie beim Algorithmus von Dijkstra. Wege. Der Zeitbedarf ist 6.10. Hier werden zwei Lösungen gegeben: a) Die Methode PERT gibt einen kritischen Weg im Graphen an, was ein längster Weg ist. b) Betrachte man eine topologische Ordnung 6 6#3:<:;: . (Es existiert eine topologische Ordnung, denn der Graph ist ein DAG. Die topologische Ordnung kann man schnell angeben.) Sei folgenderweise definiert: 5 0 0 2 %2 ?)6 0 0 0 0 2 %2 ,6 '286 %2 6 0 76 92 '6 0 286 die -Werte der alten Kanten bleiben unverändert. Wir können annehmen, dass der Anfangspunkt des längsten Weges in ist. Der längste Weg in ist zugleich der längste Weg in , falls man die Kante aus 0 weglässt. Bezeichne man die Länge des längsten Weges von nach mit 92 . 0 Sei ! ,( 76 %2 . Nun iteriere man folgenderweise: 0 0 0 92$ 23 6 92 0 und ! ,(3$ , falls die Kante 692 das Maximum geleistet hat. (Gibt es mehrere solche Kanten, wählt man eine beliebige aus.) 0 0 0 Für einen gegebenen Punkt sind die 2 -Werte für alle 692 %2 Kanten wegen der topologischen Ordnung schon bekannt. Am Ende ist der Endpunkt des längsten Weges der Punkt mit dem maximalen 0 endlichen 92 Wert. Anhand des Arrays kann man den längsten Weg mit der gelernten Methode rückwärts rekonstruieren. (Die Punkte in umgekehrter Reihenfolge: -6 ! ,( 6 ! ! ,( ( 6<:;:;: bis erreicht wird.) 10 Dazu benötigt man insgesamt 2 Schritte. 38 6.11. Ein gieriger Algorithmus besitzt die gewünschten Eigenschaften. Die Methode wird in Pseudokode angegeben und ihre Korrektheit anschliessend mit vollständiger Induktion bewiesen. //Initialisierung choose vertex x with at least 1 neighbor; classify(x, A); classify(neighbor(x), B); //Iteration while exists unclassified vertex k { k_A = number of neighbors to A; k_B = number of neighbors to B; if (k_B >= k_A) classify(k, A) else classify(k, B); } Knotenpunkte des Graphen in zwei Punktenklassen Induktionsschritt: seien und , 6 schon so eingeordnet, dass mindestens die Hälfte der Kanten des von aufgespannten Teilgraphen zwischen und laufen. Sei der nächste einzuordnende Knotenpunkt und habe er & Nachbarn aus und & & (der andere Fall kann analog behandelt werden). In diesem aus . Sei & Fall wird von dem Algorithmus in die Punktenklasse eingeordnet. Zwischen 5 ? und den Punktenklassen laufen mindestens die Hälfte der Kanten des 5 ? von aufgespannten Teilgraphen wegen Folgendem: Erstens, laut der Induktionsannahme liefen zwischen und mindenstens die Hälfte der Kanten des von aufgespannten Teilgraphen. Zweitens, die Anzahl der Kanten zwischen den zwei Punktenklassen wurde mit & , die Anzahl der Kanten innerhalb Punktenklasse wurde mit & vergrössert. Drittens, es gilt & & und viertens, die Anzahl der Kanten innerhalb Punktenklasse wurde nicht vergrössert. Es muss noch der Anfangsschritt angegeben werden. Sei einen Teilgraph mit zwei Knotenpunkten und einer Kante betrachtet. Gehöre ein Knotenpunkt in Punkten klasse , der andere in . Dieser Graph erfüllt die Induktionsbedingung. Der Algorithmus macht Iterationen und in jeder muss er sämtliche Nachbarn des 10 einzuordnenden Knotenpunktes verarbeiten. Das ergibt eine Zeitschranke 2 , also arbeitet der Algorithmus in linearer Zeit. 6.12. Das Brett wird mit einem Graph repräsentiert, wobei jeder Knotenpunkt in dem Graphen ein Feld des Bretts entspricht. Zwei Knotenpunkte sind benachbart 39 genau dann, wenn die entsprechenden Felder in dem Schachbrett mit einem Pferdesprung voneinander erreichbar sind. 0 -0 Da ein Pferd höchstens Felder schlagen kann, gilt es: %2%$ 92 (siehe Abbildung 11). Auf dem Brett befinden sich & gegnerische Pferde. Alle Felder, auf denen ein Pferd steht oder auf die ein gegnerischer Pferd schlagen kann, werden aus dem Graphen entfernt. Ob ein Feld entfernt werden soll oder nicht, kann in konstant vielen Schritten getroffen werden, wenn der -0 Graph mit einer Adjazenzliste gegeben ist. Wegen '2 müssen es höchstens &% 7& Knotenpunkte aus dem Graphen entfernt werden, also ist diese Operation 10 82 Schritten zu ausführen. In dem übriggebliebenen Graphen ist mit einer in Traversiermethode aus der Position unseres Pferdes als Startpunkt die Menge der erreichbaren Knotenpunkte zu bekommen. Da der Graph höchstens Knoten10 2 Schritte. Somit hat punkte und Kanten hat, braucht die Traversierung 10 der Algorithmus Zeitschranke 2 . Abbildung 11: mögliche Schritte aus einem Feld, Aufgabe 6.12. 6.13. Zuerst ermittle man mit dem Algoritmus von Floyd die Abstände zwischen 10 allen Punktpaaren. Das benötigt 2 Schritte. Mit dem Algorithmus bekommt man eine Matrix . In allen Spalten von wird nach dem maximalen Wert gesucht 0 (d.h. für 92 ). Hier wird -mal das Maximum von Elementen gesucht, was in 0 0 0 2 Zeit möglich ist. Am Ende bestimme man 92 ; das ist in 32 0 Zeit möglich. Die Punkte mit den kleinsten 92 Wert sind die Zentren von . 6.14. Wir konstruieren einen aufspannenden Baum von (z.B. mit BFS oder 0 DFS) in 2 Schritten.5 Die Blätter des Baumes sind sicherlich keine Artikulationspunkte, denn ? wird von dem Rest des aufspannenden Baumes zusammengehalten. 1. Die Adjazenzmatrix von ist: 40 f 3 b 1 6 5 1 d 1 4 2 1 a 3 6 e 2 c 6.15. Abbildung 12: Der Graph zur Aufgabe 6.15. (Vergleiche mit Abbildung 12) Die Menge und die 5 5 ? 5 6 ;? 5 6 46 5 6 46 5 6 46 6 46 -Werte nach jeder Iteration: A ! ? ? ? 6 6 6 ! ! A! ! ! ? ( 6 6 6 ( ( ( ( ( 2. Die gefragte Kanten dürfen in keinem minimalen Weg enthalten 0 0 0 0 0 0 sein. Diese Kanten sind: 46 2 , A6 2 , 6 2 , 6 2 , 6 2 , -6 2 . 6.16. Sei folgenderweise definiert. Seien die Punkte von die 4 lange Teilzeichenketten. So bekommt man Punkte, da es genau soviele Teilzeichenketten gibt. (Zwei Teilzeichenketten können einander überlappen.) 41 Eine Kante wird zwischen zwei Knotenpunkten gezogen, falls die Punkte die Grenzen eines gültigen Blocks bilden. Die Kante soll vorwärts gerichtet sein, das heisst der Leserichtung der Bitfolge entsprechen. (Siehe Abbildung 13.) Der 0 Graph kann in 2 Schritten aufgebaut werden. ??XY XY?? Abbildung 13: Gültiger Block und dazugehörige Kante zur Aufgabe 6.16. ist ein DAG, und zwar schon topologisch geordnet. Eine gute Zerteilung der Bitfolge auf gültige Blöcke ist äquivalent mit einem Weg in von dem ersten bis zu dem letzten Punkt. Hier muss man also einen Weg vom ersten zum letzten Punkt finden, das geht in 0 2 Zeit. 6.17. 6.18. 7 Die Sprache NP 7.1. Es wird ein Algorithmus angegeben, der die Methode als Subroutine benutzt. Zuerst wird mit ermittelt, ob der Graph mit Farben färbbar ist. Falls nicht, terminiert der Algorithmus. Sonst wird ein beliebig gefärbter Klikk mit drei aufgenommen und der folgende Schritt auf sämtliche Punkten als Hilfsgraph Punkte durchgeführt: Zwei Punkte von werden mit dem gerade bearbeiteten Punkt zusammengebunden und er bekommt die Farbe des dritten Punktes. Da mit drei Farben färbbar ist, sind die zwei Punkte im , mit denen nach dem Einziehen der neuen Kanten der Graph mit drei Farben färbbar bleibt, in höchstens drei Versuche zu finden. Nachdem alle Punkte bearbeitet wurden, wird der resultierende Graph immer noch mit drei Farben färbbar, und für jedes Punkt in wird eine Farbe festgelegt sein. Das ergibt eine korrekte Färbung. 42 7.2. Ein möglicher Hinweis ist der Wert von . Mit diesem kann es in Polynomzeit beantwortet werden, ob die Eingabe in der Sprache ist. Es ist noch zu beweisen, dass die Länge von nicht ”zu gross” sein kann. Sei eine Lösung, 0 dann gilt : ::, 2 . ist durch teilbar, denn 6;:;:;: 6;:;:;: . Davon folgt , also dass die Länge des Hin weises kleiner als die von der Eingabe ist. Somit gilt: . 7.3. Gesucht ist ein Algorithmus, der nicht mehr als ( ist eine positive Konstante) Speicherzellen benutzt. 0 Seien die zwei Punktenklassen und und sei % . Sei 2 . Eine Paarung wird ein-eindeutig mit einer Permutation von Zahlen beschrieben, nämlich mit den Endpunkten der Kanten in einer der Punktenklassen. Um zu entscheiden, ob genau & Paarungen hat, ist ein denkbarer Verfahren, alle mögliche Paarungen zu kontrollieren. Bis zu verschiedene Paarungen sind möglich, aber für das Kontrollieren einer Paarung muss gleichzeitig nur die der Paarung entsprechende Permutation in dem Speicher gehalten werden. Da die Be , schreibung einer Permutation Speicherzellen benötigt, ist die Sprache in + generiert werden falls die nacheinanderkommenden Permutationen in können. Hier wird ein Verfahren in Pseudokode angegeben, die die Permutationen von Elementen generiert: perm(1, ... n) {1, perm(2, 2, perm(1, ... i, perm(1, ... n, perm(1, } ... n); 3, ... n); ... i-1, i+1, ... n); ... n-1); perm(i) {i;} + Es ist einfach zu sehen, dass eine , die diesen Algorithmus realisiert, in arbeitet, also ist die Sprache in . 0 %2 7.4. Die Sprache ist in : ein Hinweis sei einen Weg, der aus Kanten besteht. Dieser erfüllt die zwei Forderungen für Hinweise bei Sprachen in trivialerweise. Im Folgenden wird eine Karp-Reduktion auf das HamiltonWeg-Problem gezeigt. 43 Sei einen Graphen gegeben; die Aufgabe ist es, einen Hamiltonischen Weg in zu finden. Es wird ein isolierter Knotenpunkt zu dazugenommen und 5 der resultierender Graph bezeichnet. Im wird ein Weg der ? als 0 0 Länge 2 2 mit dem Algorithmus gesucht. Einen Weg der 0 Länge entspricht einem Hamilton-Weg in . Das Dazunehmen 2 > in eines Knotenpunktes ist in Polynomzeit durchführbar, also ist die Transformation eine Karp-Reduktion auf den Hamilton-Weg-Problem, der -vollständig ist. Die Sprache ist also -vollständig. 7.5. Die Anzahl aller möglichen Teilmengen ist bekanntlich . Aus Symmetriegründen ist die Anzahl der möglichen 6 Paare # . Die generate and-test-Methode ist also keineswegs polynomial. Stattdessen können wir gezielt diejenige Aufteilungen der Punkte suchen, die solche Punktmengen erzeugen, die höchstens durch zwei Kanten verbunden sind. Das heisst, durch das Entfernen von maximal zwei Kanten soll der Graph in zwei Komponenten zerfallen. Der Algorithmus geht wie folgt: Ist der Graph nicht zusammenhängend (dieses kann man in Polynomzeit entscheiden), kann er in zwei Komponenten zerlegt werden, so dass zwi . schen die Komponenten keine Kante führt. Falls der Graph zusammenhängend ist, aber durch das Entfernen einer Kante in zwei Komponenten zerfällt, hat man zwei Komponenten gefunden, die genau eine Kante verbindet, also ist der Graph . Existiert keine solche Kante, soll man wie folgt vorgehen: Falls durch das Entfernen zweier Kanten nicht mehr zusammenhängend bleibt, haben wir zwei Komponenten gefunden zwischen denen genau zwei Kanten führen, also ist . War keiner der drei Schritte erfolgsreich, existiert keine solche Aufteilung der Punkte, die die obige Bedingungen erfüllt, also ist . 10 2 -mal testen, ob ein Insgesamt müssen wir also höchstens Graph mit Punkten zusammenhängend ist. Das heißt, es ist in Polynomzeit ent scheidbar, ob ein Graph ist. 7.6. . Nach der Stirling-Formel ist 0 7 7 7 28: Da die Länge des Outputs eine exponentielle Funktion der Länge des Inputs ist, gehört nicht zu . 1. Die Länge des Outputs ist 7 44 2. Die Primfaktorzerlegung lässt sich auf dieses Problem zurückführen. Nach dem heutigen Stand der Wissenschaft bleibt diese Frage offen. 3. Wir haben als Input die binäre Darstellung von . Sei die Position des von & . Da ) links (allgemein: von der MSB-Richtung) ersten true-Bits 0 , ist 3 2 & . Das Output zu generieren dauert lang, also ist . 7.7. 1. & -FARBE Eine Färbung mit & Farben ist ein guter Zeuge. Ob das wirklich eine richtige Färbung ist oder nicht, kann man in Polynomzeit überprüfen. Es ist genug zu kontrollieren, ob alle Punkte mit genau einer der & Farben gefärbt wurden, und die benachbarte Knotenpunkte verschiedene Farben bekommen haben. 0 Das geht in 2 Schritten. 2. Es ist bekannt, dass 3-FARBE -vollständig ist. & ) ist. 3. Nun wird gezeigt, dass 3-FARBE & -FARBE, wobei * Sei der Graph, über dem es zu entscheiden ist, ob er mit Farben färbbar ist. Konstruiere man folgenderweise: 0 0 2 $ 0 %2 0 2 $ 82 0 5 0 2 6 '2 0 286 0 %2 6 0 28? (Das heisst, nehmen wir einen vollständigen Graphen mit &+ Punkten zu , und verbinden wir alle Punkte von mit allen Punkten von ). Falls mit & Farben färbbar ist, wird höchstens mit Farben gefärbt, sind mit allen anderen Knotenpunkten von denn die Punkte von in nicht vorverbunden, und so können die Farben der Punkte von kommen. Umgekehrt gilt es auch: kann mit höchstens 3 Farben gefärbt werden, ist ohne weiteres & -färbbar, nämlich sollen die Punkte von die andere &. Farben haben. Das heisst: ist genau dann mit 3 Farben färbbar, falls mit & Farben färbbar ist. 7.8. In eine Kiste können höchstens 2 Gegenstände gepackt werden. 1. Die Gegenstände, deren Gewicht grösser oder gleich ist, kommen allein in die Kisten. 45 2. Die gebliebenen Gegenstände, deren Gewicht zwischen und ist, können im besten Fall gepaart werden. Definiere man einen bipartiten Graphen folgenderweise: die Punkte beider Punktklassen sind zu den Gegenständen zugeordnet. Die Punkte sind verbunden, falls die Gegenstände und in einer Kiste gepackt werden können. In diesem Graphen soll man eine maximale Paarung suchen. Die Endpunkte der Kanten der Paarung sind die Gegenstände, die gepaart in die Kisten kommen, die nicht bedeckten Punkte kommen allein in die Kisten. Eine maximale Paarung ist äquivalent damit, dass möglichst viele Gegenstände gepaart in die Kisten kommen. Das ist – zusammen mit der gegebenen Bedingung – mit der minimalen Anzahl von Kisten äquivalent. 7.9. 7.10. 7.11. 2. 1. Die Teilmenge selbst ist ein guter Hinweis, der in Polynomzeit kontrolliert werden kann. MAX - UNABHÄNGIG Sei der Input von dem MAX - UNABHÄNGIG Problem der Graph , und die Zahl . (Es ist zu entscheiden, ob der Graph eine unabhängige Punktmenge mit mindenstens Knotenpunkten hat). Bestehe aus den Punkten von und von weiteren Knotenpunkten, die mit allen Punkten von verbunden werden, aber unter einander sind sie disjunkt. Sei & . Falls es in mindestens unabhängige Punkte gibt, bilden diese Punkte mit den neuen Punkten einen bipartiten Graphen mit & Punkten. Die andere Richtung lautet folgenderweise: falls es in einen bipartiten Teilgraphen mit Punkte gibt, soll mindestens einer der sein, und mindestens aus Punkte – sei es mit bezeichnet – aus . Falls es eine Kante unter den Punkten von gebe, würde es mit ein Dreieck bilden, aber ein Graph, der mit 2 Farben färbbar ist, kann keine Dreiecke beinhalten. 7.12. 1. PARTITION TMSP Mit der folgenden Parameterwahl sind die zwei Probleme äquivalent: 46 : 2. TMSP RSP Man wähle die Werte & und $ . Der mit diesen Parametern eine Lösung, wofür gilt: und RSP Algorithmus sucht 4: So wird RSP äquivalent mit TMSP. 7.13. 2. 1. MAXKREIS Ein guter Hinweis ist der Kreis von Länge & selbst, was in Polynomzeit kontrolliert werden kann. HAMILTON - KREIS MAXKREIS beantwortet die Frage, ob es in einem Graphen einen Kreis 0 2 ist diese Frage mit dem HAMILTON der Länge & gibt. Mit & KREIS Problem äquivalent, d. h. HAMILTON - KREIS MAXKREIS. MAXKREIS 7.14. 2. 1. HAMILTON - WEG Ein guter Hinweis ist der Hamiltonsche Weg selbst, was schnell (in Polynomzeit) kontrolliert werden kann. HAMILTON - KREIS HAMILTON - WEG Hier soll man entscheiden, ob der Graph einen Hamiltonschen Kreis beinhaltet, wobei man das HAMILTON - WEG Problem als eine Subroutine benutzen kann. 0 6 %2 $ Hamiltonscher Kreis in Hamiltonscher Weg 0 0 6 und 6 '2 2 0 0 2 kontrollieren, ob ein Das heisst, man soll für alle Kanten 6 '2 Hamiltonscher Weg in existiert. Man muss noch irgendwie garantieren, dass die Subroutine die Existenz eines Hamiltonschen Weges zwischen und im Graphen entscheidet. Dazu modifiziere man den Graphen folgenderweise: Sei : 0 0 2 $ 0 2 $ 0 %2 %2 5 6 ) ? 6 5 0 0 6 286 -6 928?): 47 Es existiert ein Hamiltonscher Weg in zwischen wenn es in einen (beliebigen) Hamiltonschen Weg 0 0 lich bei 6 2 anfängt, und mit -6 92 endet. 7.15. und genau dann, gibt, da sicher- a) Eine notwendige Bedingung für die Existenz eines Spannbaumes von ist, dass zusammenhängend ist. Indirekt: Bezeichene man 2 Kom mit und . ponenten von muss von auch erreichen, was nur durch möglich ist, aber die Punkte von haben Grad 1 in , Widerspruch. In kann ein spannender Baum mit einer beliebigen Methode (BFS, DFS, usw.) gesucht werden. Falls es von einem Knotenpunkt keine Kante nach führt, dann gibt es keinen solchen Baum, da nur Nachbarn aus haben kann, aber in einem Baum kön nen zwei Punkte mit Grad 1 nicht benachbart sein. Falls einen Nachbar in hat, soll man sie mit einer Kante mit einem von ihren Nachbarn verbinden, damit man am Ende einen guten aufspannenden Baum bekommt. b) ist -vollständig Der spannende Baum ist ein guter Zeuge. Es kann in Polynomzeit überprüft werden, ob der spannende Baum richtig ist. HAMILTON - WEG Es ist zu entscheiden, ob es in einem gegebenen Graphen ein Ha 0 miltonscher Weg existiert. Man 2 5 soll für alle Punktpaare 6 laufen lassen, mit 6 -? . Der spannende Baum, in dem die Punkte von genau die Punkte mit Grad 1 sind, ist ein Hamiltonscher Weg. 7.16. 1. Die abdeckende Punktmenge überprüft werden kann. ist ein guter Hinweis, da sie in Polynomzeit 48 2. MAX - UNABHÄNGIG Die Existenz einer abdeckenden Punktmenge mit höchstens & Punkten ist äquivalent damit, dass es eine unabhängige Punktmenge mit mindenstens 0 ist abdeckend %2 A& Punkte gibt, da ist unabhängig. 7.17. 7.18. 7.19. Literatur [Vorlesungsskript] A. Orban und Z. Mann: Vorlesung über Algorithmentheorie BME SZIT 2001. 49