Kapitel 13: Entwurf von Algorithmen Einführung in die Informatik Wintersemester 2007/08 Prof. Bernhard Jung Übersicht Einleitung: Entwurf von Algorithmen Entwurfsprinzip: Schrittweise Verfeinerung Entwurfsprinzip: Einsatz von Algorithmenmustern Algorithmenmuster: Greedy Beispiel: optimales Kommunikationsnetz Exkurs: Listen (und Matrizen) in Python Algorithmenmuster: Backtracking Beispiel: Kürzeste Rundreise Beispiel: 8-Damen-Problem Literatur G. Saake & K.-U. Sattler. Algorithmen und Datenstrukturen. dPunkt Lehrbuch. 2006. Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 1 Einleitung: Entwurf von Algorithmen Programmieren ist konstruktive und kreative Tätigkeit Trotzdem existieren verschiedene typische Muster für Algorithmen, die in Praxis oft Anwendung finden Erstellung eines optimalen Algorithmus i.a. nicht automatisierbar aber fallspezifisch anzupassen sind schon bekanntes Muster: divide-and-conquer z.B. Algorithmenmuster für Optimierungsprobleme, u.a. greedy-Algorithmen Backtracking lokales Optimum globales Optimum effizient, liefern lokales Optimum nicht so effizient, liefert globales Optimum Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Entwurfsprinzip: Schrittweise Verfeinerung Beim Entwurf von Algorithmen beschreibe Grobverfahren in abstraktem Pseudocode Verfeinere Pseudocode und ersetze diesen nach und nach durch detaillierten Pseudocode dabei Zerlegung in Teilaufgaben wiederhole … bis letztlich Algorithmus in Programmiersprachencode beschrieben ist Top-Down-Entwurf Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 2 Entwurfsprinzip: Schrittweise Verfeinerung Beispiel Grobverfahren Einzelschritte verfeinern 1. Wasser kochen 2. Nescafe in die Tasse 3. Wasser in die Tasse 1.1 Kessel mit Wasser füllen 1.2 Auf die Herdplatte stellen 1.3 Herdplatte anstellen 1.4 Warten bis Wasser kocht 1.5 Herdplatte abstellen 2.1 Glas öffnen 2.2 Menge Kaffeepulver auf den Löffel laden 2.3 Löffel in die Tasse leeren 3.1 Kessel von der Herdplatte nehmen 3.2 Tasse mit Wasser füllen Weiter verfeinern … Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Entwurfsprinzip: Einsatz von Algorithmenmustern Das (generische) Lösungsverfahren wird an einem möglichst einfachen Vertreter der Problemklasse vorgeführt und dokumentiert Der Entwerfer versteht die Problemlösungsstrategie und überträgt diese auf sein Programm Konzept moderner Softwareentwicklung: "Design Patterns" Eine Bibliothek von Mustern ("Design Patterns", "best-practice"-Strategien) wird genutzt, um einen abstrakten Programmrahmen zu generieren Die freien Stellen dieses Programmrahmens werden dann problemspezifisch ausgefüllt In modernen Programmiersprachen wird dies z.Tl. schon weitergehend unterstützt durch parametrisierte Algorithmen und – in objektorientierter Programmierung – Vererbung mit Überschreiben Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 3 Algorithmenmuster Greedy greedy = gierig Prinzip gieriger Algorithmen: Versuche in jedem Teilschritt so viel wie möglich zu erreichen Beispiel Wechselgeld (bis 1 Euro) herausgeben, mit minimaler Anzahl von Münzen à 50, 20, 10, 5, 2, und 1-Cent Greedy-Strategie: Nimm jeweils die größte Münze unter Zielwert, und ziehe diese von Zielwert ab Bsp: 78 Cent -> 50 + 20 + 5 + 2 +1, d.h. 5 Münzen Greedy berechnet bei diesem Problem immer die optimale Lösung … … wäre anders wenn nur Münzen à 11, 5, und 1 Cent zu Verfügung stünden z.B. 15 Cent zu wechseln Æ Greedy: 15 = 11 + 4 • 1, d.h. 5 Münzen Æ optimal aber: 15 = 3 • 5, d.h. 3 Münzen Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Greedy: Optimales Kommunikationsnetz 6 1 3 7 10 4 5 0 1 2 4 8 2 9 3 Aufgabe: möglichst billige Vernetzung von vorgegeben Stationen eines Kommunikationsnetzes herstellen Jeder Knotenpunkt soll mit jedem anderen verbunden sein, ggf. auch auf Umweg über andere Knotenpunkte Bekannt sind Kosten für direkte Verbindungen Gesucht ist Teilmenge aller direkten Verbindungen, so dass alle Knoten untereinander verbunden sind die Gesamtkosten minimal sind (Optimierungsproblem!) Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 4 Greedy: Optimales Kommunikationsnetz 6 1 3 7 10 4 9 5 0 2 1 4 8 3 2 1. Schritt: Markiere beliebigen Knoten 2. Schritt: Betrachte alle Kanten von markierten zu unmarkierten Knoten … im Beispiel 0 0-1 (Kosten 7), 0-2 (Kosten 1), 0-3 (Kosten 10), 0-4 (Kosten 5) … und markiere Kante (und Zielknoten) mit geringsten Kosten Kante 0-2, Knoten 2 Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Greedy: Optimales Kommunikationsnetz 1 6 3 7 10 4 5 0 1 2 4 8 2 9 3 3. Schritt: Betrachte alle Kanten von markierten zu unmarkierten Knoten … 0-1 (Kosten 7), 0-3 (Kosten 10), 0-4 (Kosten 5) 2-1 (Kosten 2), 2-3 (Kosten 8), 2-4 (Kosten 3) … und markiere Kante (und Zielknoten) mit geringsten Kosten Prof. B. Jung Æ Kante 2-1, Knoten 1 Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 5 Greedy: Optimales Kommunikationsnetz 1 6 3 7 10 4 0 2 1 4 8 3 2 9 5 4. Schritt: Betrachte alle Kanten von markierten zu unmarkierten Knoten … 0-3 (Kosten 10), 0-4 (Kosten 5) 1-3 (Kosten 6), 1-4 (Kosten 4) 2-3 (Kosten 8), 2-4 (Kosten 3) … und markiere Kante (und Zielknoten) mit geringsten Kosten Æ Kante 2-4, Knoten 4 Prof. B. Jung TU Bergakademie Freiberg Einführung in die Informatik, WS 2007/08 Greedy: Optimales Kommunikationsnetz 1 0 6 3 7 10 4 5 0 1 2 4 3 3 5. Schritt: Betrachte alle Kanten von markierten zu unmarkierten Knoten … 0-3 (Kosten 10), 1-3 (Kosten 6), 2-3 (Kosten 8), 4-3 (Kosten 9) … und markiere Kante (und Zielknoten) mit geringsten Kosten 1 4 8 2 2 9 Æ Kante 1-3, Knoten 3 Bem: markierte Knoten bilden jetzt einen "minimalen aufspannenden Baum" (minimal spanning tree) des Graphen Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 6 Greedy: Optimales Kommunikationsnetz Erste Variante des Algorithmus 1 [ Teilbaum R besteht anfangs aus einem beliebigem Knoten ] while [ R noch nicht aufgespannt ]: [ suche billigste von R ausgehende Kante ] [ füge diese zu R hinzu ] 7 10 0 1 6 3 4 9 5 2 2 8 4 3 Verfeinerungen while-Schleife durch for-Schleife ersetzen da klar, dass Schleife genau (n-1)-mal durchlaufen wird: anfangs 1 Knoten in Baum, in jedem Durchgang kommt ein Knoten hinzu Suche der billigsten Kante z.B. geschachtelte Schleife: für alle Knoten a für alle Knoten b teste ob Kante (a,b) von R ausgeht und billiger ist bisher betrachtete Kanten Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Greedy: Optimales Kommunikationsnetz Verfeinerte Variante des Algorithmus 1 6 [ R = beliebiger Knoten von P ] 3 7 for i = 1 … (n-1): 10 4 9 # suche billigste von R ausgehende Kante 5 0 4 minKosten = MAXKOSTEN 2 8 1 3 2 for j = 0 … (n-1): for k = (j+1) … (n-1): if Kante (j,k) geht von R aus and Kosten(j,k) < minKosten: minKosten = Kosten(j,k) billigsteKante = (j,k) [ füge billigsteKante zu R hinzu ] Weitere Verfeinerungen Datenstruktur zur Darstellung von Baum festlegen am besten wäre es, eine eigene Klasse zu definieren (objektorientierte Prog.) als Behelf auch z.B. getrennte Speicherung von: Prof. B. Jung treeNodes: Liste der Knoten des Baums treeEdges: Liste der Kanten des Baums Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 7 1 7 Greedy: Optimales Kommunikationsnetz 0 Python-Code 6 10 3 4 9 4 5 2 2 1 8 def minimalSpanningTree(): treeNodes = [0] # initially only node 0 in tree treeEdges = [] # ... and no edges 3 Lokale Funktion: Nur von äußerer Funktion aufrufbar. Hat Zugriff auf lokale Variablen der äußeren Funktion. def fromTreeToOutside(a,b): if a in treeNodes and b not in treeNodes: return True if b in treeNodes and a not in treeNodes: return True return False for i in range(1,SIZE): # find cheapest edge leaving the current tree cheapestCost = MAXCOST for j in range(SIZE): for k in range(j+1,SIZE): if fromTreeToOutside(j,k) and getCost(j,k) < cheapestCost: cheapestCost = getCost(j,k) cheapestEdge = (j,k) # expand the current tree treeEdges.append( cheapestEdge ) a,b = cheapestEdge hier nicht definiert: if a in treeNodes: treeNodes.append(b) getCost(j,k) – Kosten für Kante (j,k) else: treeNodes.append(a) SIZE – Anzahl Knoten Prof. B. Jung TU Bergakademie Freiberg Einführung in die Informatik, WS 2007/08 return treeEdges MAXCOST - z.B.1000000 Datenstruktur für Graphen: Adjazenzmatrix (Nachbarschaftsmatrix) Graph mit n Knoten wird durch n × n Matrix m repräsentiert ungerichtete Graphen ohne Kantengewichte: ungerichtete Graphen mit Kantengewichten: bei ungerichteten Graphen ist Adjazenzmatrix symmetrisch Kante (i,j) durch Eintag 1 in m[i][j] und m[j][i] repräsentiert m[i][j] und m[j][i] enthalten Gewicht der Kante (i,j) bei gerichteten Graphen i.a. unsymmetrisch 0 Prof. B. Jung 0 1 2 3 4 0 7 1 10 5 1 7 0 2 6 4 2 1 2 0 8 3 3 10 6 8 0 9 4 5 4 3 9 0 1 7 6 4 5 0 Einführung in die Informatik, WS 2007/08 3 10 1 2 4 8 2 9 3 TU Bergakademie Freiberg 8 Darstellung von Matrizen in Python Matrix darstellbar als Liste von Listen: jede enthaltene Liste entspricht einer Zeile der Matrix >>> >>> [1, >>> 1 2 m = [ [1,2], [3,4] ] print m[0], m[1] 2] [3, 4] print m[0][0], m[0][1], m[1][0], m[1][1] 3 4 Potentielle Fehlerquelle beim Erzeugen von Matrizen: Enthaltene Listen müssen verschiedene (d.h. evtl. gleiche, aber nicht dieselben) Objekte sein Falls alle Teillisten dasselbe Objekt: Veränderungen an einer Teilliste … … wirken sich auf alle anderen aus! nicht gewollt bei Matrizen! Prof. B. Jung Einführung in die Informatik, WS 2007/08 ⎡1 2⎤ ⎢3 4⎥ ⎣ ⎦ >>> l = [1,2] >>> m = [l,l] >>> print m [[1, 2], [1, 2]] >>> l[0] = 0 >>> print l [0, 2] >>> print m [[0, 2], [0, 2]] TU Bergakademie Freiberg Exkurs: Listen in Python Erzeugung von Listen >>> l1 = [2,5,9] Explizites Aufzählen (in eckigen Klammern) Zahlenfolgen mit range() "Multiplikation" von Listen mit Zahl List comprehension Prof. B. Jung Generierung von Liste aus Elementen einer andern Liste ähnlich Def. math. Mengen, z.B. : { i2 | i = 0..4} auch mit Test: nur Elemente, die Bedingung genügen, werden in neue Liste aufgenommen >>> l2 = range(5) >>> l2 [0, 1, 2, 3, 4] >>> l3 = [0] * 5 >>> l3 [0, 0, 0, 0, 0] >>> l4 = [ i**2 for i in range(5) ] >>> l4 [0, 1, 4, 9, 16] >>> l5 = [ i for i in range(50) if i%7 == 0 ] >>> l5 [0, 7, 14, 21, 28, 35, 42, 49] Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 9 Exkurs: Listen in Python Slice-Operator >>> l = [1,2,3,4] l[min:max] Teilliste der Elemente von Index min (einschließlich) bis max (ausschließlich) l[min:] Teilliste ab Index min (einschließlich) l[:max] Teilliste bis Index max (ausschließlich) l[:] flache Kopie der Liste (Listenelemente werden nicht mit kopiert) Prof. B. Jung Alias Liste wird kopiert, Elemente aber nicht l2 = l[:] Tiefe Kopie anderer Name für gleiche Liste l1 = l Flache Kopie auch Listenelemente werden kopiert Modul copy l3 = deepcopy(l) Unterschiedl. Verhalten bei Hinzufügen oder Entfernen von Elementen der Original-Liste Ändern von Elementen der Originalliste Prof. B. Jung >>> l[1:] [2, 3, 4] >>> l[:2] [1, 2] >>> l[:] [1,2,3,4] Einführung in die Informatik, WS 2007/08 Exkurs: Listen in Python Flache und tiefe Kopien >>> l[1:4] [2, 3, 4] >>> >>> >>> >>> >>> TU Bergakademie Freiberg l = [ [1,2], [3,4] ] l1 = l # Alias für l l2 = l[:] # flache Kopie from copy import deepcopy l3 = deepcopy(l) >>> l.append([5,6]) >>> l1 [[1, 2], [3, 4], [5, 6]] >>> l2 [[1, 2], [3, 4]] >>> l3 [[1, 2], [3, 4]] >>> l[0][0] = 100 >>> l1 [[100, 2], [3, 4], [5, 6]] >>> l2 [[100, 2], [3, 4]] >>> l3 [[1, 2], [3, 4]] Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 10 Erzeugen von n×n Matrizen in Python Falsch: Richtig: >>> def makeMatrixBuggy(n): return [ [0]*n ] * n >>> def makeMatrix(n): return [ [0]*n for i in range(n) ] >>> m1 = makeMatrixBuggy(2) >>> m1 [[0, 0], [0, 0]] >>> m1[0] [0, 0] >>> m1[0][0] 0 >>> m1[0][0] = 1 >>> m1 [[1, 0], [1, 0]] >>> m2 = makeMatrix(2) >>> m2 [[0, 0], [0, 0]] >>> m2[0] [0, 0] >>> m2[0][0] 0 >>> m2[0][0] = 1 >>> m2 [[1, 0], [0, 0]] erzeugt Liste, deren n Elemente jeweils dieselbe Liste sind erzeugt Liste, deren n Elemente zwar gleich, aber nicht dieselbe Liste sind Kopieren so erzeugter Matrizen i.d.R. mit deepcopy()! Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Greedy: Optimales Kommunikationsnetz Analyse [ R = beliebiger Knoten von P ] for i = 1 … (n-1): # suche billigste von R ausgehende Kante minKosten = MAXKOSTEN for j = 0 … (n-1): for k = (j+1) … (n-1): if Kante (j,k) geht von R aus and Kosten(j,k) < minCosts: minKosten = Kosten(j,k) billigsteKante = (j,k) [ füge billigsteKante zu R hinzu ] Laufzeit: proportional zu n3 Begründung: drei geschachtelte for-Schleifen, die jeweils ungefähr n-mal durchlaufen werden Verbesserung möglich, so dass Laufzeit proportional zu n2 Algorithmus findet immer (global) optimale Lösung Algorithmus von Prim i.a. finden greedy-Algorithmen nur lokales Optimum bei betrachtetem Problem gilt aber: lokales Optimum = globales Optimum Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 11 Wo Greedy fehlschlägt: Kürzeste Rundreise Gesucht: Rundreise durch alle Knoten, so dass jeder Knoten genau einmal besucht wird Rundreise am selben Knoten beginnt und endet Kosten minimal sind 1 7 6 3 10 4 5 0 1 2 4 8 2 9 3 Greedy-Strategie: beginne bei beliebigem Knoten gehe vom aktuellen Knoten zu dem unbesuchten Nachbarknoten mit geringsten Pfadkosten wiederhole, bis alle Knoten besucht gehe zurück zum Ausgangsknoten liefert im Beispiel z.B. Rundreise: 0 Æ 2 Æ 1 Æ 4 Æ 3 Æ 0 (Kosten 26) kürzere Rundreise z.B.: 0 Æ 2 Æ 1 Æ 3 Æ 4 Æ 0 (Kosten 23) optimale Lösung kann z.B. mit Backtracking gefunden werden … Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Algorithmenmuster Backtracking Backtracking ist wichtiges Algorithmenmuster für Such- und Optimierungsprobleme Backtracking realisiert eine allgemeine, systematische Suchtechnik Typische Anwendungsgebiete: Planungsprobleme (Routen- u. Tourenplanung), Konfigurierungen (z.B. VLSI-Layout), Optimierungen, Spielprogramme (Schach, Dame, …), … Lösungsraum kann vollständig betrachtet werden Vorgehen nach Versuch-und-Irrtum-Prinzip (trial-and-error) versucht, schrittweise eine Teillösung zu Lösung auszubauen dabei systematisches Ausprobieren alternativer Lösungswege Beispiel: Maus und Käse durch "Backtracking" zu früheren Entscheidungspunkten Backtracking wird i.d.R. durch Rekursion implementiert Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 12 Backtracking: Finden in einem Labyrinth Jeder Weg (Konfiguration) kann mit einem Folgeschritt zu einem verlängerten Weg (Folgekonfiguration) erweitert werden Für jeden Weg (Konfiguration) ist entscheidbar, ob er eine Lösung ("Käse gefunden") oder keine Lösung ("Sackgasse" oder "Käse nicht gefunden") ist In der Sackgasse sucht die Maus keinen Folgeweg, sondern kehrt um zur vorherigen Konfiguration (Backtracking) zurück und sucht dort weiter Das wiederholte Absuchen desselben Folgeweges muss verhindert werden Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Prinzip des Backtracking Prolemlösungsverfahren betrachtet zunächst Start-Konfiguration Für jede Konfiguration (Zustand) können i.a. mehrere Nachfolge-Konfigurationen (Zustände) erzeugt werden Für jede Konfiguration (Zustand) ist entscheidbar, ob es sich um eine Lösung handelt i.a. wird jede dieser Nachfolgekonfigurationen überprüft Backtracking-Muster: procedure backtrack(K: Konfiguration): if [K ist Lösung]: gib K aus else: for each [ direkte Erweiterung K' von K ]: backtrack( K' ) Aufruf von backtrack() mit Start-Konfiguration Backtracking terminiert garantiert, wenn nur endlich viele Konfigurationen bestehen jede Konfiguration höchstens einmal gestestet wird Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 13 Backtracking: Kürzeste Rundreise, bzw. Travelling salesman problem (tsp) 1 7 3 10 0 Backtracking-Muster: 1 procedure backtrack(K: Konfiguration): if [K ist Lösung]: gib K aus else: for each [ direkte Erweiterung K' von K ]: backtrack( K' ) 6 4 5 2 4 8 2 9 3 Traveling Salesman Problem: finde kürzeste Rundreise durch n Städte (jede Stadt darf nur einmal besucht werden, außer Start- = Zielort) Anpassung des Musters für Traveling Salesman Problem: procedure tsp( trip ): if [trip besucht alle Städte]: [erweitere trip um Heimreise zum Ausgangsort] [gib trip und Kosten dafür aus] else: for each [bislang unbesuchte Stadt s]: [ trip' = trip erweitert um Reise nach s] Prof. B. Jung TU Bergakademie Freiberg Einführung in die Informatik, WS 2007/08 tsp( trip' ) Backtracking: Travelling salesman problem (tsp) Umsetzung in Python def tsp(trip): # solution found if len(trip) == SIZE: roundtrip = trip + [ trip[0] ] # go home print "Roundtrip", roundtrip, "costs", getPathCost(roundtrip) # expand trip, try all unvisited cities else: for i in range(SIZE): if i not in trip: tsp( trip + [i]) return # Hauptprogramm if __name__ == '__main__': tsp([0]) # start trip in city 0 Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 14 Backtracking: Travelling salesman, Testlauf u. Analyse Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Roundtrip Prof. B. Jung [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, [0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 2, 2, 3, 3, 4, 4, 1, 1, 3, 3, 4, 4, 1, 1, 2, 2, 4, 4, 1, 1, 2, 2, 3, 3, 3, 4, 2, 4, 2, 3, 3, 4, 1, 4, 1, 3, 2, 4, 1, 4, 1, 2, 2, 3, 1, 3, 1, 2, 4, 0] costs 31 3, 0] costs 30 4, 0] costs 28 2, 0] costs 25 3, 0] costs 31 2, 0] costs 29 4, 0] costs 23 3, 0] costs 26 4, 0] costs 24 1, 0] costs 29 3, 0] costs 23 1, 0] costs 25 4, 0] costs 25 2, 0] costs 23 4, 0] costs 29 1, 0] costs 31 2, 0] costs 26 Æ 1, 0] costs 30 3, 0] costs 29 Æ 2, 0] costs 24 3, 0] costs 25 Æ 1, 0] costs 28 2, 0] costs 23 1, 0] costs 31 WS 2007/08 Einführung in die Informatik, 1 7 0 6 10 3 4 9 4 5 2 2 1 8 3 Es werden 24 Rundreisen gefunden minimale Kosten (per Inspektion der Ergebnisse): 23 jede Rundreise entspricht einer Permutation der Zahlen 1..4 Für n Objekte gibt es n! Permutationen 24 = 4! Laufzeit des Algorithmus proportional zu (n-1)! damit nur für kleine n effektiv ausführbar (mit Code-Optimierungen z.B. n = 40 – 60) TU Bergakademie Freiberg Backtracking: Das Achtdamen-Problem Das Damenproblem (8−Queens−Problem): Man finde eine Stellung für acht Damen auf einem Schachbrett, so dass keine zwei Damen sich gegenseitig schlagen können In Informatik auch auf n-Damenproblem erweitert (Platzierung von n Damen auf n × n Brett) Zwei Lösungen des Achtdamen-Problems Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 15 Backtracking: Das Achtdamen-Problem Vorgangsweise von oben nach unten, je1 Dame in Zeile setzen (gleiche Zeile ausgeschlossen) dabei werden jeweils alle unbedrohten Felder der Zeile ausprobiert Lösung gefunden, wenn 8. Dame auf unbedrohtem Feld platziert wird def PLATZIERE(i): # platziere Dame in i-ter Zeile for h = 1 .. 8: # alle Spalten h ausprobieren if [Feld in Zeile i, Spalte h nicht bedroht]: [setze Dame auf dieses Feld (i,h)] if [Brett voll]: # d.h. i == 8 [Gib Konfiguration aus] else: PLATZIERE(i+1) Prof. B. Jung TU Bergakademie Freiberg Einführung in die Informatik, WS 2007/08 Konfigurations- bzw. Suchraum für 4-Damenproblem D D D D D D … D D D … D … [symmetrisch zu Teilbaum links] D D D D D D def PLATZIERE(i): # platziere Dame in i-ter Zeile for h = 1 .. 8: # alle Spalten h ausprobieren if [Feld in Zeile i, Spalte h nicht bedroht]: [setze Dame auf dieses Feld (i,h)] D if [Brett voll]: # d.h. i == 8 [Gib Konfiguration aus] else: Prof. B. Jung Einführung in die Informatik, WS 2007/08 PLATZIERE(i+1) D D Lösung! D TU Bergakademie Freiberg 16 Backtracking: Das Achtdamen-Problem Gegenseitige Bedrohung von 2 Damen, falls: • gleiche Zeile (ausgeschlossen durch Algorithmus) • gleiche Spalte • gleiche Diagonale Prof. B. Jung Einführung in die Informatik, WS 2007/08 def bedroht( (x1,y1), (x2,y2) ): if x1 == x2: return True if y1 == y2: return True if x1+y1 == x2+y2: return True if x1-y1 == x2-y2: return True return False # # # # gleiche gleiche gleiche gleiche TU Bergakademie Freiberg Spalte Reihe Diagonale (aufwaerts) Diagonale (abwaerts) def istBedroht( (x,y), andere): # ist Feld bedroht? for xi,yi in andere: if bedroht( (xi,yi), (x,y) ): return True return False def platziereNaechsteDame( bisher ): zeile = len(bisher) + 1 for spalte in range(1,9): if not istBedroht( (zeile,spalte), bisher): jetzt = bisher + [ (zeile,spalte) ] if len(jetzt)==8: print "Loesung", jetzt else: platziereNaechsteDame(jetzt) # Hauptprogramm if __name__ == "__main__": platziereNaechsteDame([]) Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 17 Backtracking: Das Achtdamen-Problem Anzahl der Lösungen steigt mit der Brettgröße sehr stark an! Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg Backtracking: Sudoku Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 18 Zusammenfassung Betrachtete Algorithmenmuster greedy backtracking berechnet lokales Optimum polynomiale Zeitkomplexität berechnet globales Optimum Zeitkomplexität entsprechend Größe des (problemspezifischen) Konfigurationsraums, z.B. exponentiell (proportional 2n) oder auch – noch schlimmer – proportional zu n! vollständiges Durchsuchen des Konfigurationsraums per Backtracking daher nur für kleinere Probleminstanzen praktisch durchführbar bei großen Probleminstanzen muss man sich evtl. mit lokalen Optima begnügen, z.B. per Greedy-Algorithmus berechnet Weitere Algorithmenmuster divide-and-conquer (vgl. Kapitel 11: Suche) dynamisches Programmieren (Literatur) … Prof. B. Jung Einführung in die Informatik, WS 2007/08 TU Bergakademie Freiberg 19