Kannst du das Acht-Königinnen-Rätsel beim Kompilieren lösen ?
Wählen Sie ein geeignetes Ausgabeformat.
Ich bin besonders an einer Metaprogrammierungslösung für C ++ - Vorlagen interessiert, aber Sie können Sprachen verwenden, die ähnliche Konstrukte haben, wie zum Beispiel das Typensystem von Haskell.
Idealerweise würde Ihr Metaprogramm alle Lösungen ausgeben. Keine Hardcodierung.
puzzle-solver
compile-time
R. Martinho Fernandes
quelle
quelle
Antworten:
Mein Metaprogramm findet alle 92 Lösungen. Sie werden als Fehlermeldungen ausgegeben:
Dies bedeutet, dass die erste Dame bei y = 1, die zweite bei y = 5, die dritte bei y = 8 usw. platziert werden sollte.
Zunächst einige nützliche Metafunktionen:
Dann zwei interessante Metafunktionen (beachten Sie den Singular und den Plural):
Die Variable
queens
speichert die y-Koordinaten der bisher auf der Tafel platzierten Damen. Die folgenden drei Variablen speichern die Zeilen und Diagonalen, die bereits von Königinnen belegt sind.x
undy
sollte selbsterklärend sein.Das erste Argument, um zu
if_then_else
prüfen, ob die aktuelle Position gesperrt ist. Wenn dies der Fall ist, stoppt die Rekursion, indem das (bedeutungslose) Ergebnis 0 zurückgegeben wird. Andernfalls wird die Dame auf die Tafel gelegt und der Vorgang wird mit der nächsten Spalte fortgesetzt.Wenn x 8 erreicht, haben wir eine Lösung gefunden:
Da die
print
Vorlage kein Mitglied hatsolution
, generiert der Compiler einen Fehler.Um den Prozess zu starten, inspizieren wir schließlich das
value
Mitglied der leeren Tafel:Das komplette Programm finden Sie bei ideone .
quelle
Ich habe eine Lösung gefunden, die das Haskell-System verwendet. Ich habe ein wenig nach einer vorhandenen Lösung für das Problem auf der Wertebene gegoogelt , sie ein wenig geändert und sie dann auf die Typebene angehoben. Es bedurfte einer Menge Neuerfindungen. Ich musste auch eine Reihe von GHC-Erweiterungen aktivieren.
Erstens, da Ganzzahlen auf Typebene nicht zulässig sind, musste ich die natürlichen Zahlen noch einmal neu erfinden, diesmal als Typen:
Der Algorithmus, den ich angepasst habe, addiert und subtrahiert Naturals, daher musste ich diese ebenfalls neu erfinden. Funktionen auf Typebene werden mit Rückgriff auf Typklassen definiert. Dies erfordert die Erweiterungen für mehrere Parametertypklassen und funktionale Abhängigkeiten. Typklassen können keine "Werte zurückgeben", daher verwenden wir einen zusätzlichen Parameter, ähnlich wie bei PROLOG.
Die Rekursion wird mit Zusicherungen von Klassen implementiert, sodass die Syntax etwas rückständig wirkt.
Als nächstes waren Booleaner:
Und eine Funktion zum Vergleichen von Ungleichungen:
Und Listen ...
if
s fehlen auch auf der Typebene ...Und damit waren alle unterstützenden Maschinen, die ich benutzte, an Ort und Stelle. Zeit, das Problem selbst anzugehen!
Beginnen Sie mit einer Funktion, um zu testen, ob das Hinzufügen einer Dame zu einem vorhandenen Board in Ordnung ist:
Beachten Sie die Verwendung von Class Assertions, um Zwischenergebnisse zu erhalten. Da die Rückgabewerte tatsächlich ein zusätzlicher Parameter sind, können wir die Zusicherungen nicht einfach direkt voneinander aufrufen. Wenn Sie PROLOG bereits verwendet haben, ist Ihnen dieser Stil möglicherweise ein wenig vertraut.
Nachdem ich einige Änderungen vorgenommen hatte, um die Notwendigkeit von Lambdas zu beseitigen (die ich hätte implementieren können, aber ich beschloss, für einen anderen Tag zu gehen), sah die ursprüngliche Lösung folgendermaßen aus:
map
ist eine Funktion höherer Ordnung. Ich dachte, das Implementieren von Meta-Funktionen höherer Ordnung wäre zu mühsam (wieder Lambdas), also entschied ich mich für eine einfachere Lösung: Da ich weiß, welche Funktionen abgebildet werden, kann ichmap
für jede spezielle Versionen implementieren , so dass dies nicht der Fall ist Funktionen höherer Ordnung.Und die letzte Meta-Funktion kann jetzt geschrieben werden:
Alles, was bleibt, ist eine Art Fahrer, der die Maschinen zur Typprüfung überredet, die Lösungen zu erarbeiten.
Dieses Metaprogramm soll auf dem Type Checker laufen, so dass man starten und nach dem Typ
ghci
fragen kannqueens eight
:Dies wird die Standard-Rekursionsgrenze ziemlich schnell überschreiten (es ist eine dürftige 20). Um dieses Limit zu erhöhen, müssen wir
ghci
mit der-fcontext-stack=N
Option aufrufen , woN
die gewünschte Stapeltiefe ist (N = 1000 und 15 Minuten sind nicht genug). Ich habe diesen Lauf noch nicht vollständig gesehen, da er sehr lange dauert, aber ich habe es geschafft, ihn zu erreichenqueens four
.Es gibt ein volles Programm auf ideone mit einigen Maschinen zum hübschen Drucken der Ergebnistypen, aber es
queens two
kann nur ausgeführt werden, ohne die Grenzen zu überschreiten :(quelle
C über den Präprozessor
Ich denke, das ANSI-Komitee hat sich bewusst dafür entschieden, den C-Präprozessor nicht so weit zu erweitern, dass er vollständig ist. In jedem Fall ist es nicht stark genug, um das Acht-Königinnen-Problem zu lösen. Nicht in irgendeiner allgemeinen Weise.
Dies ist jedoch möglich, wenn Sie bereit sind, die Schleifenzähler fest zu codieren. Natürlich gibt es keine echte Möglichkeit, eine Schleife zu erstellen, aber Sie können die Selbsteinbeziehung (über
#include __FILE__
) verwenden, um eine begrenzte Art von Rekursion zu erhalten.Trotz der schrecklichen Menge an sich wiederholenden Inhalten kann ich Ihnen versichern, dass das Acht-Königinnen-Problem damit wirklich algorithmisch gelöst wird. Leider konnte ich mit dem Präprozessor nur eine allgemeine Push-Down-Stack-Datenstruktur implementieren. Das Ergebnis ist, dass ich den Wert von dort,
i
wo er verwendet wurde , fest codieren musste , um einen anderen einzustellenden Wert auszuwählen. (Im Gegensatz zum Abrufen von Werten, was ganz allgemein möglich ist. Aus diesem Grund musste der#if
obere Bereich der Datei, der darüber entscheidet, ob eine Dame an der aktuellen Position hinzugefügt werden kann, nicht acht Mal wiederholt werden.)Im Präprozessor Code,
i
undj
zeigt die aktuelle Position in Betracht gezogen werden, währendr
,p
undn
zu verfolgen, welche Reihen und Diagonalen sind derzeit nicht verfügbar für die Platzierung. Doppelt sich jedochi
auch als Indikator für die aktuelle Rekursionstiefe, sodass in Wirklichkeit alle anderen Werte i als eine Art Index verwenden, sodass ihre Werte erhalten bleiben, wenn eine Rekursion fortgesetzt wird. (Und auch wegen der ernsthaften Schwierigkeit, den Wert eines Präprozessorsymbols zu ändern, ohne es vollständig zu ersetzen.)Das kompilierte Programm druckt alle 92 Lösungen. Die Lösungen sind direkt in die ausführbare Datei eingebettet. Die Ausgabe des Präprozessors sieht folgendermaßen aus:
Es kann getan werden, auch wenn es eindeutig nicht sollte.
quelle
Hier ist eine C ++ 11-Lösung ohne Vorlagen:
Die Lösung wird wie in FredOverflows Antworten als Dezimalziffern codiert. GCC 4.7.1 kompiliert die obige Datei in die folgende Assembly-Quelle mit
g++ -S -std=c++11 8q.cpp
:Der Wert des Symbols
places
ist 84136275, dh die erste Dame ist bei a8, die zweite bei b4 usw.quelle
c ++ template, wobei nur eine Template-Klasse definiert ist:
Die Fehlermeldung sieht dann so aus:
Fehler C2440: 'Typumwandlung': Umwandlung von 'int' in 'char [15863724]' nicht möglich
quelle