Westfälische Wilhelms-Universität Münster Ausarbeitung Kombinatorische Suche im Rahmen des Seminars Parallele Programmierung Christian Hermanns Themensteller: Prof. Dr. Herbert Kuchen Betreuer: Prof. Dr. Herbert Kuchen Institut für Wirtschaftsinformatik Praktische Informatik in der Wirtschaft Inhaltsverzeichnis 1 Einleitung...............................................................................................................1 2 Branch-and-Bound .................................................................................................1 2.1 Traveling Salesman Problem..........................................................................3 2.2 Parallele Branch-and-Bound Algorithmen......................................................7 2.2.1 2.2.2 2.2.3 3 Multiprozessor Algorithmen.......................................................................7 Multicomputer Algorithmen.......................................................................8 Anomalien beim parallelen Branch-and-Bound ........................................10 Alpha-Beta Suche.................................................................................................14 3.1 3.1.1 3.2 3.2.1 3.2.2 3.2.3 Sequenzielle Alpha-Beta Suche....................................................................14 Erweiterungen der Alpha-Beta Suche.......................................................18 Parallele Alpha-Beta Suche..........................................................................18 Parallelisierung der Zugerzeugung und der Positionsbewertung ...............18 Parallele Aspirationssuche........................................................................19 Parallele Teilbaumsuche...........................................................................19 4 Zusammenfassung ................................................................................................21 5 Literaturverzeichnis ..............................................................................................22 II Kapitel 1: Einleitung 1 Einleitung Die kombinatorische Suche ist ein Verfahren zur Lösung von Problemen, deren Lösungsparameter nur ganzzahlige Werte annehmen können. Die untersuchten Probleme lassen sich unterteilen in Entscheidungsprobleme, deren Lösungen sämtliche Nebenbedingungen des Problems erfüllen, und Optimierungsprobleme, deren Lösungen zusätzlich eine gegebene Zielfunktion minimieren. Die in dieser Arbeit behandelten kombinatorischen Optimierungsprobleme treten in vielen Anwendungsgebieten, wie z.B. der künstlichen Intelligenz, dem Operations Research oder der Datenbanktechnik, auf. Aufgrund der hohen Komplexität dieser Probleme ist die Suche nach einer Lösung oft sehr aufwendig und zeitintensiv. Es stellt sich die Frage, wie gut sich kombinatorische Suchalgorithmen parallelisieren lassen, um deren Ausführung zu beschleunigen. Im Rahmen dieser Ausarbeitung sollen einige kombinatorische Suchalgorithmen vorgestellt und deren Parallelisierungsmöglichkeiten aufgezeigt werden. Das zweite Kapitel beschäftigt sich mit dem Branch-and-Bound Verfahren. Nach einer allgemeinen Beschreibung dieses Verfahrens wird ein Algorithmus zur Lösung des Traveling Salesman Problems vorgestellt. Im Anschluss daran wird gezeigt, wie sich das Branch-and-Bound Verfahren parallelisieren lässt. Im dritten Kapitel wird zunächst der sequenzielle Alpha-Beta Algorithmus vorgestellt. Anschließend werden auch hier Möglichkeiten der Parallelisierung erläutert. Die Ausarbeitung endet mit einer Zusammenfassung im vierten Kapitel. 2 Branch-and-Bound Das Branch-and-Bound Verfahren ist eine Variante des Backtrackings. Während beim Backtracking eine erschöpfende Suche durchgeführt wird, um alle Lösungsmöglichkeiten auf Optimalität zu prüfen, kann das Suchfeld durch das Branchand-Bound Verfahren eingeschränkt werden. Teillösungen, die zu keiner optimalen Lösung führen können, werden vernachlässigt. Ein Branch-and-Bound Algorithmus versucht ein gegebenes Problem bei gleichzeitiger Minimierung einer Ziel- oder Kostenfunktion zu lösen. Falls ein Problem nicht 1 Kapitel 2: Branch-and-Bound unmittelbar lösbar ist, wird das Problem in ein oder mehrere Teilprobleme zerlegt. Diese werden durch Einschränkung des übergeordneten Problems gebildet. Die Teilprobleme werden so lange zerlegt, bis sie eine zulässige Lösung darstellen oder gesichert ist, dass sie zu keiner optimalen Lösung führen. Dieser Vorgang wird als Branching bezeichnet. Um zu erkennen, dass ein Teilproblem nicht zu einer optimalen Lösung führen kann, wird zu jedem Teilproblem eine untere Schranke berechnet. Diese gibt an, wie hoch die Kosten für jede mögliche Lösung des Teilproblems mindestens sind. Ist bereits eine Lösung gefunden worden, brauchen nur noch die Teilprobleme untersucht werden, deren untere Schranken kleiner sind als die Kosten der besten bereits gefundenen Lösung. Da somit einige der Teilprobleme nicht weiter untersucht werden, wird diese Technik auch als Bounding (Begrenzen) bezeichnet. Der Zerlegungsprozess des Problems lässt sich durch einen Suchbaum darstellen. Die Knoten des Baumes entsprechen den Teilproblemen und die Kanten beschreiben die Zerlegungsschritte von der Wurzel zu den Blättern. Die Wurzel repräsentiert das Gesamtproblem und die Blätter entsprechen gelösten oder nicht weiter untersuchten Teilproblemen. Während der Zerlegung enthält der Suchbaum meist mehrere noch nicht untersuchte Teilprobleme. Das nächste zu untersuchende Teilproblem muss durch eine Suchstrategie bestimmt werden. Diese legt die Reihenfolge fest, in der der Baum durchlaufen wird. Mögliche Suchstrategien sind die Tiefensuche (depth-first), die Breitensuche (breadth-first) und die Bestensuche (best-first). Die vier wesentlichen Bestandteile eines Branch-and-Bound Algorithmus sind: • Eine Regel zur Generierung von Teilproblemen, • eine Auswahlregel für nicht untersuchte Teilprobleme, • eine Regel zur Eliminierung uninteressanter Teilprobleme und • eine Abbruchbedingung. 2 Kapitel 2: Branch-and-Bound 2.1 Traveling Salesman Problem Das Traveling Salesman Problem (TSP) lässt sich folgendermaßen beschreiben: Eine Menge von Städten soll durch eine möglichst kostengünstige, zyklische Tour miteinander verbunden werden, wobei keine der Städte mehr als einmal besucht werden darf. Zur Veranschaulichung kann das Problem durch einen zusammenhängenden, gewichteten und ungerichteten Graphen (vgl. Abbildung 1) dargestellt werden. Die gesuchte Tour ist der kürzeste, zyklische Pfad, der alle Knoten des Graphen genau einmal enthält. Abbildung 1: Ein gewichteter Graph mit TSP-Tour [Qu94, S.344] Im Folgenden soll nun der Branch-and-Bound Algorithmus von Little (vgl. [Li63]) zur Lösung des TSP vorgestellt werden (vgl. Abbildung 2). Der Algorithmus arbeitet mit Hilfe einer Liste, die die bisher noch nicht untersuchten Teilprobleme enthält. Zu Beginn wird das Ausgangsproblem, welches der Menge aller möglichen Routen entspricht, in die Liste eingefügt. Da der Algorithmus eine best-first Suche durchführt, entnimmt er jeweils das Teilproblem mit der niedrigsten unteren Schranke aus der Liste und prüft, ob dieses direkt lösbar ist. Ein Teilproblem ist direkt lösbar, wenn es nur eine mögliche Route zulässt. In diesem Fall ist die optimale Route gefunden und der Algorithmus terminiert. Ist ein Problem nicht direkt lösbar, wird es mit Hilfe einer Kante in zwei Teilprobleme unterteilt. Das eine Teilproblem entspricht der Menge aller Routen, die die ausgewählte Kante enthalten, das andere Teilproblem entspricht der Menge aller Routen, die die ausgewählte Kante nicht enthalten. Es wird immer diejenige Kante gewählt, deren Ausschluss die untere Schranke am stärksten anhebt. Mit dieser Heuristik wird versucht, die untere Schranke möglichst schnell zu 3 Kapitel 2: Branch-and-Bound erhöhen. Der Suchbaum soll hierdurch so stark wie möglich beschränkt werden, um nur wenige Knoten untersuchen zu müssen. public static Tour tsp( WeightMatrix matrix ) { // Erzeuge Knoten für das Ausgangaproblem TspNode node = new TspNode( matrix ); // Finde untere Schranke durch Matrizenreduktion node.findLowerBound(); // Knoten in leeren Suchbaum einfügen TspTree tree = new TspTree( node ); while (true) { // Wähle nicht untersuchten Knoten mit kleinster unterer // Schranke node = tree.removeSmallestLowerBoundNode(); // Ende, falls Tour gefunden if ( node.representsTour() ) return node.getTour(); // Finde Kante, deren Ausschluß die untere Schranke am // meißten erhöht. Edge e = node.getMostIncreasingEdge(); for (int i=0; i<2; i++) { // Kinder mit Einschränkungen werden erzeugt. // Mit i=0 für Einschluss und i = 1 für Ausschluss der // Kante e Node child = node.createChild( i, e ); child.findLowerBound(); tree.addNode( child ); } } } Abbildung 2: Java-Implementierung eins sequenziellen Branch-and-Bound Algorithmus von Little zur Lösung des Traveling Salesman Problems. Die Berechnung der unteren Schranke für die Kosten einer Route wird mit Hilfe der Matrixreduktion durchgeführt. Die Kosten für die Kanten lassen sich in einer Kostenmatrix darstellen, in der jede Zeile und jede Spalte einem Knoten des Graphen zugeordnet ist. Die Zeile der Matrix gibt den Startknoten, die Spalte den Zielknoten einer Kante an. In einer Zeile der Kostenmatrix stehen die Kosten für alle Kanten, die von einem bestimmten Knoten weg führen. Für jeden Knoten wird nun die wegführende Kante mit den geringsten Kosten ermittelt, d.h. der kleinste Wert der Zeile wird gewählt. Alle Einträge in der Zeile der Matrix werden um den Wert dieser Kante reduziert und anschließend wird die untere Schranke um diesen Wert erhöht. Genauso wird anschließend mit den Spalten der Matrix verfahren. Eine Spalte beinhaltet die Kosten aller zu einem bestimmten Knoten hinführenden Kanten. Die entstehende Matrix enthält nun nur noch die Zusatzkosten für die Kanten. Die Länge einer Route 4 Kapitel 2: Branch-and-Bound berechnet sich also aus der Höhe der unteren Schranke und den zusätzlichen Kosten der gewählten Kanten. Das Verfahren beruht auf der Tatsache, dass in der gesuchten Route jeder Knoten genau eine hinführende und eine wegführende Kante besitzt. Für alle Knoten wurde zur Ermittlung der unteren Schranke jeweils die kürzeste dieser beiden Kantenarten gewählt. Ein Beispiel für die Matrizenreduktion wird in Abbildung 3 gezeigt. Abbildung 3: Beispiele für die Matrizen Reduktion aus dem TSP Algorithmus von Little. (a) zeigt die Berechnung der unteren Schranke für das Ausgangsproblem. In (b) und (c) wird eine untere Schranke für die beiden ersten Teilprobleme berechnet (vgl. Abbildung 4). In Abbildung 3 ist der Suchbaum für die optimale Route des Graphen aus Abbildung 1 dargestellt. Jeder Knoten des Suchbaumes stellt eine Menge von Routen dar. Die Wurzel repräsentiert die Menge aller möglichen Routen. Jeder Kindknoten stellt eine Teilmenge der Menge des übergeordneten Knotens dar. Die Teilmengen werden durch Einschränkung der erlaubten Routen gebildet. Der linke Kindknoten der Wurzel stellt 5 Kapitel 2: Branch-and-Bound z.B. die Menge aller Routen dar, die die Kante (B,C) enthalten und der rechte Kindknoten die Menge aller Routen, die die Kante (B,C) nicht enthalten. Die gebildeten Teilmengen werden in jedem Verzweigungsschritt durch neue Einschränkungen weiter unterteilt. Die Werte innerhalb der Knoten entsprechen jeweils den unteren Schranken, die mittels der Matrixreduktion ermittelt wurden (vgl. Abbildung 3). Der Algorithmus berechnet zunächst einen Wert von 25 für die untere Schranke der Wurzel, was bedeutet, dass alle möglichen Routen eine Mindestlänge von 25 besitzen. Anschließend wird die Kante gesucht, deren Ausschluss die untere Schranke am stärksten erhöht. Hierfür wird derjenige Null-Wert der Ergebnismatrix gewählt, für den der nächst höhere Wert in der zugehörigen Zeile oder Spalte möglichst groß ist. In diesem Fall ist das die Kante (B,C) (vgl. Abbildung 3(a)). Die untere Schranke erhöht sich auf 29 für den Ausschluss von (B,C). Für den Einschluss beträgt die untere Schranke nur 28, daher wird die Suche im linken Teilbaum fortgesetzt. Dort werden die Kindknoten mit den unteren Schranken 31 und 34 erstellt. Da nun der Knoten „Ohne (B,C)“ die kleinste untere Schranke besitzt, wird an dieser Stelle weitergesucht. Nachdem die Kindknoten des Knotens „Ohne (B,C)“ erstellt sind, existieren zwei Knoten mit identischen unteren Schranken auf gleicher Ebene. In diesem Fall ist egal, ob der Knoten „Mit (E,D)“ oder „Mit (E,C)“ untersucht wird. Die Suche wird nach dem beschriebenen Muster fortgesetzt, bis eine zulässige Lösung gefunden ist. Abbildung 4: Der Suchbaum des TSP - Algorithmus aus Abbildung 2. 6 Kapitel 2: Branch-and-Bound 2.2 Parallele Branch-and-Bound Algorithmen Dieses Kapitel beschreibt die Möglichkeiten einen Branch-and-Bound Algorithmus zu parallelisieren. Während im Abschnitt 2.2.1 auf Parallelisierungen mit Hilfe einer globalen Liste der noch nicht untersuchten Teilprobleme eingegangen wird, beschäftigt sich der Abschnitt 2.2.2 mit Parallelisierungen durch Verwendung von verteilten Listen für die nicht untersuchten Teilprobleme. In beiden Fällen dient das Traveling Salesman Problem als Grundlage. Der letzte Abschnitt beschreibt Bedingungen, unter denen bei parallelen Branch-and-Bound Algorithmen Anomalien, wie z.B. überlineare Beschleunigung, auftreten können. 2.2.1 Multiprozessor Algorithmen An dieser Stelle sollen zwei von Mohan (1983) entwickelte Parallelisierungen des TSP Algorithmus von Little vorgestellt werden. Der erste Algorithmus basiert auf einer Parallelisierung der for-Schleife (vgl. Abbildung 2), die die beiden Teilprobleme – eines für den Einschluss und eines für den Ausschluss einer bestimmten Kante – erzeugt und für diese jeweils eine untere Schranke berechnet. Ursprünglich hat die Schleife eine natürliche Parallelität von zwei, da jeder Knoten des Suchbaumes genau zwei Kinder besitzt. Um eine größere Anzahl von Prozessoren zu ermöglichen, lässt sich der Verzweigungsfaktor des Zustandsbaumes durch die Betrachtung weiterer Kanten erhöhen. Werden bei der Erstellung von Teilproblemen an jedem Knoten des Baumes jeweils k Kanten berücksichtigt, so besitzt jeder Knoten des Baumes insgesamt 2k Kinder, da für jede Kombinationsmöglichkeit die k Kanten einzuschließen oder auszuschließen ein Teilproblem erzeugt wird. Entsprechend der Heuristik von Little werden hier die k Kanten ausgewählt, deren Ausschluss die unteren Schranken am stärksten anheben. Dieser Algorithmus eignet sich für die Ausführung auf 2k Prozessoren. Der zweite Algorithmus arbeitet mit einer sortierten Liste, die die nicht untersuchten Teilprobleme des Zustandsbaumes enthält. Diese Liste wird von mehreren von einander unabhängigen Prozessen bearbeitet. Dabei entnimmt ein Prozess das Teilproblem mit der niedrigsten unteren Schranke aus der Liste und berechnet, sofern das Problem noch keine Lösung darstellt, anhand des bekannten Verfahrens zwei neue Teilprobleme mit zugehörigen unteren Schranken. Die beiden neuen Teilprobleme werden entsprechend 7 Kapitel 2: Branch-and-Bound ihren unteren Schranken in die Liste einsortiert. Zu beachten ist, dass der Zugriff auf die Liste synchronisiert werden muss. Ein Prozess hat also immer exklusives Schreib- oder Leserecht. Quinn weist aber darauf hin, dass die Listenoperationen im Vergleich zur Erstellung von Teilproblemen nur relativ wenig Zeit in Anspruch nehmen. Demnach sollten diese die Beschleunigung kaum negativ beeinflussen. Vergleicht man die praktisch erreichte Beschleunigung der beiden Algorithmen, so fällt auf, dass insbesondere der erste Algorithmus bei größeren Prozessorzahlen sehr ineffektiv arbeitet. Die Beschleunigung ist bei mehr als 8 Prozessoren sogar rückläufig. Nach Quinn liegt dies an der Tatsache, dass mit zunehmender Prozessorzahl vermehrt Knoten erzeugt werden, die wegen ihrer hohen unteren Schranke für die optimale Lösung nicht in Betracht kommen. Bei nur zwei Prozessoren ist die Beschleunigung erwartungsgemäß nahezu linear, da hier der gleiche Zustandsbaum wie beim seriellen Algorithmus durchlaufen wird. Der zweite Algorithmus erreicht in dem Testdurchlauf mit 16 Prozessoren immerhin eine Beschleunigung von 8 bei einem 30-Knoten TSP. Als Hindernis für eine höhere Beschleunigung nennt Quinn die nicht lokalen Speicherzugriffe der Prozessoren. 2.2.2 Multicomputer Algorithmen Es sollen nun vier Varianten eines asynchronen, parallelen Branch-and-Bound Algorithmus von Quinn (vgl. [Qu90]) zur Lösung des Traveling Salesman Problems auf einem Hypercube – Multicomputer vorgestellt werden. Der Algorithmus sieht für jeden Prozessor eine eigene Prioritätswarteschlange für die noch nicht untersuchten Teilprobleme vor. Die Prozessoren bearbeiten ihre Warteschlangen unabhängig von einander. Der Unterschied zwischen den vier Varianten des Algorithmus besteht ausschließlich in der Heuristik für den Austausch von nicht untersuchten Teilproblemen. Der Ablauf des Algorithmus lässt sich wie folgt beschreiben: Jeder Prozessor, dessen Warteschlange nicht leer ist, entfernt das Teilproblem mit der niedrigsten unteren Schranke aus seiner Warteschlange. Sollte sich das Teilproblem nicht direkt lösen lassen, wird es mittels der bereits beschriebenen Methode von Little in zwei Teilprobleme unterteilt. Nachdem diese wieder in die Warteschlange eingefügt wurden, wird entsprechend einer Heuristik eines der Teilprobleme aus der Warteschlange entfernt und an einen benachbarten Prozessor geschickt. Alle Algorithmen verwenden bei der Wahl des Zielprozessors folgende Regel: Sei p=2d die Anzahl der Prozessoren, dann sendet ein Prozessor i bei der Iteration j ein nicht 8 Kapitel 2: Branch-and-Bound untersuchtes Teilproblem an Prozessor r. Die Nummer r wird durch Invertierung des Bits an der Stelle (j mod d) der Zahl i ermittelt. Zu Beginn der Ausführung enthält die Warteschlange von Prozessor 0 das Ausgangsproblem. Jeder Prozessor, dessen Warteschlange nicht leer ist, kann bei jeder Iteration einen weiteren Prozessor mit einem Teilproblem versorgen. Die Anzahl der aktiven Prozessoren kann also mit jeder Iteration verdoppelt werden. Nach log2(p) Iterationen sind somit alle p Prozessoren aktiviert (vgl. Abbildung 5). Abbildung 5: 3-dimensionaler Hypercube (aus 8 Prozessoren). Die Pfeile mit den Ziffern verdeutlichen die Aktivierungsreihenfolge zu Beginn der Ausführung (wie sie in den hier vorgestellten Algorithmen implementiert ist). Entsprechend dem seriellen TSP Algorithmus ist die Suche beendet, sobald die beiden Abbruchbedingungen eines Branch-and-Bound Algorithmus erfüllt sind. Es muss mindestens eine Lösung gefunden sein und es darf keine nicht untersuchten Knoten mit einer unteren Schranke geben, die niedriger ist, als die beste der bisher gefundenen Lösungen. Wie bereits erwähnt, hat Quinn vier Varianten dieses Algorithmus hinsichtlich der erzielten Beschleunigung getestet. Die Unterschiede der Algorithmen beruhen auf der Verteilungsheuristik für nicht untersuchte Teilprobleme und können der Tabelle 1 entnommen werden. Algorithmus 1 Füge neues Teilproblem, bei dem die Kante eingeschlossen wird, in die Warteschlange ein und sende Teilproblem bei dem die Kante ausgeschlossen wird. Algorithmus 2 Füge neues Teilproblem mit kleinerer unterer Schranke in die Warteschlange ein und sende das Teilproblem mit der größeren 9 Kapitel 2: Branch-and-Bound unteren Schranke. Algorithmus 3 Füge die beiden neuen Teilprobleme in die Warteschlange ein. Sende und entferne das zweitbeste Problem der Warteschlange. Algorithmus 4 Füge beide neuen Teilprobleme in die Warteschlange ein. Sende und entferne das beste Problem der Warteschlange. Tabelle 1: Austauschheuristiken der vier Varianten des parallelen Algorithmus von Quinn für das TSP. Ein theoretischer und praktischer Vergleich der vier Varianten in [QU94] für ein 30Knoten Taveling Salesman Problem führt zu dem Ergebnis, dass die Algorithmen 3 und 4 den beiden anderen deutlich überlegen sind. Die Performanceunterschiede weisen laut Quinn darauf hin, dass es bei dieser Art von parallelen Algorithmen wichtig ist, eine gute Heuristik für das Austauschen von nicht untersuchten Teilproblemen zu verwenden. Das Ziel eines guten parallelen Algorithmus, der verteilte Warteschlangen benutzt, sollte es folglich sein, möglichst viele Prozessoren mit sinnvoller Arbeit zu versorgen. In diesem konkreten Fall bedeutet dies, dass die Prozessoren einen möglichst großen Anteil ihrer Arbeit mit der Untersuchung von „lohnenswerten“ Knoten verbringen sollen. Der durchlaufene Zustandsbaum soll dem des seriellen Algorithmus möglichst ähnlich sein. 2.2.3 Anomalien beim parallelen Branch-and-Bound Unter bestimmten Voraussetzungen lassen sich bei parallelen Branch-and-Bound Algorithmen Aussagen über die theoretisch erreichbare Beschleunigung machen. Die folgenden Sätze beruhen auf der Analyse von Lai und Sahni. Zunächst werden aber noch einige vereinfachende Annahmen gemacht, um die folgende Analyse zu erleichtern: 1. Für alle Knoten des Zustandsbaumes sei die benötigte Zeit, um diese in Teilprobleme aufzuteilen, konstant. 2. Bei jeder Iteration bearbeitet jeder Prozessor ein anderes Teilproblem (sofern genügend Teilprobleme vorhanden sind). 3. I(p) sei die Anzahl der Iterationen, die p Prozessoren benötigen, um ein gegebenes Branch-and-Bound Problem zu lösen. 10 Kapitel 2: Branch-and-Bound Außerdem soll g(x) dem Wert der unteren Schranke eines Knotens x entsprechen und f* dem Wert der optimalen Lösung. Satz 1 besagt, dass eine Erhöhung der Prozessorenzahl zu einer Verlängerung der Ausführungszeit führen kann. Satz 1: Sei n1 < n2 und k > 0, dann gibt es einen Zustandsbaum, für den gilt k * I(n1) < I(n2). (vgl. [LS84]) Beweis: Die Richtigkeit des Satzes lässt sich leicht durch Konstruktion eines geeigneten Zustandsbaumes zeigen. Abbildung 6 stellt einen solchen Baum dar. Alle „=“ - Knoten haben die gleiche untere Schranke, wie der Knoten mit der optimalen Lösung (Knoten A). Entsprechend besitzen alle „>“ - Knoten eine größere untere Schranke, als der Knoten mit der optimalen Lösung (g(x)>f*). Führen nun n1 Prozessoren die Suche durch, so wird in der ersten Iteration das Ausgangsproblem, also die Wurzel des Zustandsbaumes, erweitert. Das Resultat sind n1+1 entstehende Teilprobleme. Angenommen der Algorithmus verfolgt die n1 am weitesten links stehenden Teilprobleme, so werden diese zu n1 neuen Knoten erweitert. Bei der 3. Iteration werden nun in jedem Fall der „=“ – Knoten auf Ebene 3 und der Knoten B erweitert, da diese die kleinsten unteren Schranken aufweisen. Nach der 3. Iteration ist eine optimale Lösung, der Knoten A, bereits erreicht. Weitere Knoten brauchen nicht untersucht zu werden, da alle unteren Schranken der nicht untersuchten Knoten größer oder gleich der Lösung im Knoten A sind. Es gilt I(n1) = 3. Abbildung 6: Zustandsbaum, der zeigt, dass eine Erhöhung der Prozessorenzahl zu einer Verlängerung der Ausführungsdauer führen kann. 11 Kapitel 2: Branch-and-Bound Bei n2 Prozessoren verläuft die Suche folgendermaßen: Bei der ersten Iteration wird die Wurzel des Zustandsbaumes auf n1+1 neue Knoten erweitert. Im nächsten Schritt werden alle n1+1 Knoten erweitert, da n2 n1 + 1 ist. Aus den n1+n2 entstandenen Knoten wählt dieser Algorithmus nun die n2 am weitesten rechts stehenden aus. Diese werden nun bis zur Iteration 3k ständig erweitert. Bei der Iteration 3k+1 wird in jedem Fall der verbleibende „=“ – Knoten der 3. Ebene zum Knoten A erweitert, da dieser die niedrigste untere Schranke aufweist. Die Suche ist hier also erst nach 3k+1 Iterationen beendet. Es gilt I(n2) = 3k+1. Aus beiden Ergebnissen folgt: kI(n1) = 3k < 3k + 1 = I(n2) Die Anomalie, die in Satz 1 beschrieben wird, beruht auf der relativ großen Anzahl an Knoten, für die gilt g(x)=f*. Wenn jedoch für alle inneren Knoten g(x) f* gilt, so führt eine größere Anzahl Prozessoren in jedem Fall zu einer größeren Beschleunigung (vgl. Satz 2). Definition: Jeder Knoten x mit g(x)< f* wird als kritischer Knoten bezeichnet (vgl. [Qu94]). Satz 2: Wenn für alle Knoten, die keine Lösungsknoten sind, gilt g(x) daraus I(1) f*, dann folgt I(n) für alle n > 1. (vgl. [LS84]) Beweis: Bei nur einem Prozessor werden wegen der best-first Suchstrategie des Branchand-Bound Algorithmus bis zum Erreichen einer optimalen Lösung ausschließlich kritische Knoten untersucht. Angenommen es existieren genau m solcher Knoten, dann terminiert die Suche mit einem Prozessor nach m Iterationen. Es gilt I(1) = m. Aufgrund der Tatsache, dass in einem Suchbaum die unteren Schranken der Knoten von der Wurzel zu den Blättern nicht kleiner werden dürfen, gilt das folgende Lemma. Lemma: In einem Suchbaum ist jeder Knoten, der einem kritischen Knoten übergeordnet ist, ebenfalls ein kritischer Knoten. Für den Fall, dass nun n (n>1) Prozessoren eine best-first Suche durchführen, wird, wegen der Gültigkeit des Lemmas, bei jeder Iteration mindestens einer dieser kritischer Knoten erweitert. Nach höchstens m Iterationen enthält der Zustandsbaum also keine ununtersuchten kritischen Knoten mehr. Spätestens jetzt ist eine optimale Lösung gefunden. Bei m kritischen Knoten gilt: I(n) m. Es folgt also: I(1) I(n). Der dritte Satz zeigt, dass es unter bestimmten Umständen zu einer überlinearen Beschleunigung ( I(n1)/I(n2) > n2/n1 ) kommen kann. 12 Kapitel 2: Branch-and-Bound Satz 3: Sei n1 < n2 und k > n2/n1, dann existiert ein Zustandsbaum, für den gilt I(n1)/I(n2) k. (vgl. [LS84]) Beweis: Der Zustandsbaumes aus Abbildung 7 zeigt die Korrektheit des Satzes. Wird das zugehörige Problem von n1 Prozessoren bearbeitet, so wird bei der ersten Iteration das Ausgangsproblem erweitert. Auf der Ebene 1 des Baumes entstehen n2 neue Knoten. Der Algorithmus wählt nun die n1 am weitesten links stehenden Knoten aus und erweitert diese bis zur Ebene k. Die restlichen Knoten der Ebene 1 werden also erst nach k Iterationen untersucht. Es gilt I(n1) k. Wird die Suche von n2 Prozessoren durchgeführt, so ergeben sich nach einer Iteration ebenfalls n2 neue Knoten auf Ebene 1. Der Algorithmus terminiert hier also schon nach einer Iteration, da der Knoten A mit einer optimalen Lösung erreicht ist. Alle nicht untersuchten unteren Schranken sind größer oder gleich der gefundenen Lösung. Es gilt I(n2) = 1. Aus beiden Ergebnissen folgt, I(n1)/I(n2) k (bzw. I(n1)/I(n2) > n2/n1 ). Abbildung 7: Zustandsbaum, der zeigt, wie es zu einer überlinearen Beschleunigung kommt. Es gilt k > n1/n2. Im Allgemeinen kann jedoch nicht mit einer überlinearen Beschleunigung gerechnet werden, wie der folgende Satz zeigt. Satz 4: Wenn für alle Knoten, die keine optimalen Lösungsknoten sind, gilt: g(x) f* ist dann ist I(1)/I(n) n für alle n > 1. (vgl. [LS84]) Beweis: Sei m die Anzahl der kritischen Knoten, dann gilt I(1) = m (vgl. Satz 2). Bei mehreren Prozessoren kann die Suche frühestens nach Untersuchung aller kritischen 13 Kapitel 3: Alpha-Beta Suche Knoten terminieren. n (n>1) Prozessoren benötigen hierfür mindestens m/n Iterationen. Also gilt, I(n) m/n. Es folgt, dass I(1)/I(n) n. 3 Alpha-Beta Suche 3.1 Sequenzielle Alpha-Beta Suche Die Alpha-Beta Suche ist eine Erweiterung des Minimax Verfahrens und eine Methode zur Bewertung von Spielzügen in so genannten Zwei-Personen-Null-Summen Computerspielen, wie z.B. Schach, Dame oder „Tic Tac Toe“. Die möglichen Spielverläufe eines solchen Spieles lassen sich durch einen Spielbaum darstellen (vgl. Abbildung 8). Die gestrichelten Kanten stellen die Zugvarianten des ersten Spielers dar, die durchgezogenen die des zweiten Spielers. Mögliche Zustände des Spieles werden durch die Knoten repräsentiert, wobei die Wurzel des Baumes den Startzustand und die Blätter die Endzustände darstellen. In den Blättern ist der Endwert des Spieles vermerkt, dieser entspricht dem Gewinn des ersten Spielers (bzw. der Verlust des zweiten Spielers). Der erste Spieler wird versuchen, den Endwert zu maximieren, während der zweite Spieler bemüht ist, diesen zu minimieren. Ausgehend von den Blättern des Spielbaumes ist es mit Hilfe des Minimax Algorithmus möglich, die restlichen Knoten mit Werten zu versehen. Alle Knoten, bei denen Spieler Eins an der Reihe ist, werden mit dem maximalen Wert ihrer Kindknoten versehen, da Spieler Eins immer den Zug wählen wird, der den Spielwert maximiert. Dem entsprechend erhalten die Knoten, bei denen Spieler Zwei an der Reihe ist, den minimalen Wert ihrer Kindknoten. Die Knoten von Spieler Eins werden daher auch als Maximum-Knoten, die Knoten von Spieler Zwei als Minimum-Knoten bezeichnet. Der Wert eines MaximumKnotens gibt den Gewinn an, den Spieler Eins mindestens erhält, sofern er nach der Maximum-Strategie spielt. Der Wert eines Minimum-Knotens steht für den höchstens erreichbaren Gewinn von Spieler Eins. Da die Spielbäume vieler Null-Summen Spiele oft zu komplex sind, um sie komplett zu durchsuchen, muss die maximale Suchtiefe in diesen Fällen (die Höhe des Baumes) beschränkt werden. Die Werte der Blätter werden in diesem Fall durch eine Schätzfunktion für den Wert des Spieles ermittelt. Da eine Schätzfunktion den Wert des 14 Kapitel 3: Alpha-Beta Suche Spieles nur sehr ungenau bestimmen kann, ist für eine gute Prognose des Spielwertes an der Wurzel ein Suchbaum mit möglichst hoher Tiefe wünschenswert. Abbildung 8: oben: Spielbaum eines Null-Summen Spiels. Die Knoten repräsentieren die Spielstellungen, die Kanten die Spielzüge. Die Blätter enthalten Der Alpha-Beta Algorithmus versucht die Suchtiefe zu erhöhen, in dem er durch Anwendung einer Branch-and-Bound Technik diejenigen Teilbäume, die das Ergebnis der Suche nicht beeinflussen, aus der Suche ausschließt. Eine mögliche JavaImplementierung des Alpha-Beta Algorithmus zeigt Abbildung 9. Der Algorithmus wird mit vier Parametern aufgerufen: Dem Knoten des zu durchsuchenden Teilbaumes, der Tiefe des Suchvorganges und den Parametern alpha und beta, die eine untere bzw. obere Grenze für den gesuchten Spielwert darstellen. public static int alphaBeta (MinMaxNode node, int depth, int alpha, int beta) { if (node.isLeaf() || depth <= 0) return node.evaluate(); Iterator moves = node.getMoves(); if ( node.isMinNode() ) { while ( moves.hasNext() ) { int val = alphaBeta ( (MinMaxNode)moves.next(), depth-1, alpha, beta); if ( val < beta ) beta = val; if alpha >= beta return beta; } return beta; } else { 15 Kapitel 3: Alpha-Beta Suche while ( moves.hasNext() ) { int val = alphaBeta ( (MinmaxNode)moves.next(), depth-1, alpha, beta); if ( val > alpha ) alpha = val; if alpha >= beta return alpha; } return alpha; } } Abbildung 9: Java-Implementierung des sequenziellen Alpha-Beta Algorithmus Die Abbildung 10 verdeutlicht den Ablauf des Alpha-Beta Algorithmus anhand eines Spielbaumes. Der „preorder“ Durchlauf startet an der Wurzel mit den Parametern = - und = . Im Verlauf der Suche gibt den minimal und den maximal erreichbaren Endwert des Spieles im Bezug auf alle bisher durchsuchten Knoten an. Ein innerer Knoten wird mit den ermittelten und Werten des übergeordneten Knotens durchsucht. Handelt es sich bei diesem Knoten um einen Maximum-Knoten, so wird in dessen Nachfolgern nach einem größeren Wert für gesucht. Im Falle eines Minimum- Knotens wird in den Nachfolgern nach einem geringeren Wert für gesucht. Das Ergebnis der Suche wird an den übergeordneten Knoten zurückgegeben. Im Falle eines Maximum-Knotens ist dies der Wert und im Falle eines Minimum-Knotens der Wert. Wird ein Blatt untersucht, so wird der Wert des Blattes zurückgegeben. Abbildung 10: Suchbaum einer Alpha-Beta Suche. Die Werte der Blätter werden durch eine Schätzfunktion bestimmt. Alle inneren Knoten werden durch ihre Nachfolger berechnet. An den markierten Knoten wird der Suchbaum beschnitten. Zu einer Beschneidung des Suchbaumes kommt es, sobald in einem Knoten der größer oder gleich dem Wert Wert ist. In diesem Fall ist für den Spieler, dessen Zug zu diesem Knoten führen würde, bereits eine gleich gute oder bessere Zugfolge gefunden worden. Eine solche Beschneidung tritt zum Beispiel im Knoten A von Abbildung 10 auf. Da der Vorgänger von A, ein Minimum-Knoten, in seinem linken Teilbaum bereits 16 Kapitel 3: Alpha-Beta Suche einen Spielwert von 2 ermittelt hat, wird Knoten A mit den Werten =- und =2 untersucht. Dieser untersucht zunächst seinen ersten Nachfolger, ebenfalls mit den Werten = - und = 2. Da dieser Knoten ein Minimum-Knoten ist und sich in dessen Nachfolgern (den Blättern) keine kleineren Werte als (= 2) befinden, wird der Wert (= 2) zurückgegeben. Im Maximum-Knoten A wird darauf hin = = 2 gesetzt. Da nun gilt, braucht dieser Teil des Suchbaumes nicht weiter untersucht zu werden, denn für den Spieler des übergeordneten Knoten ist bereits eine mindestens gleichwertige Zugfolge ermittelt worden. Der Aufwand der Alpha-Beta Suche entspricht größenordnungsmäßig der Anzahl der untersuchten Blätter des Spielbaums. Der Worst-Case tritt auf, falls von jedem Knoten aus der beste Zug zuletzt untersucht wird. In diesem Fall müssen alle Blätter des Spielbaums durchsucht werden. Wenn b der Verzweigungsfaktor des Baumes und d die Suchtiefe ist, dann beträgt der Aufwand der Alpha-Beta Suche O( bd ). Im Best-Case wird von jedem Knoten aus der beste Zug zuerst untersucht (vgl. Abbildung 11). Slagle und Dixon [SD69] haben gezeigt, dass der Alpha-Beta Algorithmus im Best-Case nur b d /2 +b d /2 + 1 Blätter untersucht. Es ergibt sich also ein Aufwand von O( 2bd/2 ). Abbildung 11: Suchbaum der Alpha-Beta Suche im Best-Case. Es gibt drei Typen von Knoten. Die Wurzel des Baumes ist ein Typ 1 Knoten. Der erste Nachfolger eines Typ 1 Knotens ist wieder ein Typ 1 Knoten. Alle weiteren sind vom Typ 2. Der erste Nachfolger eines Typ 2 Knotens ist ein Typ 3 Knoten. Alle weiteren Nachfolger werden nicht untersucht. Alle Nachfolger eines Typ 3 Knotens sind vom Typ 2. Der effektive Verzweigungsfaktor eines Algorithmus, der einen Spielbaum durchsucht berechnet sich nach der Definition von Quinn [Qu94] durch folgende Formel: d Anzahl der untersuchten Blätter , wobei d die Suchtiefe des Algorithmus bezeichnet. Der Verzweigungsfaktor der Alpha-Beta Suche im Best-Case ist also gleich d 2b d/2 = d 2 * b . Bei größeren Suchtiefen entspricht dies näherungsweise einem 17 Kapitel 3: Alpha-Beta Suche Verzweigungsfaktor von b . Bei gleichem Aufwand kann die Suchtiefe also verdoppelt werden. Quinn [Qu94] weist darauf hin, dass optimal geordnete Spielbäume in der Praxis nicht auftauchen und daher in der Regel mit einem 50 Prozent Mehraufwand im Vergleich zum Best-Case gerechnet werden muss. 3.1.1 Erweiterungen der Alpha-Beta Suche An dieser Stelle sollen zwei Erweiterungsmöglichkeiten der Alpha-Beta Suche, die Aspirationssuche und die iterative Vertiefung, vorgestellt werden. Die Aspirationssuche versucht die Alpha-Beta Suche durch Verkleinerung des Suchfensters, das durch und beschrieben wird, zu beschleunigen. Statt der Werte (- , ) für die Parameter ( , ) wird das Suchfenster auf ( - , + ) beschränkt. Wobei ein Schätzer für das erwartete Ergebnis der Suche ist und die Höhe des zulässigen Fehlers angibt. Liegt das tatsächliche Ergebnis innerhalb des verkleinerten Suchfensters, so verkürzt sich die Suchzeit. Entspricht das Ergebnis der Suche jedoch der oberen oder unteren Schranke des Suchfensters, so befindet sich das tatsächliche Ergebnis oberbzw. unterhalb des Suchfensters. Es muss eine erneute Suche mit den Parametern ( + , ) bzw. (- , - ) durchgeführt werden. Bei der iterativen Vertiefung wird die Alpha-Beta Suche mehrmals hintereinander mit steigender Suchtiefe durchgeführt. Obwohl bei dieser Vorgehensweise Teile des Spielbaumes mehrfach durchsucht werden, stellt dieses Verfahren eine Verbesserung dar. Die Informationen vorheriger Iterationen dienen dazu, die folgenden Iterationen zu beschleunigen, indem z.B. der Suchbaum optimiert oder das Suchfenster verkleinert wird. Zudem ist es bei der iterativen Vertiefung möglich, die Laufzeit der Suche zu kontrollieren. Sollte die Suche während einer Iteration unterbrochen werden, ist sie nicht ergebnislos, da die vorherigen Iterationen bereits Ergebnisse geliefert haben. 3.2 Parallele Alpha-Beta Suche 3.2.1 Parallelisierung der Zugerzeugung und der Positionsbewertung Die Zugerzeugung und die Bewertung von Spielpositionen lassen sich bei der AlphaBeta Suche leicht parallelisieren. Beide beziehen sich nur auf einen Teil des 18 Kapitel 3: Alpha-Beta Suche Algorithmus (vgl. Abbildung 9). Der Suchprozess bleibt weiterhin sequenziell. Zudem ist die erreichbare Beschleunigung, z.B. durch die Anzahl der Zugmöglichkeiten, begrenzt. 3.2.2 Parallele Aspirationssuche Auch die weiter oben vorgestellte Aspirationssuche lässt sich einfach parallelisieren. Mit 3 Prozessoren ist es möglich, für jedes der Suchfenster (- , - ), ( - , + ) und ( , ) gleichzeitig eine Suche durchzuführen. Liegt das tatsächliche Ergebnis nicht im vermuteten Suchfenster ( - , + ), so garantiert die parallele Suche für die beiden anderen Suchfenster, dass die Suche mindestens genauso schnell beendet ist, wie die Suche mit einem Prozessor im Bereich (- , ). Durch Änderung der Größe und Anzahl der Suchfenster kann die Anzahl der Prozessoren weiter erhöht werden. Zu beachten ist, dass ( 2bd/2 ) auch eine untere Schranke für den Aufwand der Suche mit minimalem Suchfenster ist. Nach Quinn [Qu94] kann daher unabhängig von der Anzahl an Prozessoren nur eine maximale Beschleunigung von 5 oder 6 erreicht werden. 3.2.3 Parallele Teilbaumsuche Eine weitere Methode, die Alpha-Beta Suche zu parallelisieren, besteht darin, den Spielbaum in eine Menge von unanhängigen Teilbäumen zu zerlegen und diese mit einer Menge von Prozessoren parallel zu untersuchen. Auf Grund der Tatsache, dass mehrere Teilbäume gleichzeitig durchsucht werden, entsteht im Vergleich zur sequenziellen Suche ein zusätzlicher Aufwand. Dieser besteht aus zusätzlichem Kommunikationsaufwand und zusätzlichem Suchaufwand. Angenommen ein Spielbaum ließe sich an seiner Wurzel in mehrere Teilbäume zerlegen. Ein sequenzieller Alpha-Beta Algorithmus würde die einzelnen Teilbäume nacheinander durchsuchen. Die Suche würde in diesem Fall im ersten Teilbaum mit dem Suchfenster (- , ) beginnen und alle nachfolgenden Teilbäume mit einem eingeschränkten Fenster durchsuchen. Werden nun aber alle Teilbäume gleichzeitig mit einem Suchfenster von (- , ) durchsucht, so müssen in einigen Teilbäumen mehr Knoten untersucht werden, als im sequenziellen Fall. Dieser zusätzliche Suchaufwand lässt sich nur durch zusätzlichen Kommunikationsaufwand reduzieren, indem die Prozessoren ihre neu ermittelten Suchfenster untereinander austauschen. 19 Kapitel 3: Alpha-Beta Suche Im Folgenden sollen die beiden Extremfälle, ein Algorithmus mit minimalem zusätzlichem Kommunikationsaufwand und ein Algorithmus mit minimalem zusätzlichem Suchaufwand, untersucht werden. Eine einfache Möglichkeit, den Kommunikationsaufwand zu minimieren, besteht darin, den Spielbaum an seiner Wurzel in mehrere Teilbäume zu unterteilen und jedem Teilbaum einen Prozessor zuzuordnen. Die Prozessoren durchsuchen die Teilbäume unabhängig voneinander, ohne ihre neu ermittelten Schranken auszutauschen. Wie bereits oben erwähnt kommt es bei diesem Vorgehen zu einem erheblichen zusätzlichen Suchaufwand. Unterstellt man einen optimal geordneten Spielbaum und nimmt weiterhin an, dass der erste Teilbaum des Spielbaumes von nur einem Prozessor durchsucht wird, so ist die maximal erreichbare Beschleunigung äußerst gering (vgl. [Qu94]). Dies liegt daran, dass der erste Teilbaum eines optimalen Spielbaumes der Höhe d ebenfalls ein optimaler Spielbaum mit Höhe d-1 ist (vgl. Abbildung 11). Ein überproportional großer Anteil des Suchaufwandes entfällt damit auf den ersten Teilbaum. Quinn nennt hier als Beispiel einen optimal geordneten Spielbaum der Höhe 8 mit einem Verzweigungsfaktor von 38. Hier entfallen ca. 51,3% des Suchaufwandes auf den ersten Teilbaum, die erreichbare Beschleunigung wäre niedriger als 2. Um den zusätzliche Suchaufwand zu minimieren muss sichergestellt werden, dass der parallele Algorithmus die gleichen Knoten untersucht, wie der sequenzielle Alpha-Beta Algorithmus. Unter der Annahme, dass ein optimal geordneter Spielbaum untersucht wird, muss ein paralleler Algorithmus daher die folgenden Regeln einhalten. Wird der Teilbaum eines Knotens vom Typ 1 (vgl. Abbildung 11) untersucht, so muss zunächst der Kindknoten vom Typ 1 untersucht werden, bevor mit der Suche in den Kindknoten vom Typ 2 begonnen werden kann. Hierdurch wird garantiert, dass die Suche in den Kindknoten vom Typ2 mit minimalem Suchfenster begonnen wird. Nur so ist sichergestellt, dass möglichst wenig Knoten untersucht werden müssen. Alle Knoten vom Typ 2 können anschließend parallel zueinander durchsucht werden. Ein Teilbaum eines Knotens vom Typ 2 wird nur in seinem ersten Kindknoten durchsucht, daher kann hier keine parallele Suche angewandt werden. Ein Teilbaum eines Knotens vom Typ 3 enthält nur Knoten vom Typ 2 und kann daher ohne Einschränkungen parallel durchsucht werden. 20 Kapitel 4: Zusammenfassung Bei der hier angewandten Strategie wird mit der Durchsuchung von Teilen des Spielbaumes gewartet, bis kleinere Suchfenster gefunden sind. Dies führt auch bei nicht optimalen Spielbäumen zu einer erheblichen Reduzierung des zusätzlichen Suchaufwands. 4 Zusammenfassung Im Rahmen dieser Ausarbeitung wurden zwei Arten von Algorithmen für die kombinatorische Suche vorgestellt, das Branch-and-Bound Verfahren und die Alpha Beta Suche. Für das Branch-and-Bound Verfahren, welches anhand des Traveling Salesman Problems erläutert wurde, sind drei parallele Algorithmen vorgestellt worden. Zwei Algorithmen von Mohan für ein Multiprozessorsystem und ein Algorithmus von Quinn für einen Hypercube Multicomputer. Die Arbeit hat gezeigt, dass die erreichbare Beschleunigung für parallele Branch-and-Bound Algorithmen umso größer ist, je besser es gelingt, die einzelnen Prozessoren mit sinnvoller Arbeit zu beschäftigen. Für die Alpha-Beta Suche wurden drei Möglichkeiten der Parallelisierung aufgezeigt, die parallele Zugerzeugung und Bewertung, die parallele Aspirationssuche und die parallele Teilbaumsuche. Es wurde gezeigt, dass beim Einsatz von einer größeren Anzahl an Prozessoren nur die parallele Teilbaumsuche eine akzeptable Beschleunigung erzielt. 21 Literaturverzeichnis 5 Literaturverzeichnis [Qu94] Michael J. Quinn: Parallel Computing Theory and Practise, 2. Aufl., McGrawn-Hill, 1994. [Qu90] Michael J. Quinn: Analysis and implementation of branch-and-bound algorithms on a hypercube multicomputer. IEEE Transactions on Computers, vol. C-39, no. 3, S. 384-387, 1990 [LS84] T. H. Lai und S. Sahni: Anomalies in parallel branch-and-bound algorithms, Communications of the ACM, vol. 27, no.6, S. 594-602, 1984 [Li63] J. D. C. Little, K. G. Murty, D. W. Sweeney, C. Karel: An algorithm for the traveling salesman problem. Operations Research, vol. 11, no. 6, S. 972989, 1963 [SD69] J. R. Slagle, J. K. Dixon: Experiments with some programs that search game trees. Journal of the ACM, vol. 16, no.3, S.189-207, 1969. 22