Backtracking (Zurücksetzen) 10 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) 10.1 Grundlagen Ablauf: • Implizit wird ein Baum mogliher Losungsansatze aufgebaut. Prinzip: • Systematishes Explorieren aller eventuellen Losungsmoglihkeiten (exhaustive Suhe) • mit Zur uksetzen, falls ein Weg als niht zum Ziel fuhrend erkannt wird (z.B. im Irrgarten). • F uhrt ein Kind eines Knotens niht zur Losung, wird zum Va- ter zurukgesetzt und es werden, falls vorhanden, weitere Kinder untersuht. • Dies entspriht der Tiefensuhe DFS in Graphen; dort wird ebenfalls implizit ein Spannbaum erzeugt. Dagegen sind beim dynamishen Programmieren alle unterwegs berehneten Werte essentiell fur das Gesamtergebnis. c B. Möller {1{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) {2{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) Ein typishes Beispiel ist die Beispiel 10.1.1 (Acht-Damen-Aufgabe) Stelle aht Damen so auf ein Shahbrett, dass sie sih paarweise niht bedrohen. Lösung: • Setze eine Dame auf ein beliebiges Feld. • Versuhe nun, die restlihen so zu plazieren, dass die Aufgabe gelost wird. • Geht dies niht, setze zur uk und versuhe ein anderes Feld fur die vorige Dame. ⊓ ⊔ c B. Möller c B. Möller {3{ Informatik III WS 2011/12 Entsheidend ist, dass oft sofort, ohne in die Tiefe abzusteigen, • erkannt werden kann, dass ein Weg niht zum Ziel f uhrt bzw. • sih keine Losung mit besserer Gewihtung als die bisher gefun- denen bietet (branh-and-bound). Dies ergibt in der Regel, wenn auh niht im shlehtesten Fall, eine erheblihe Beshleunigung. Beispiel 10.1.2 (Forts. Acht-Damen-Aufgabe) F ur die zweite Dame sheiden alle Felder aus, die die erste bedroht. c B. Möller {4{ ⊓ ⊔ Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) Schemaalgorithmus f ur Baktraking-Verfahren (erfolgreich ist eine globale Variable, die mit false zu initialisieren ist): public static void solve() ; { initialisiere Kandidatenwahl für nächsten Schritt ; do { wähle nächsten Kandidaten ; if (annehmbar) { liste den Kandidaten auf ; if (Lösung unvollstaendig) { solve() ; // untersuche nächste Suchbaumetage if (!erfolgreich) lösche Kandidaten aus der Liste ; } else erfolgreich = true ; } } while (!erfolgreich && weitere Kandidaten) ; } c B. Möller {5{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) Grundlage ist, dass sih in zulassigen Stellungen die Damen auh auf Ausshnitten des Shahbretts niht gegenseitig bedrohen. • Damit ist folgende Verallgemeinerung moglih: • Betrahte zu beliebigem n ein n × n-Shahbrett mit n Damen. • Versuhe nun durh Rekursion, eine Losung f ur ein kleines Brett zu einer Losung fur ein groeres zu erweitern. c B. Möller {6{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) Beobachtung: In einer zulassigen Stellung stehen nie 2 Damen in der selben Spalte oder Zeile. Also kann man die Damen z.B. anhand ihrer Spaltennummern identizieren und brauht nur noh die Zeilenposition jeder Dame zu speihern. c B. Möller Wir geben nun ein Programm fur das Aht-Damen-Problem an. {7{ Informatik III WS 2011/12 In der folgenden Java-Losung handeln die Damen \eigenverantwortlih": • Jede fordert ihre rehte Nahbarin auf, sih zulassig zu platzieren; • misslingt dies, shreitet sie ein Feld in ihrer Spalte fort und wie- derholt das Verfahren. c B. Möller {8{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) class Queen { int row ; int column ; Queen neighbor ; int testOrAdvance() { if (neighbor != null && neighbor.canAttack(row, column)) return next(); return 1 ; } public Queen(int c, Queen ngh) { column = c ; neighbor = ngh ; } public int first() boolean canAttack (int r, int c) { int cd ; if (row == r) return true; cd = c - column; if (row + cd == r || row - cd == r) return true; if (neighbor != null) return neighbor.canAttack(r, c) ; return false ; } // berechne erste zulässige Stellung // für Dame und Nachbarin { row = 1 ; if (neighbor!= null && neighbor.first() != 0) return testOrAdvance() ; return 1 ; } c B. Möller {9{ Informatik III WS 2011/12 Backtracking (Zurücksetzen) public int next() Informatik III WS 2011/12 Backtracking (Zurücksetzen) // berechne weitere zulässige Stellung // für Dame und Nachbarin { if (row == 8) { if (neighbor == null || neighbor.next() == 0) return 0 ; row = 0 ; } row = row + 1; return testOrAdvance() ; } public void print() // print solution { if (neighbor != null) neighbor.print() ; System.out.println("column " + column + " row " + row) ; } c B. Möller { 10 { c B. Möller { 11 { Informatik III WS 2011/12 public static void main(String[] args) //Hauptprogramm { Queen lastQueen; int i; lastQueen = null ; for (i = 1; i <= 8; i++) lastQueen = new Queen(i, lastQueen) ; if (lastQueen.first() != 0) lastQueen.print() ; } } c B. Möller { 12 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) 10.2 Backtracking (Zurücksetzen) Spiele Ziel dieses Abshnitts ist die Entwiklung einer Strategie fur das Spiel Rehner gegen Mensh. Beispiel 10.2.1 (Tic-Tac-Toe, 3-Gewinnt) • Idee: Zeihne die moglihen Spielablaufe als Baum bzw. Graphen. • Zur Analyse konnte man Tabellen verwenden, in denen alle • Die Knoten entsprehen Stellungen, Kanten stehen f ur moglihe Uberg ange gema den Spielregeln. • Versuhe, mit dem Spannbaum des Graphen allein auszukommen, um mehrfahe Analyse gleiher Stellungen zu vermeiden. Bei optimalem Spiel beiderseits entsteht stets Remis. moglihen Spielverlaufe festgehalten sind. • Damit wird aber nur das spezielle Spiel Ti-Ta-Toe erfasst. • Auerdem werden die Tabellen f ur komplexere Spiele viel zu gro. Besser ist ein allgemeiner Ansatz zur Berehnung, ob eine Stellung sihere Gewinn-, Remis- oder Verluststellung ist. Fragestellung: Gibt es eine Strategie, die stets zu Remis bzw. zum Verlust des Gegners fuhrt? { 13 { c B. Möller Informatik III WS 2011/12 Backtracking (Zurücksetzen) 10.2.1 c B. Möller { 14 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Minmax-Strategie Wir betrahten Spiele mit zwei Teilnehmern. • Die Spieler seien S1 (etwa der Rehner) und S2 (etwa der Mensh). • Weiter verwenden wir eine ganzzahlige Bewertungsfunktion f ur Stellungen, etwa Wert 0 fur Remis, positive Bewertung fur sihere Gewinnstellungen von S1 und negative fur sihere Verluststellungen von S1. • Eine Stellung, f ur die die Bewertung sofort bestimmt werden kann, heit terminal. • F ur eine niht-terminale Stellung berehnet man die Bewertung durh rekursive Analyse aller Nahfolgestellungen unter Annahme von optimalem Spiel beider Gegner. c B. Möller { 15 { Informatik III WS 2011/12 • Ist in einer Stellung P der Spieler S1 am Zug, so bewertet er rekursiv alle Nahfolgestellungen. Er zieht in eine Nahfolgestellung P ′ mit maximaler Bewertung. • In P ′ ist nun S2 am Zug. Er bewertet rekursiv alle Nahfolgestellungen von P ′ und zieht in eine mit minimaler Bewertung. c B. Möller { 16 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) class Pair { public int value ; public int best_move ; Pair (int v, int m) { value = v ; best_move = m ; } Konkretisierung in unserem Spiel: • 0 f ur unentshieden (remis) • 1 Computer gewinnt (comp win) } • -1 Mensh gewinnt (comp loss) Damit ergibt sih unser erstes Programm fur Ti-Ta-Toe: class MyTicTac { public static int comp = 1 ; public static int human = -1 ; static int free = 0 ; static Pair undecided = new Pair(0,-1) ; c B. Möller { 17 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) static int remis static int comp_win static int comp_loss Informatik III WS 2011/12 Backtracking (Zurücksetzen) = 0 ; = 1 ; = -1 ; int best_move = start ; for (int i=start ; i < 9 ; i++) // 9 ist die Felderzahl if (is_empty(i)) { place(i, player) ; int response = find_move(opponent(player)).value ; if (better(player, response, value)) { value = response ; // bisher besten Zug best_move = i ; // verbessern } unplace(i); // Brett wiederherstellen } return new Pair(value,best_move) ; int board [] = {free,free,free,free,free,free,free,free,free} ; int filled = 0; public Pair find_move (int player) { if (board_full()) return undecided ; else { Pair p = test_immediate_win(player) ; if (p.value == win(player)) return p ; else { int value = loss(player) ; int start = first_free() ; c B. Möller { 18 { c B. Möller { 19 { } } } // Initialisierung // fuer Extremumssuche Informatik III WS 2011/12 c B. Möller { 20 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) boolean better (int player, int response, int value) { return (player == comp)?(response > value):(response < value) ; } boolean board_full () { return filled == 9 ; } static int opponent (int player) { return -player ; } int first_free () { for (int i = 0 ; i < 9 ; i++) if (is_empty(i)) return i ; return -1 ; } static int win (int player) { return (player==comp)?comp_win:comp_loss ; } int loss (int player) { return (player==comp)?comp_loss:comp_win ; } c B. Möller { 21 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) boolean is_empty (int i) { return board[i] == free ; } c B. Möller { 22 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) void place (int pos, int player) { board[pos] = player ; filled++ ; } else if (board[pos1] == free && board[pos2] == player && board[pos3] == player) return new Pair(win(player),pos1) ; else return undecided ; } void unplace (int pos) { board[pos] = free ; filled-- ; } Pair can_complete (int player, int pos1, int pos2, int pos3) { if (board[pos1] == player && board[pos2] == player && board[pos3] == free) return new Pair(win(player),pos3) ; else if (board[pos1] == player && board[pos2] == free && board[pos3] == player) return new Pair(win(player),pos2) ; c B. Möller { 23 { Informatik III WS 2011/12 Pair test_immediate_win (int player) { Pair p = can_complete(player,0,1,2) ; if (p.value == win(player)) return p ; p = can_complete(player,3,4,5) ; if (p.value == win(player)) return p ; p = can_complete(player,6,7,8) ; if (p.value == win(player)) return p ; p = can_complete(player,0,3,6) ; if (p.value == win(player)) return p ; p = can_complete(player,1,4,7) ; if (p.value == win(player)) return p ; c B. Möller { 24 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) p = can_complete(player,2,5,8) ; if (p.value == win(player)) return p ; p = can_complete(player,0,4,8) ; if (p.value == win(player)) return p ; return can_complete(player,2,4,6) ; for (int i = 0 ; i < 9 ; i++) { p = tt.find_move(player) ; } System.out.print("player: "+player+" best move: "+ p.best_move+"\n") ; public void print_board() { System.out.print(board[0]+" "+board[1]+" "+board[2]+" "+"\n") ; System.out.print(board[3]+" "+board[4]+" "+board[5]+" "+"\n") ; System.out.print(board[6]+" "+board[7]+" "+board[8]+" "+"\n") ; } tt.place(p.best_move, player) ; player = opponent(player) ; } tt.print_board() ; } public static void main (String argv []) { MyTicTac tt = new MyTicTac() ; int player = comp ; Pair p = undecided ; c B. Möller { 25 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) c B. Möller { 26 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) • F ur Ti-Ta-Toe ist bei optimalem Spiel beiderseits stets Remis garantiert. • Daher wird als Startfeld des Rehners Feld 01 berehnet. • Dazu werden 97162 Stellungen untersuht. • Wenn der Mensh als erster am Zug ist, so werden 5185 Stellun- gen gepruft, 9761 Stellungen, wenn er das Mittelfeld wahlt, und fur ein Ekfeld 13233. c B. Möller } { 27 { Informatik III WS 2011/12 • F ur komplexere Spiele ist also der naive Minmax-Ansatz oenbar niht mehr durhfuhrbar. • Dort muss die Rekursion nah einer bestimmten Tiefe gestoppt werden, um dann auf eine genauere Bewertungsfunktion zurukzugreifen. • Die G ute eines Spielprogramms wird von der Rekursionstiefe (Zugzahl der Vorausberehnung) und der Genauigkeit der Bewertungsfunktion bestimmt. c B. Möller { 28 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) Wie lasst sih (zunahst unabhangig von der Bewertungsfunktion) die Vorausshautiefe verbessern? • Fasse Stellungen zu Klassen zusammen. • Tabelliere bereits untersuhte Stellungen mit ihren Werten und vermeide Neuberehnungen. • Eine solhe Tabelle heit Transpositionstabelle; sie wird meist durh Hashing implementiert. Beispiel: Endspiele im Shah: Man hat nur noh wenige Figuren, daher werden haug gleihe Stellungen erreiht. c B. Möller { 29 { ⊓ ⊔ Informatik III WS 2011/12 Backtracking (Zurücksetzen) 10.2.2 α-β-Beschneidung (Pruning) Idee: Sheide Nahfolgestellungen aus der Untersuhung aus, wenn aufgrund der Vorgeshihte klar ist, dass sie keine Verbesserung bringen. Insbesondere konnte man in obigem Programm in der for-Shleife von find move die Abbruhbedingung verbessern zu i < 9 && better(player,win(player),value). Wir ordnen dies aber einem allgemeineren Ansatz unter. { 30 { c B. Möller Informatik III WS 2011/12 Backtracking (Zurücksetzen) • Nun stehe Knoten x mit seinem Teilgraphen zur Untersuhung Wir betrahten Spielgraphen, deren Knoten mit allgemeinen ganzen Zahlen markiert sind. an: • Wir bewerten zunahst eine Stellung, d.h. einen Knoten, in dem der Rehner am Zug ist. • Dieser Knoten soll als Wert das Maximum der Werte seiner unmittelbaren Teilgraphen erhalten. • Jeder Wurzelknoten eines solhen hat als Wert das Minimum der Werte seiner unmittelbaren Teilgraphen. • Die bisher ermittelten Approximationen der exakten Werte seien an den Knoten angetragen. c B. Möller { 31 { Informatik III WS 2011/12 ≥ 44 RR Max RRR vvv ≤ 40 44 9 Min {{ @@ 9999 40 II w x 22 99 I ww • Da der linke Nahbar von x bereits den Wert 40 hat, kann das zu ermittelnde Minimum niht groer sein, tragt also zur oberhalb stattndenden Maximumsbildung nihts bei. • Damit ist der Wert von x unerheblih, brauht also niht berehnet zu werden. Die α-Beshneidung vermeidet gerade die Berehnung solher niht benotigter Maximumsknoten. c B. Möller { 32 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) Die β-Beshneidung verlauft symmetrish fur Minimumsknoten wie y im folgenden Graphen: ≤ 30 RR Min RRR vvv ≥ 53 Max 30 9 {{ @@ 9999 53 II w y 3 99 I ww c B. Möller { 33 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) • Dies lasst sih nur mit erheblihem Aufwand einheitlih nah dem Spieler parametrisiert formulieren. • Daher sind hier die Suhmethoden f ur den Rehner und den Menshen getrennt ausprogrammiert. c B. Möller { 34 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) class Pair { public int value ; public int best_move ; static int remis static int comp_win static int comp_loss = 0 ; = 1 ; = -1 ; int board [] = {free,free,free,free,free,free,free,free,free} ; int filled = 0; Pair (int v, int m) { value = v ; best_move = m ; } public Pair find_comp_move (int alpha, int beta) { if (board_full()) return undecided ; else { Pair p = test_immediate_win(comp) ; if (p.value == comp_win) return p ; else { int value = alpha ; // Initialisierung int start = first_free() ; // fuer Maximumssuche } class PruneTicTac { public static int comp = 1 ; public static int human = -1 ; static int free = 0 ; static Pair undecided = new Pair(0,-1) ; c B. Möller Wir verbessern nun unser Ti-Ta-Toe-Programm um die α-β-Beshneidung. { 35 { Informatik III WS 2011/12 c B. Möller { 36 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) int best_move = start ; for (int i=start ; i < 9 && value < beta ; i++) if (is_empty(i)) { place(i, comp) ; int response = find_human_move(value,beta).value ; if (response > value) { value = response ; // bisher besten Zug best_move = i ; // verbessern } unplace(i); // Brett wiederherstellen } return new Pair(value,best_move) ; } } } { 37 { c B. Möller Informatik III WS 2011/12 Backtracking (Zurücksetzen) c B. Möller { 38 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) if (response < value) { value = response ; best_move = i ; } unplace(i); } return new Pair(value,best_move) ; // bisher besten Zug // verbessern // Brett wiederherstellen } int first_free () { for (int i = 0 ; i < 9 ; i++) if (is_empty(i)) return i ; return -1 ; } boolean is_empty (int i) { return board[i] == free ; } } } boolean board_full () { return filled == 9 ; } c B. Möller public Pair find_human_move (int alpha, int beta) { if (board_full()) return new Pair(remis,-1) ; else { Pair p = test_immediate_win(human) ; if (p.value == comp_loss) return p ; else { int value = beta ; // Initialisierung int start = first_free() ; // fuer Minimumssuche int best_move = start ; for (int i=start ; i < 9 && value > alpha ; i++) if (is_empty(i)) { place(i, human) ; int response = find_comp_move(alpha,value).value ; void place (int pos, int player) { board[pos] = player ; filled++ ; } { 39 { Informatik III WS 2011/12 c B. Möller { 40 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) void unplace (int pos) { board[pos] = free ; filled-- ; } static int win (int player) { return (player==comp)?comp_win:comp_loss ; } int loss (int player) { return (player==comp)?comp_loss:comp_win ; } Pair can_complete (int player, int pos1, int pos2, int pos3) { if (board[pos1] == player && board[pos2] == player && board[pos3] == free) return new Pair(win(player),pos3) ; c B. Möller { 41 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) } Pair test_immediate_win (int player) { Pair p = can_complete(player,0,1,2) ; if (p.value == win(player)) return p ; p = can_complete(player,3,4,5) ; if (p.value == win(player)) return p ; p = can_complete(player,6,7,8) ; if (p.value == win(player)) return p ; p = can_complete(player,0,3,6) ; c B. Möller { 42 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) if (p.value == win(player)) return p p = can_complete(player,1,4,7) ; if (p.value == win(player)) return p p = can_complete(player,2,5,8) ; if (p.value == win(player)) return p p = can_complete(player,0,4,8) ; if (p.value == win(player)) return p return can_complete(player,2,4,6) ; ; ; ; ; } public void print_board() { System.out.print(board[0]+" "+board[1]+" "+board[2]+" "+"\n") ; System.out.print(board[3]+" "+board[4]+" "+board[5]+" "+"\n") ; System.out.print(board[6]+" "+board[7]+" "+board[8]+" "+"\n") ; } c B. Möller else if (board[pos1] == player && board[pos2] == free && board[pos3] == player) return new Pair(win(player),pos2) ; else if (board[pos1] == free && board[pos2] == player && board[pos3] == player) return new Pair(win(player),pos1) ; else return undecided ; { 43 { Informatik III WS 2011/12 public static void main (String argv []) { PruneTicTac tt = new PruneTicTac() ; Pair p = undecided ; int aux = comp_win ; while (!tt.board_full()) { p = tt.find_comp_move(comp_loss,aux) ; System.out.print("player: "+comp+" best move: "+ p.best_move+"\n") ; tt.place(p.best_move,comp) ; if (tt.board_full()) break ; aux = p.value ; p = tt.find_human_move(aux,comp_win) ; System.out.print("player: "+human+" best move: "+ p.best_move+"\n") ; c B. Möller { 44 { Informatik III WS 2011/12 Backtracking (Zurücksetzen) Backtracking (Zurücksetzen) Weitere Verbesserungen ergeben sih, indem man • Nahfolgestellungen in anderer Reihenfolge durhsuht, gema einer weiteren Bewertungsfunktion, • bei gewissen Stellungen weiter in die Tiefe suht als bei anderen. tt.place(p.best_move,human) ; aux = p.value ; • Praktish werden bei α-β-Beshneidung statt O(n) Knoten nur √ noh O( n) Knoten untersuht. } tt.print_board() ; } } Beispiel 10.2.2 (Tic-Tac-Toe) Zieht der Computer am Anfang, so reduziert sih die Zahl der Berehnungen/moglihen Stellungen von 97162 auf 4493. c B. Möller { 45 { Informatik III WS 2011/12 c B. Möller { 46 { Informatik III WS 2011/12
© Copyright 2024 ExpyDoc