C99 - 3x3 Platine in 0.084s
Bearbeiten: Ich habe meinen Code überarbeitet und die Ergebnisse eingehender analysiert.
Weitere Änderungen: Beschneiden nach Symmetrien hinzugefügt. Dies ermöglicht 4 Algorithmuskonfigurationen: mit oder ohne Symmetrien X mit oder ohne Alpha-Beta-Bereinigung
Weiteste Änderungen: Memoisierung mit einer Hash-Tabelle hinzugefügt, um endlich das Unmögliche zu erreichen: Lösen eines 3x3-Brettes!
Hauptmerkmale:
- Einfache Implementierung von Minimax mit Alpha-Beta-Bereinigung
- sehr wenig Speicherverwaltung (behält DLL der gültigen Bewegungen bei; O (1) Aktualisierungen pro Zweig in der Baumsuche)
- zweite Datei mit Beschneiden nach Symmetrien. Erreicht weiterhin O (1) Aktualisierungen pro Zweig (technisch O (S), wobei S die Anzahl der Symmetrien ist. Dies ist 7 für quadratische Karten und 3 für nicht quadratische Karten)
- Die dritte und vierte Datei fügen Memoization hinzu. Sie haben die Kontrolle über die Größe der Hashtabelle (
#define HASHTABLE_BITWIDTH). Wenn diese Größe größer oder gleich der Anzahl der Wände ist, werden keine Kollisionen und O (1) -Updates garantiert. Kleinere Hashtabellen haben mehr Kollisionen und sind etwas langsamer.
- Kompilieren Sie mit
-DDEBUGfür Ausdrucke
Mögliche Verbesserungen:
Behebung eines kleinen Speicherverlusts bei der ersten Bearbeitung
Alpha / Beta-Beschneidung in der 2. Bearbeitung hinzugefügt
Prune Symmetrien in der 3. bearbeiten hinzugefügt (beachten Sie, dass Symmetrien sind nicht von memoization behandelt, so dass eine separate Optimierung bleibt.)
Memoisierung in der 4. Bearbeitung hinzugefügt
- Derzeit verwendet Memoization ein Anzeigebit für jede Wand. Ein 3x4-Board hat 31 Wände, sodass diese Methode trotz zeitlicher Einschränkungen keine 4x4-Boards verarbeiten kann. Die Verbesserung wäre, X-Bit-Ganzzahlen zu emulieren, wobei X mindestens so groß ist wie die Anzahl der Wände.
Code
Aufgrund mangelnder Organisation ist die Anzahl der Dateien unvorbereitet gewachsen. Der gesamte Code wurde in dieses Github-Repository verschoben . In der Memoization-Bearbeitung habe ich ein Makefile und ein Testskript hinzugefügt.
Ergebnisse

Hinweise zur Komplexität
Brute-Force-Ansätze für Punkte und Kästchen nehmen sehr schnell an Komplexität zu .
Betrachten Sie eine Tafel mit RZeilen und CSpalten. Es gibt R*CQuadrate, R*(C+1)vertikale Wände und C*(R+1)horizontale Wände. Das ist insgesamt W = 2*R*C + R + C.
Da Lembik uns gebeten hat , das Spiel mit Minimax zu lösen , müssen wir zu den Blättern des Spielbaums gehen. Lassen Sie uns das Beschneiden zunächst ignorieren, denn es kommt auf Größenordnungen an.
Es gibt WOptionen für den ersten Zug. Für jede davon kann der nächste Spieler eine der W-1verbleibenden Wände spielen, usw .. Das gibt uns einen Suchraum von SS = W * (W-1) * (W-2) * ... * 1, oder SS = W!. Factorials sind riesig, aber das ist nur der Anfang. SSist die Anzahl der Blattknoten im Suchraum. Für unsere Analyse relevanter ist die Gesamtzahl der zu treffenden Entscheidungen (dh die Anzahl der Zweige B im Baum). Die erste Schicht von Zweigen hat WOptionen. Für jeden von denen hat der nächste Level W-1usw.
B = W + W*(W-1) + W*(W-1)*(W-2) + ... + W!
B = SUM W!/(W-k)!
k=0..W-1
Schauen wir uns einige kleine Tischgrößen an:
Board Size Walls Leaves (SS) Branches (B)
---------------------------------------------------
1x1 04 24 64
1x2 07 5040 13699
2x2 12 479001600 1302061344
2x3 17 355687428096000 966858672404689
Diese Zahlen werden lächerlich. Zumindest erklären sie, warum der Brute-Force-Code für immer auf einem 2x3-Board zu hängen scheint. Der Suchraum einer 2x3-Karte ist 742560-mal größer als 2x2 . Wenn die Ausführung von 2x2 20 Sekunden dauert, sagt eine konservative Extrapolation eine Ausführungszeit von über 100 Tagen für 2x3 voraus . Natürlich müssen wir beschneiden.
Schnittanalyse
Zunächst habe ich mit dem Alpha-Beta-Algorithmus einen sehr einfachen Schnitt hinzugefügt. Im Grunde hört es auf zu suchen, ob ein idealer Gegner ihm niemals seine gegenwärtigen Möglichkeiten geben würde. "Hey schau - ich gewinne um ein Vielfaches, wenn mein Gegner mich jedes Feld holen lässt!", Dachte keine KI.
edit Ich habe auch einen Schnitt hinzugefügt, der auf symmetrischen Brettern basiert. Ich verwende keinen Memoisierungsansatz, nur für den Fall, dass ich eines Tages Memoisierung hinzufüge und diese Analyse getrennt halten möchte. Stattdessen funktioniert es so: Die meisten Linien haben ein "symmetrisches Paar" an einer anderen Stelle im Raster. Es gibt bis zu 7 Symmetrien (horizontal, vertikal, 180-Drehung, 90-Drehung, 270-Drehung, Diagonale und die andere Diagonale). Alle 7 gelten für quadratische Bretter, die letzten 4 gelten jedoch nicht für nicht quadratische Bretter. Jede Wand hat für jede dieser Symmetrien einen Zeiger auf das "Paar". Wenn das Brett in einer Runde horizontal symmetrisch ist, muss nur eines von jedem horizontalen Paar gespielt werden.
bearbeiten bearbeiten Memoization! Jede Wand erhält eine eindeutige ID, die ich bequem als Indikatorbit festgelegt habe. Die n-te Wand hat die ID 1 << n. Der Hash eines Bretts ist also nur das ODER aller gespielten Wände. Dies wird bei jeder Verzweigung zu O (1) -Zeit aktualisiert. Die Größe der Hashtabelle wird in a festgelegt #define. Alle Tests wurden mit Größe 2 ^ 12 durchgeführt, warum nicht? Wenn mehr Wände als Bits vorhanden sind, die die Hash-Tabelle indizieren (in diesem Fall 12 Bits), werden die niederwertigsten 12 maskiert und als Index verwendet. Kollisionen werden mit einer verknüpften Liste an jedem Hashtable-Index behandelt. Das folgende Diagramm zeigt, wie sich die Größe der Hashtabelle auf die Leistung auswirkt. Auf einem Computer mit unbegrenztem RAM haben wir die Größe des Tisches immer auf die Anzahl der Wände eingestellt. Eine 3x4-Karte hätte eine Hash-Tabelle mit einer Länge von 2 ^ 31. Leider haben wir diesen Luxus nicht.

Ok, zurück zum Beschneiden. Indem wir die Suche hoch im Baum stoppen, können wir viel Zeit sparen, indem wir nicht zu den Blättern hinuntergehen. Der 'Pruning Factor' ist der Bruchteil aller möglichen Zweige, die wir besuchen mussten. Brute-Force hat einen Schnittfaktor von 1. Je kleiner es ist, desto besser.

