Algorithmen und Datenstrukturen



Ähnliche Dokumente
Babeș-Bolyai Universität Cluj Napoca Fakultät für Mathematik und Informatik Grundlagen der Programmierung MLG5005. Paradigmen im Algorithmenentwurf

3.1 Konstruktion von minimalen Spannbäumen Es gibt zwei Prinzipien für die Konstruktion von minimalen Spannbäumen (Tarjan): blaue Regel rote Regel

1 topologisches Sortieren

S=[n] Menge von Veranstaltungen J S kompatibel mit maximaler Größe J

Anmerkungen zur Übergangsprüfung

Vorkurs Informatik WiSe 15/16

Algorithmen II Vorlesung am

Kostenmaße. F3 03/04 p.188/395

Kapiteltests zum Leitprogramm Binäre Suchbäume

Datenstrukturen & Algorithmen

Entscheidungsbäume. Definition Entscheidungsbaum. Frage: Gibt es einen Sortieralgorithmus mit o(n log n) Vergleichen?

Konzepte der Informatik

Kapitel 6. Komplexität von Algorithmen. Xiaoyi Jiang Informatik I Grundlagen der Programmierung

Algorithmen und Datenstrukturen (für ET/IT)

1 Einführung. 2 Grundlagen von Algorithmen. 3 Grundlagen von Datenstrukturen. 4 Grundlagen der Korrektheit von Algorithmen

Suche in Spielbäumen Spielbäume Minimax Algorithmus Alpha-Beta Suche. Suche in Spielbäumen. KI SS2011: Suche in Spielbäumen 1/20

Theoretische Grundlagen der Informatik

Algorithmen & Datenstrukturen 1. Klausur

Statistische Untersuchungen zu endlichen Funktionsgraphen

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Zeichen bei Zahlen entschlüsseln

3.2 Binäre Suche. Usr/local/www/ifi/fk/menschen/schmid/folien/infovk.ppt 1

Programmierkurs Java

Das Briefträgerproblem

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Algorithmen und Datenstrukturen 2

Folge 19 - Bäume Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

16. All Pairs Shortest Path (ASPS)

Grundlagen der Theoretischen Informatik, SoSe 2008

Überblick. Lineares Suchen

Primzahlen und RSA-Verschlüsselung

Algorithmen und Datenstrukturen

Informationsblatt Induktionsbeweis

Kapitel 4: Dynamische Datenstrukturen. Algorithmen und Datenstrukturen WS 2012/13. Prof. Dr. Sándor Fekete

Professionelle Seminare im Bereich MS-Office

1. Man schreibe die folgenden Aussagen jeweils in einen normalen Satz um. Zum Beispiel kann man die Aussage:

Guten Morgen und Willkommen zur Saalübung!

Datenstrukturen und Algorithmen

4 Greedy-Algorithmen (gierige Algorithmen)

Übersicht. Datenstrukturen und Algorithmen. Übersicht. Divide-and-Conquer. Vorlesung 9: Quicksort (K7)

Rekursionen. Georg Anegg 25. November Methoden und Techniken an Beispielen erklärt

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse Lösung 10 Punkte

Sortieren durch Einfügen. Prof. Dr. W. Kowalk Sortieren durch Einfügen 1

WS 2009/10. Diskrete Strukturen

Abschnitt: Algorithmendesign und Laufzeitanalyse

Das Dilemma des Einbrechers Wer die Wahl hat, hat die Qual!

OECD Programme for International Student Assessment PISA Lösungen der Beispielaufgaben aus dem Mathematiktest. Deutschland

ERGÄNZUNGEN ZUR ANALYSIS II MITTELWERTSATZ UND ANWENDUNGEN

Name:... Vorname:... Matrikel-Nr.:... Unterschrift:...

Einführung. Vorlesungen zur Komplexitätstheorie: Reduktion und Vollständigkeit (3) Vorlesungen zur Komplexitätstheorie. K-Vollständigkeit (1/5)

Datenaufbereitung in SPSS. Daten zusammenfügen

WS 2013/14. Diskrete Strukturen

Literatur. Dominating Set (DS) Dominating Sets in Sensornetzen. Problem Minimum Dominating Set (MDS)

Kapitel 2: Formale Sprachen Kontextfreie Sprachen. reguläre Grammatiken/Sprachen. kontextfreie Grammatiken/Sprachen

15 Optimales Kodieren

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Grundbegriffe der Informatik

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

4. Lernen von Entscheidungsbäumen. Klassifikation mit Entscheidungsbäumen. Entscheidungsbaum

Algorithmen und Datenstrukturen

Häufig wiederkehrende Fragen zur mündlichen Ergänzungsprüfung im Einzelnen:

Übungen für Woche 10

Übungskomplex Felder (1) Eindimensionale Felder Mehrdimensionale Felder

Algorithmen und Datenstrukturen. Große Übung vom Nils Schweer

Graphen: Datenstrukturen und Algorithmen

Institut für Programmierung und Reaktive Systeme 25. August Programmier-Labor Übungsblatt. int binarysearch(int[] a, int x),

Branch-and-Bound. Wir betrachten allgemein Probleme, deren Suchraum durch Bäume dargestellt werden kann. Innerhalb des Suchraums suchen wir

Wir arbeiten mit Zufallszahlen

Tangentengleichung. Wie lautet die Geradengleichung für die Tangente, y T =? Antwort:

Effiziente Algorithmen und Datenstrukturen I. Kapitel 9: Minimale Spannbäume

AVL-Bäume Analyse. Theorem Ein AVL-Baum der Höhe h besitzt zwischen F h und 2 h 1 viele Knoten. Definition Wir definieren die nte Fibonaccizahl:

SUDOKU - Strategien zur Lösung

WS 2008/09. Diskrete Strukturen

Lineare Gleichungssysteme

Gleichungen Lösen. Ein graphischer Blick auf Gleichungen

Lineare Gleichungssysteme

Würfelt man dabei je genau 10 - mal eine 1, 2, 3, 4, 5 und 6, so beträgt die Anzahl. der verschiedenen Reihenfolgen, in denen man dies tun kann, 60!.

Nichtlineare Optimierung ohne Nebenbedingungen

Informatik I WS 07/08 Tutorium 24

Repetitionsaufgaben: Lineare Funktionen

a n auf Konvergenz. Berechnen der ersten paar Folgenglieder liefert:

Musterlösungen zur Linearen Algebra II Blatt 5

13. Binäre Suchbäume

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Suchen und Sortieren Sortieren. Heaps

Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung)

1. Motivation / Grundlagen 2. Sortierverfahren 3. Elementare Datenstrukturen / Anwendungen 4. Bäume / Graphen 5. Hashing 6. Algorithmische Geometrie

Summenbildung in Bauteiltabellen mit If Then Abfrage

Die Verbindung von Linearer Programmierung und Graphentheorie

Die Komplexitätsklassen P und NP

Kapitel 5: Dynamisches Programmieren Gliederung

1 Mathematische Grundlagen

Programmiertechnik II

KONSTRUKTION VON ROT-SCHWARZ-BÄUMEN

Eine Baumstruktur sei folgendermaßen definiert. Eine Baumstruktur mit Grundtyp Element ist entweder

Algorithmen und Datenstrukturen Suchbaum

Effiziente Algorithmen und Datenstrukturen I. Kapitel 10: Lineare Algebra

Übersicht. Datenstrukturen und Algorithmen Vorlesung 5: Rekursionsgleichungen (K4) Übersicht. Binäre Suche. Joost-Pieter Katoen. 20.

Transkript:

Algorithmen und Datenstrukturen Werner Struckmann Wintersemester 2005/06

9. Entwurf von Algorithmen 9.1 Einführung 9.2 Teile-und-Beherrsche-Algorithmen 9.3 Gierige Algorithmen 9.4 Backtracking-Algorithmen 9.5 Dynamische Programmierung

Entwurf von Algorithmen In diesem Kapitel stellen wir anhand von Beispielen einige typische Prinzipien für den Entwurf von Algorithmen vor. Die folgenden Techniken haben wir (implizit oder explizit) bereits kennen gelernt. Schrittweise Verfeinerung des Problems Reduzierung der Problemgröße durch Rekursion Einsatz von Algorithmenmustern 9.1 Einführung 9-1

Schrittweise Verfeinerung des Problems Die erste Formulierung des Problems erfolgt in einem sehr abstrakten Pseudocode. Die schrittweise Verfeinerung basiert auf dem Ersetzen von Pseudocode durch verfeinerten Pseudocode und letztlich durch konkrete Algorithmenschritte. 9.1 Einführung 9-2

Problemreduzierung durch Rekursion Diese Technik kann angewendet werden, wenn das Problem auf ein gleichartiges, aber kleineres Problem zurückgeführt werden kann. Die Rekursion muss schließlich auf ein oder mehrere kleine Probleme führen, die sich direkt lösen lassen. Rekursion bietet sich an, wenn die Problemstruktur rekursiv aufgebaut ist. Beispiele: Listen, Bäume. Zu rekursiven Lösungen gibt es iterative Entsprechungen (zum Beispiel durch Einsatz eines Kellers, s. Aufgabe 23). Bei der Auswahl zwischen iterativer und rekursiver Lösung ist die Effizienz der Realisierung zu berücksichtigen. 9.1 Einführung 9-3

Einsatz von Algorithmenmustern Beispiele für Algorithmenmuster: Inkrementelle Vorgehensweise Teile-und-Beherrsche-Algorithmen Gierige Algorithmen (Greedy Algorithmen) Backtracking-Algorithmen Dynamische Programmierung Die Zuordnung eines Musters zu einem Algorithmus ist nicht immer eindeutig und manchmal sogar unmöglich. Beispielsweise kann der Algorithmus von Kruskal als inkrementeller und als gieriger Algorithmus gesehen werden. Es gibt weitere Algorithmenmuster. 9.1 Einführung 9-4

Inkrementelle Vorgehensweise Beispiel: Sortieren durch Einfügen benutzt eine inkrementelle Herangehensweise. Nachdem das Teilfeld a[1..j 1] sortiert wurde, wird das Element a[j] an der richtigen Stelle eingefügt, woraus sich das sortierte Teilfeld a[1..j] ergibt. Weitere Beispiele: Algorithmus von Kruskal Algorithmus von Prim Beide Algorithmen bauen schrittweise einen minimalen Spannbaum auf. 9.1 Einführung 9-5

Teile-und-Beherrsche-Algorithmen Teile das Problem in eine Anzahl von Teilproblemen auf. Beherrsche die Teilprobleme durch rekusives Lösen. Wenn die Teilprobleme hinreichend klein sind, dann löse sie auf direktem Wege. Verbinde die Lösungen der Teilprobleme zur Lösung des Ausgangsproblems. 9.2 Teile-und-Beherrsche-Algorithmen 9-6

Beispiel: Sortieren durch Mischen Sortieren durch Mischen (Mergesort, vgl. Abschnitt 3.2) arbeitet rekursiv nach folgendem Schema: 1. Teile die Folge in zwei Teilfolgen auf. 2. Sortiere die beiden Teilfolgen. 3. Mische die sortierten Teilfolgen. 4 2 9 5 8 2 1 6 4 2 9 5 8 2 1 6 2 4 5 9 1 2 6 8 1 2 2 4 5 6 8 9 9.2 Teile-und-Beherrsche-Algorithmen 9-7

Beispiel: Sortieren durch Mischen Alternativ könnte man die Liste auch in mehr als zwei Listen aufteilen, hätte dann aber in der Mischphase größeren Aufwand. Allgemein: Die Rekursionstiefe kann durch stärkere Spaltung verringert werden. Dies bedingt allerdings einen größeren Aufwand in der Teile- und der Zusammenführungsphase. 9.2 Teile-und-Beherrsche-Algorithmen 9-8

Beispiel: Türme von Hanoi n Scheiben verschiedener Größe sind aufeinandergestapelt. Es liegen stets nur kleinere Scheiben auf größeren. Der gesamte Stapel soll Scheibe für Scheibe umgestapelt werden. Ein dritter Stapel darf zur Zwischenlagerung benutzt werden, ansonsten dürfen die Scheiben nirgendwo anders abgelegt werden. Auch in jedem Zwischenzustand dürfen nur kleinere Scheiben auf größeren liegen. 9.2 Teile-und-Beherrsche-Algorithmen 9-9

Beispiel: Türme von Hanoi Gesucht ist ein Algorithmus, der dieses Problem löst. Dazu muss der Algorithmus angeben, in welcher Reihenfolge die Scheiben zu bewegen sind. 1. Bringe die obersten n 1 Scheiben von Turm 1 zu Turm 3. 2. Bewege die unterste Scheibe von Turm 1 zu Turm 2. 3. Bringe die n 1 Scheiben von Turm 3 zu Turm 2. 2 1 3 9.2 Teile-und-Beherrsche-Algorithmen 9-10

Beispiel: Türme von Hanoi 1. Bringe die obersten n 1 Scheiben von Turm 1 zu Turm 3. 2. Bewege die unterste Scheibe von Turm 1 zu Turm 2. 3. Bringe die n 1 Scheiben von Turm 3 zu Turm 2. proc hanoi(n:int; t1, t2, t3: Turm) begin if n > 1 then hanoi(n-1, t1, t3, t2); fi; <bewege die Scheibevon t1 nach t2>; if n > 1 then hanoi(n-1, t3, t2, t1); fi; end 9.2 Teile-und-Beherrsche-Algorithmen 9-11

Beispiel: Türme von Hanoi Diese Lösung besteht aus einer rekursiven Prozedur hanoi(n, t1, t2, t3). Die Problemgröße ist n. Der Aufrufhanoi(n, t1, t2, t3) bewegt den Stapel von t1 nacht2 und verwendett3 als Hilfsstapel. Für die Anzahl T(n) der notwendigen Schritte gilt T(n) = Explizit ergibt sich T(n) = 2 n 1. 1 für n = 1, 2T(n 1)+1 für n>1. 9.2 Teile-und-Beherrsche-Algorithmen 9-12

Komplexität von Teile-und-Beherrsche-Algorithmen Die Problemgröße sei durch eine natürliche Zahl n gegeben. Die Berechnung der Komplexität führt häufig auf Rekurrenzgleichungen der Form T(n) = Θ(1), falls n klein ist, at(n/b) + f(n), falls n groß genug ist, oder T(n) = f(t(n 1),...,T(n k)) mit gegebenen Anfangswerten T(0),...,T(k 1). Beispiel: Sortieren durch Mischen, Türme von Hanoi. Wir wiederholen jetzt das Mastertheorem und das Verfahren zur Lösung linearer Rekurrenzgleichungen mit konstanten Koeffizienten. 9.2 Teile-und-Beherrsche-Algorithmen 9-13

Beispiel: Die Multiplikation nach Karatsuba Wie groß ist die Komplexität des klassischen Verfahrens zur Multiplikation natürlicher Zahlen? Wenn der erste Faktor n-stellig und der zweite m-stellig ist, dann müssen zuerst n m Einzelmultiplikationen durchgeführt werden. Anschließend sind m Zahlen der Maximallänge n+m zu addieren. Das Ergebnis ist im Allgemeinen eine (n + m)-stellige Zahl. Den größten Anteil trägt offenbar das Produkt n m bei. Die Komplexität des Verfahrens liegt daher in Θ(n m) bzw. in Θ(n 2 ), wenn beide Zahlen die Länge n besitzen. 9.2 Teile-und-Beherrsche-Algorithmen 9-14

Beispiel: Die Multiplikation nach Karatsuba Im Jahre 1962 stellte A. Karatsuba ein schnelleres Verfahren zur Multiplikation vor. Die Idee besteht darin, die Zahlen x und y der Länge nin Stücke der Länge n/2 aufzuteilen, sodass x y = a 10 n/2 + b = c 10 n/2 + d gilt. Beispiel: n = 4, x = 3141 = 31 10 2 + 41 9.2 Teile-und-Beherrsche-Algorithmen 9-15

Beispiel: Die Multiplikation nach Karatsuba Wir erhalten: x y = (a 10 n/2 + b)(c 10 n/2 + d) = ac 10 n +(ad + bc) 10 n/2 + bd = ac 10 n +((a + b)(c + d) ac bd) 10 n/2 + bd Die Berechnung des Produkts zweier Zahlen x und y der Länge nwird zurückgeführt auf die Berechnung der drei Produkte ac, bd und (a + b)(c + d) der Länge n/2. Dann wird dasselbe Verfahren rekursiv auf diese drei Produkte angewendet. 9.2 Teile-und-Beherrsche-Algorithmen 9-16

Beispiel: Die Multiplikation nach Karatsuba Beispiel: x = 3141, y = 5927 x y = 3141 5927 = 31 59 10 4 + ((31+41)(59+27) 31 59 41 27) 10 2 + 41 27 = 31 59 10 4 + (72 86 31 59 41 27) 10 2 + 41 27 = 1829 10 4 +(6192 1829 1107) 10 2 + 1107 = 1829 10 4 + 3256 10 2 + 1107 = 18616707 9.2 Teile-und-Beherrsche-Algorithmen 9-17

Beispiel: Die Multiplikation nach Karatsuba Für die Komplexität T(n) des Verfahrens gilt: T(n) = k, n = 1 ( n 3 T 2) + kn, n>1 Diese Rekurrenzgleichung besitzt die Lösung T(n) = 3kn log 2(3) 2kn = Θ ( n log 2(3) ) = Θ ( n 1,585). Das ist deutlich günstiger als Θ ( n 2). Allerdings wirken sich die Verbesserungen erst bei großen Zahlen aus. 9.2 Teile-und-Beherrsche-Algorithmen 9-18

Beispiel: Die Multiplikation nach Karatsuba Wir haben oben die Faktoren x und y in je zwei Teile zerlegt. Durch Aufspalten in noch mehr Teile können wir die Laufzeit weiter verbessern: Für jedesε>0gibt es ein Multiplikationsverfahren, das höchstens c(ε)n 1+ε Schritte benötigt. Die Konstante c(ε) hängt nicht von n ab. In den 1970er Jahren wurde diese Schranke auf verbessert. O (n log(n) log(log(n))) 9.2 Teile-und-Beherrsche-Algorithmen 9-19

Gierige Algorithmen Annahmen: Es gibt eine endliche Menge von Eingabewerten. Es gibt eine Menge von Teillösungen, die aus den Eingabewerten berechnet werden können. Es gibt eine Bewertungsfunktion für Teillösungen. Die Lösungen lassen sich schrittweise aus Teillösungen, beginnend bei der leeren Lösung, durch Hinzunahme von Eingabewerten ermitteln. Gesucht wird eine/die optimale Lösung. Vorgehensweise: Nimm (gierig) immer das am besten bewertete Stück. 9.3 Gierige Algorithmen 9-20

Beispiel: Algorithmus zum Geldwechseln Münzwerte: 1, 2, 5, 10, 20, 50 Cent und 1, 2 Euro. Wechselgeld soll mit möglichst wenig Münzen ausgezahlt werden. 1,42 : 1 +20 Cent + 20 Cent + 2 Cent Allgemein: Wähle im nächsten Schritt die größtmögliche Münze. In unserem Münzsystem gibt diese Vorgehensweise immer die optimale Lösung. Im Allgemeinen gilt dies nicht. Angenommen, es stünden 1, 5 und 11 Cent Münzen zur Verfügung. Um 15 Cent herauszugeben, ergäbe sich: gierig: 11 + 1 + 1 + 1 + 1, optimal: 5 + 5 + 5. 9.3 Gierige Algorithmen 9-21

Gierige Algorithmen func greedy(e: Eingabemenge): Ergebnis begin var L: Ergebnismenge; var x: Element; E.sort(); while! E.empty() do x E.first(); E.remove(x); if valid(l {x}) then L.add(x); fi; od; return L; end 9.3 Gierige Algorithmen 9-22

Beispiel: Bedienreihenfolge im Supermarkt n Kunden warten vor einer Kasse. Der Bezahlvorgang von Kunde i dauere c i Zeiteinheiten. Welche Reihenfolge der Bedienung der Kunden führt zur Minimierung der mittleren Verweilzeit (über alle Kunden)? Die Gesamtbedienzeit T ges = n i=1 c i ist konstant. Die mittlere Verweilzeit ist T = 1 n (c 1 +(c 1 + c 2 )+ +(c 1 + +c n )) = 1 n (nc 1 +(n 1)c 2 +(n 2)c 3 + +2c n 1 + c n ) = 1 n n (n k + 1)c k k=1 9.3 Gierige Algorithmen 9-23

Beispiel: Bedienreihenfolge im Supermarkt Die mittlere Verweilzeit pro Kunde steigt, wenn Kunden mit langer Bedienzeit vorgezogen werden. sinkt, wenn Kunden mit kurzer Bedienzeit zuerst bedient werden. wird minimal, wenn die Kunden nach c i aufsteigend sortiert werden. Konsequenzen: Greedy-Algorithmus ist geeignet. Die Funktion zur Bestimmung des nächsten Kandidaten wählt den Kunden mit minimaler Bedienzeit. Frage: Ist dies eine geeignete Strategie für die Prozessorvergabe? 9.3 Gierige Algorithmen 9-24

Beispiel: Algorithmus von Kruskal Selektiere fortwährend eine verbleibende Kante mit geringstem Gewicht, die keinen Zyklus erzeugt, bis alle Knoten verbunden sind (Kruskal, 1956). Eine eindeutige Lösung ist immer dann vorhanden, wenn alle Gewichte verschieden sind. 4 3 3 2 6 3 5 8 6 4 5 7 2 6 Nach Wahl der Kanten 2, 2, 3 und 3 darf die verbleibende 3 nicht gewählt werden, da sonst ein Zyklus entstünde. 9.3 Gierige Algorithmen 9-25

Matroide Greedy-Algorithmen liefern nicht immer eine optimale Lösung. Mithilfe der Theorie der gewichteten Matroide kann bestimmt werden, wann ein Greedy-Algorithmus eine optimale Lösung liefert. Die Theorie der bewerteten Matroide deckt nicht alle Fälle ab. 9.3 Gierige Algorithmen 9-26

Matroide Ein Matroid ist ein Paar M = (S,I) mit folgenden Eigenschaften: S ist eine endliche Menge. I ist eine nichtleere Familie von Teilmengen von S. Vererbungseigenschaft: Sind A B und B I, so ist A I. Austauscheigenschaft: Sind A, B I und A < B, so gibt es ein x (B\ A) mit A {x} I. Die Mengen iniheißen unabhängig. Eine unabhängige Menge A Iheißt maximal, wenn es keine Erweiterung x mit A {x} Igibt. 9.3 Gierige Algorithmen 9-27

Matroide Ein Matroid M = (S,I) heißt gewichtet, wenn es eine Gewichtsfunktion w : S R + gibt. Die Gewichtsfunktion lässt sich auf Teilmengen A S durch w(a) = w(a) erweitern. x A Eine Menge A Imit maximalem Gewicht heißt optimal. 9.3 Gierige Algorithmen 9-28

Matroide Satz: Es sei M = (S,I) ein gewichtetes Matroid mit der Gewichtsfunktion w : S R +. Der folgende gierige Algorithmus gibt eine optimale Teilmenge zurück. func greedy(m, w): I begin A ; sortiere S in monoton fallender Reihenfolge nach dem Gewicht w; foreach x S do if A {x} I then A A {x}; return A; end 9.3 Gierige Algorithmen 9-29

Matroide Satz: Die Komplexität des gierigen Algorithmus liegt in O(n log n + n f(n)), wobei n log n der Aufwand für das Sortieren und f(n) der Aufwand für den Test A {x} ist. n ist die Anzahl der Elemente von S, d. h. S = n. 9.3 Gierige Algorithmen 9-30

Matroide Beispiel: Es sei G = (V, E) ein ungerichteter Graph. Dann ist M G = (S G,I G ) ein Matroid, dabei gilt: S G = E, A E: A I G A azyklisch. Eine Menge A von Kanten ist genau dann unabhängig, wenn der Graph G A = (V, A) einen Wald bildet. Der Algorithmus von Kruskal ist ein Beispiel für das obige allgemeine Verfahren. 9.3 Gierige Algorithmen 9-31

Backtracking-Algorithmen Das Backtracking realisiert eine systematische Suchtechnik, die die Menge aller möglichen Lösungen eines Problems vollständig durchsucht. Führt die Lösung auf einem Weg nicht zum Ziel, wird zur letzten Entscheidung zurückgegangen und dort eine Alternative untersucht. Da alle möglichen Lösungen untersucht werden, wird eine Lösung wenn sie existiert stets gefunden. 9.4 Backtracking-Algorithmen 9-32

Beispiel: Wegsuche in einem Labyrinth Gesucht ist ein Weg in einem Labyrinth von einem Start- zu einem Zielpunkt. Gehe zur ersten Kreuzung und schlage dort einen Weg ein. Markiere an jeder Kreuzung den eingeschlagenen Weg. Falls eine Sackgasse gefunden wird, gehe zurück zur letzten Kreuzung, die einen noch nicht untersuchten Weg aufweist und gehe diesen Weg. 9.4 Backtracking-Algorithmen 9-33

Beispiel: Wegsuche in einem Labyrinth Die besuchten Kreuzungspunkte werden als Knoten eines Baumes aufgefasst. Der Startpunkt bildet die Wurzel dieses Baumes. Blätter sind die Sackgassen und der Zielpunkt. Der Baum wird beginnend mit der Wurzel systematisch aufgebaut. Wegpunkte sind Koordinatentupel (x, y). Im Beispiel ist (1, 1) der Startpunkt und (1, 3) der Zielpunkt. (1,1) (2,1) (2,2) (3,1) (1,2) (2,3) (3,2) (3,3) (1,3) 9.4 Backtracking-Algorithmen 9-34

Backtracking-Algorithmen Es gibt eine endliche Menge K von Konfigurationen. K ist hierarchisch strukturiert: Es gibt eine Ausgangskonfiguration k 0 K. Zu jeder Konfiguration k x K gibt es eine Menge k x 1,..., k x nx von direkt erreichbaren Folgekonfigurationen. Für jede Konfiguration ist entscheidbar, ob sie eine Lösung ist. Gesucht werden Lösungen, die von k 0 aus erreichbar sind. 9.4 Backtracking-Algorithmen 9-35

Backtracking-Algorithmen proc backtrack(k: Konfiguration) Konfiguration begin if k ist Lösung then print(k); fi; foreach Folgekonfiguration k von k do backtrack(k ); od; end Dieses Schema terminiert nur, wenn der Lösungsraum endlich und wiederholte Bearbeitung einer bereits getesteten Konfiguration ausgeschlossen ist (keine Zyklen). Kritisch ist ggf. der Aufwand. Er ist häufig exponentiell. 9.4 Backtracking-Algorithmen 9-36

Backtracking-Algorithmen (Varianten) Lösungen werden bewertet. Zuletzt wird die beste ausgewählt. Das angegebene Schema findet alle Lösungen. Oft genügt es, den Algorithmus nach der ersten Lösung zu beenden. Aus Komplexitätsgründen wird eine maximale Rekursionstiefe vorgegeben. Als Lösung dient dann beispielsweise die am besten bewertete Lösung aller bisher gefundenen. Branch-and-Bound-Algorithmen. 9.4 Backtracking-Algorithmen 9-37

Branch-and-Bound-Algorithmen Das angegebene Schema untersucht jeden Konfigurationsteilbaum. Oft kann man schon im Voraus entscheiden, dass es sich nicht lohnt, einen bestimmten Teilbaum zu besuchen. Dies ist zum Beispiel bei einer Sackgasse der Fall oder wenn man weiß, dass die zu erwartende Lösung auf jeden Fall schlechter sein wird, als eine bisher gefundene. In diesem Fall kann auf die Bearbeitung des Teilbaums verzichtet werden. 9.4 Backtracking-Algorithmen 9-38

Branch-and-Bound-Algorithmen Beispiele: Spiele (insbesondere rundenbasierte Strategiespiele), zum Beispiel Schach. Konfigurationen entsprechen den Stellungen, Nachfolgekonfigurationen sind durch die möglichen Spielzüge bestimmt. Nachweisbar schlechte Züge müssen nicht untersucht werden. Erfüllbarkeitstests von logischen Aussagen. Planungsprobleme. Optimierungsprobleme. 9.4 Backtracking-Algorithmen 9-39

Beispiel: Das N-Damen-Problem Es sollen alle Stellungen von n Damen auf einem n n-schachbrett ermittelt werden, bei denen keine Dame eine andere bedroht. Es dürfen also nicht zwei Damen in der gleichen Zeile, Spalte oder Diagonale stehen. 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9.4 Backtracking-Algorithmen 9-40

Beispiel: Das N-Damen-Problem K sei die Menge aller Stellungen mit einer Dame in jeder der ersten m Zeilen, 0 m n, sodass je zwei Damen sich nicht bedrohen. K enthält alle Lösungen. Nicht jede Stellung lässt sich allerdings zu einer Lösung erweitern. So ist zum Beispiel unten jedes Feld in der 7. Zeile bereits bedroht, sodass dort keine Dame mehr gesetzt werden kann. Durch Ausnutzung von Symmetrien lässt sich der Aufwand verringern. 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9.4 Backtracking-Algorithmen 9-41

Beispiel: Das N-Damen-Problem proc platziere(zeile: int) begin var i: int; for i 1 to n do if <feld (zeile, i) nicht bedroht> then <setze Dame auf (zeile, i)>; if zeile = n then <gib Konfiguration aus>; else platziere(zeile + 1); fi; fi; od; end 9.4 Backtracking-Algorithmen 9-42

Beispiel: Das N-Damen-Problem 4 3 2 1 1 2 3 4 4 3 2 1 4 3 2 1 1 2 3 4 4 3 2 1 4 3 2 1 4 3 2 1 1 2 3 4 1 2 3 4 Sackgasse 4 3 2 1 4 3 2 1 1 2 3 4 Sackgasse 4 3 2 1 Lösung 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 9.4 Backtracking-Algorithmen 9-43

Beispiel: Das N-Damen-Problem Das N-Damen-Problem ist für n 4lösbar. Wenn die erste Dame nicht richtig gesetzt ist, werden allerdings bis zu (n 1)! Schritte benötigt, um dies herauszufinden. Nach der stirlingschen Formel ist der Aufwand also exponentiell. n! n n e n 2πn, Für jedes n 4ist ein Verfahren bekannt (Ahrens, 1912), das in linearer Zeit eine Lösung findet (nur eine, nicht alle). Es basiert auf der Beobachtung, dass in Lösungsmustern häufig Rösselsprung-Sequenzen auftreten. Im Jahre 1990 ist ein schneller probabilistischer Algorithmus veröffentlicht worden, dessen Laufzeit in O(n 3 ) liegt. 9.4 Backtracking-Algorithmen 9-44

Beispiel: Problem des Handlungsreisenden Gegeben seien n durch Straßen verbundene Städte mit Reisekosten c(i, j) zwischen je zwei Städten i und j, 1 i, j n. Gesucht ist die billigste Rundreise, die jede Stadt genau einmal besucht (Traveling Salesman Problem, TSP). Eine solche Kantenfolge heißt hamiltonscher Zyklus. Dieser Graph ist vollständig. Augsburg Frankfurt 2 4 9 9 9 3 Braunschweig 1 8 3 2 2 Erfurt 8 6 9 3 Celle Darmstadt Die billigste Rundreise kostet 13 Einheiten. 9.4 Backtracking-Algorithmen 9-45

Beispiel: Problem des Handlungsreisenden Ein naiver Algorithmus beginnt bei einem Startknoten und sucht dann alle Wege ab. Die Komplexität dieses Verfahrens liegt in O(n!). Das folgende Verfahren führt zu einer Näherungslösung: 1. Die Kanten werden nach ihren Kosten sortiert. 2. Man wählt die billigste Kante unter den beiden folgenden Nebenbedingungen: Es darf kein Zyklus entstehen (außer am Ende der Rundreise). Kein Knoten darf zu mehr als 2 Kanten adjazent sein. 9.4 Backtracking-Algorithmen 9-46

Beispiel: Problem des Handlungsreisenden Die Laufzeit dieses gierigen Algorithmus liegt in O(n 2 log n 2 ). Das Verfahren führt nicht immer zu einer optimalen Lösung. Trotzdem wird es in der Praxis erfolgreich eingesetzt. Branch-and-Bound: Wenn man weiß, dass eine Lösung mit Kosten k existiert (zum Beispiel durch obigen Algorithmus), dann kann ein Backtrack-Algorithmus alle Teillösungen abschneiden, die bereits teurer als k sind. 9.4 Backtracking-Algorithmen 9-47

Dynamische Programmierung Rekursive Problemstruktur: 1. Aufteilung in abhängige Teilprobleme. 2. Berechnen und Zwischenspeichern wiederbenötigter Teillösungen. 3. Bestimmung des Gesamtergebnisses unter Verwendung der Teillösungen. Die dynamische Programmierung ist mit der Teile-und-Beherrsche-Methode verwandt. Die Teilprobleme sind aber abhängig. Einmal berechnete Teillösungen werden wiederverwendet. Die dynamische Programmierung wird häufig bei Optimierungsproblemen angewendet. 9.5 Dynamische Programmierung 9-48

Beispiel: Fibonacci-Zahlen fib(n) = 0 n = 0 1 n = 1 fib(n 1)+fib(n 2) n 2 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,... func fib(n: int): int begin if n < 2 then return n; fi; return fib(n-1) + fib(n-2); end 9.5 Dynamische Programmierung 9-49

Beispiel: Fibonacci-Zahlen Berechnung vonfib(5): fib5 fib4 fib3 fib3 fib2 fib2 fib1 fib2 fib1 fib1 fib0 fib1 fib0 1 fib1 fib0 1 1 0 1 0 1 0 9.5 Dynamische Programmierung 9-50

Beispiel: Fibonacci-Zahlen Die Berechnung vonfib(5) führt zweimal auf die Berechnung von fib(3). Die zugehörigen Teilbäume werden zweimal ausgewertet. Aufwandsabschätzung: T(n) = Anzahl der Funktionsaufrufe 1 n = 0, n = 1 = 1+T(n 1)+T(n 2) n 2 T(n) wächst exponentiell. Wir haben in der Übung gezeigt: T(n) = ( 1+ 1 5 1,45 1,62 n. 5 ) 1+ 5 2 n + ( 1 1 5 5 ) 1 5 2 n 1 9.5 Dynamische Programmierung 9-51

Beispiel: Fibonacci-Zahlen Iterative dynamische Lösung (vgl. Abschnitt 2.1): func fibdyn(n: int): int begin var i, result, minus1, minus2: int; if n < 2 then return n; fi; minus2 0; minus1 1; for i 2 to n do result minus1 + minus2; minus2 minus1; minus1 result; od; return result; end 9.5 Dynamische Programmierung 9-52

Beispiel: Das Rucksackproblem Das Rucksackproblem (knapsack problem): Ein Wanderer findet einen Schatz aus Edelsteinen. Jeder Edelstein hat ein bestimmtes Gewicht und einen bestimmten Wert. Er hat nur einen Rucksack, dessen Kapazität durch ein maximales Gewicht begrenzt ist. Gesucht ist ein Algorithmus, der diejenige Befüllung des Rucksacks ermittelt, die einen maximalen Wert hat, ohne die Gewichtsbeschränkung zu verletzen. 9.5 Dynamische Programmierung 9-53

Beispiel: Das Rucksackproblem Gegeben: Kapazität c N, Menge O mit n NObjekten o 1,...,o n, Gewichtsfunktion g : O Nmit j O g(j)>c, Bewertungsfunktion w : O N. Gesucht ist eine Menge O O mit g(j) c und w(j) maximal. j O j O Da Gegenstände nur vollständig oder gar nicht eingepackt werden, spricht man auch vom 0-1-Rucksackproblem. Beim fraktionalen Rucksackproblem können auch Teile eines Gegenstands ausgewählt werden. 9.5 Dynamische Programmierung 9-54

Beispiel: Das Rucksackproblem Der gierige Algorithmus führt nicht zur Lösung: O ={o 1, o 2, o 3 }, Kapazität c = 5. Gewichte: g(o 1 ) = 1, g(o 2 ) = 2, g(o 3 ) = 3. Werte: w(o 1 ) = 6, w(o 2 ) = 10, w(o 3 ) = 12. Ein gieriger Algorithmus wählt das Objekt mit dem größten relativen Wert. r(o) = w(o) g(o), r(o 1) = 6, r(o 2 ) = 5, r(o 3 ) = 4. O ={o 1, o 2 }, g(j) = 3 5=c, w(j) = 16. j O j O Die optimale Lösung ist O ={o 2, o 3 }, j O g(j) = 5 = c, j O w(j) = 22. 9.5 Dynamische Programmierung 9-55

Beispiel: Das Rucksackproblem Backtracking liefert die korrekte Lösung, ist aber ineffizient. O ={o 1, o 2, o 3, o 4 }, c = 10. Gewichte: g(o 1 ) = 2, g(o 2 ) = 2, g(o 3 ) = 6, g(o 4 ) = 5. Werte: w(o 1 ) = 6, w(o 2 ) = 3, w(o 3 ) = 5, w(o 4 ) = 4. 10 nein ja 10 8 10 8 8 6 10 4 8 2 8 2 6 0 10 5 4 8 3 2 8 3 2 6 1 0 zur Disposition o : (g,w) o 1 : (2,6) o 2 : (2,3) o 3 : (6,5) o 4 : (5,4) (0/0),(5/4),(6/5),(2/3),(7/7),(8/8),(2/6),(7/10),(8/11),(4/9),(9/13),(10/14), jeweils (Gewicht/Wert). Es ist O ={o 1, o 2, o 3 } mit g(o ) = 10 und w(o ) = 14. 9.5 Dynamische Programmierung 9-56

Beispiel: Das Rucksackproblem Rückgabewert ist der Wert der optimalen Füllung. Aufruf: btknapsack(1,c). func btknapsack(i, rest: int): int begin if i = n then if g(i) > rest then return 0; else return w(i); fi; else if g(i) > rest then return btknapsack(i+1,rest); else return max(btknapsack(i+1,rest), btknapsack(i+1,rest-g(i))+w(i)); fi; fi; end Das Optimierungspotential durch Vermeidung wiederkehrender Berechnungen wird nicht genutzt. 9.5 Dynamische Programmierung 9-57

Beispiel: Das Rucksackproblem Dynamische Programmierung: O ={o 1,...,o 5 }, c = 10. Gewichte: g(o 1 ) = 2, g(o 2 ) = 2, g(o 3 ) = 6, g(o 4 ) = 5, g(o 5 ) = 4. Werte: w(o 1 ) = 6, w(o 2 ) = 3, w(o 3 ) = 5, w(o 4 ) = 4, w(o 5 ) = 6. Es wird ein zweidimensionalen Feld f[i, r] berechnet: r i 0 1 2 3 4 5 6 7 8 9 10 5 0 0 0 0 6 6 6 6 6 6 6 4 0 0 0 0 6 6 6 6 6 10 10 3 0 0 0 0 6 6 6 6 6 10 11 2 0 0 3 3 6 6 9 9 9 10 11 f[4, 9] = 10: Wenn o 4 und o 5 bei der Restkapazität 9 zur Disposition stehen, beträgt der Wert der zusätzlichen Füllung 10. 9.5 Dynamische Programmierung 9-58

Beispiel: Das Rucksackproblem Die zentrale Anweisung in btknapsack: return max(btknapsack(i+1,rest), btknapsack(i+1,rest-g(i))+w(i)); r i 0 1 2 3 4 5 6 7 8 9 10 5 0 0 0 0 6 6 6 6 6 6 6 4 0 0 0 0 6 6 6 6 6 10 10 3 0 0 0 0 6 6 6 6 6 10 11 2 0 0 3 3 6 6 9 9 9 10 11 f[3, 8] = max(f[4, 8], f[4, 2]+5) = max(6, 0+5) = 6 9.5 Dynamische Programmierung 9-59

Beispiel: Das Rucksackproblem Der Algorithmus arbeitet folgendermaßen: 1. Berechne zunächst die Werte f[n, 0],...,f[n, c]. 2. Berechne anschließend f[i, 0],...,f[i, c] für i = n 1 bis i = 2 unter Rückgriff auf die bereits berechneten Werte der Zeile i + 1. 3. Das Gesamtergebnis f[1, c] ergibt sich dann aus f[1, 10] = max(f[2, 10], f[2, 8]+6) = max(11, 9+6) = 15. 9.5 Dynamische Programmierung 9-60

Beispiel: Das Rucksackproblem func dynknapsack(n, c: int): int begin var f[2..n,0..c]:int; var i, r: int; for r 0 to c do if g(n)>rthen f[n,r] 0; else f[n,r] w(n);fi; for i n - 1 downto2do for r 0 to c do if g(i) > r then f[i,r] f[i+1,r]; else f[i, r] max(f[i+1,r],f[i+1,r-g(i)]+w(i)); fi; od; od; if g(1) > c then returnf[2, c]; else return max(f[2,c],f[2,c-g(1)]+w(1)); end 9.5 Dynamische Programmierung 9-61

Beispiel: Suche in einem Text Im Folgenden werden Zeichenketten als Felder behandelt. Gegeben: Feld t[1..n] von Zeichen, der Text, Feld p[1..m] von Zeichen, das Muster (pattern). Es sei m n. In der Regel ist sogar m<< n. Gesucht: Vorkommen von p in t, d. h. Indices s, 0 s n m, mit t[s + 1..s + m] = p[1..m]. 9.5 Dynamische Programmierung 9-62

Beispiel: Suche in einem Text Naive Lösung: Vergleiche für alle s = 0..n m und für alle i = 0..m die Zeichen p[i] = t[s + i]. proc naiv(t, p): begin var i,s: int; for s 0 to n - m do i 1; while i m && p[i] = t[s+i] do i i+1; od; if i = m+1 then print(s);fi; od; end 9.5 Dynamische Programmierung 9-63

Beispiel: Suche in einem Text Als Maß für die Laufzeit nehmen wir die Anzahl der ausgeführten Tests der inneren Schleife. Der schlimmste Fall tritt ein, wenn für jeden Wert s die Zeichenkette p bis zum letzten Zeichen mit t verglichen werden muss. Beispiel: t = "aaaaaaaaaaaaaaaaab", p = "aaab". Die Laufzeit liegt in O((n m)m) = O(nm). Gesucht ist ein effizienterer Algorithmus. 9.5 Dynamische Programmierung 9-64

Beispiel: Suche in einem Text s = 4, q = 5: s = s + 2: t: bacbababaabcbab p: ababaca t: bacbababaabcbab p: ababaca Die nächste möglicherweise erfolgreiche Verschiebung ist s = s +(q d[q]). 9.5 Dynamische Programmierung 9-65

Beispiel: Suche in einem Text Es seien die Musterzeichen p[1..q] gegeben, die mit den Textzeichen t[s + 1..s + q] übereinstimmen. Wie groß ist die geringste Verschiebung s > s für die mit s + k = s + q gilt? p[1..k] = t[s + 1..s + k] 9.5 Dynamische Programmierung 9-66

Beispiel: Suche in einem Text Die Präfixfunktion für das Musterababababca: i 1 2 3 4 5 6 7 8 9 10 p[i] a b a b a b a b c a d[i] 0 0 1 2 3 4 5 6 0 1 9.5 Dynamische Programmierung 9-67

Beispiel: Suche in einem Text proc Berechnung der Präfixfunktion d begin d(1) 0; k 0; for q 2 to m do while k > 0 p[k+1] p[q]; do k d[k]; od; if p[k+1] = p[q] then k k+1; fi; d[q] k; od; return d; end 9.5 Dynamische Programmierung 9-68

Beispiel: Suche in einem Text Der Knuth-Morris-Pratt-Algorithmus: proc kmp(t, p): begin berechne d; q 0; for i 1 to n do while q > 0 p[q+1] t[i] do q d[q]; od; if p[q+1] = t[i] then q q+1; fi; if q = m then print(i-m); q d[q]; fi; od; end Die Laufzeit dieses Algorithmus liegt in O(n + m). 9.5 Dynamische Programmierung 9-69

Weitere Algorithmenmuster Zufallsgesteuerte Algorithmen Verteilte und parallele Algorithmen Lokale Suche Amortisierte Analyse Approximative Algorithmen Genetische Algorithmen Schwarmbasierte Algorithmen und Koloniealgorithmen... 9.5 Dynamische Programmierung 9-70

Weitere Gebiete Mathematische Algorithmen Geometrische Algorithmen Algorithmen für Texte Lineare Programmierung... Große Bedeutung für die Theorie der Algorithmen besitzt die Komplexitätstheorie. Hierzu zählen zum Beispiel die Untersuchung von Komplexitätsklassen wie P und NP und die so genannte NP-Vollständigkeit. Die Komplexitätstheorie wird in der Vorlesung Theoretische Informatik II behandelt. 9.5 Dynamische Programmierung 9-71