Gewinner gefunden
Es scheint, als hätten wir einen Gewinner! Sofern niemand vorhat, den derzeit schnellsten Sudoku-Löser der Welt herauszufordern, gewinnt Benutzer 53x15 mit dem erstaunlich schnellen Löser Tdoku. Für alle, die noch an ihren Solvern arbeiten, vergleiche ich immer noch neue Übermittlungen, wenn ich Zeit habe.
Die Herausforderung
Das Ziel eines Sudoku-Spiels ist es, das Spielfeld mit den Nummern 1-9 zu füllen, eine in jeder Zelle, so dass jede Zeile, Spalte und Box nur einmal jede Nummer enthält. Ein sehr wichtiger Aspekt eines Sudoku-Puzzles ist, dass es nur eine gültige Lösung geben sollte.
Das Ziel dieser Herausforderung ist einfach: Sie sollten so schnell wie möglich eine Reihe von Sudoku-Rätseln lösen. Sie werden jedoch nicht nur ein altes Sudoku lösen, sondern auch die schwierigsten Sudoku-Rätsel, die es gibt, die 17-Ahnung-Sudokus. Hier ist ein Beispiel:
Regeln
Sprache
Sie können jede Sprache verwenden. Wenn für Ihre Sprache kein Compiler installiert ist, sollten Sie in der Lage sein, eine Reihe von Befehlszeilenanweisungen bereitzustellen, die zum Installieren einer Umgebung erforderlich sind, in der Ihr Skript unter Linux ausgeführt werden kann .
Benchmark-Maschine
Der Benchmark wird auf einem Dell XPS 9560 mit 2,8 GHz Intel Core i7-7700HQ (3,8 GHz Boost), 4 Kernen, 8 Threads und 16 GB RAM ausgeführt. GTX 1050 4GB. Auf der Maschine läuft Ubuntu 19.04. Hier ist die uname
Ausgabe für alle Interessierten.
Linux 5.0.0-25-generic #26-Ubuntu SMP Thu Aug 1 12:04:58 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Eingang
Die Eingabe erfolgt als Datei. Es kann hier gefunden werden . Die Datei enthält 49151 Sudoku-Rätsel. Die erste Zeile der Datei gibt die Anzahl der Rätsel an. Jede Zeile danach ist 81 Zeichen lang und steht für ein Rätsel. Die unbekannten Zellen sind 0
und die bekannten Zellen sind 1-9
.
Ihr Programm sollte in der Lage sein, den Dateinamen als Argument zu verwenden oder die Datei von STDIN einzugeben , um die manuelle Überprüfung Ihrer Lösung zu erleichtern. Bitte geben Sie an, wie Ihr Programm Eingaben macht.
Timing / Wertung
Aufgrund von Diskussionen in den Kommentaren und einigen Überlegungen wurde das Bewertungskriterium so geändert, dass es die Zeit Ihres gesamten Programms ist. Ihr Programm sollte die Ausgabedatei auch während des offiziellen Scorings mit dem richtigen Hash erzeugen. Dies beeinträchtigt keine bestehende Lösung und ändert nichts an den aktuellen Platzierungen. Alle Gedanken zum Punktesystem sind willkommen.
Wenn zwei Lösungen für einzelne Läufe ähnliche Ergebnisse erzielen, führe ich mehrere Benchmarks durch und die durchschnittliche Zeit ist das Endergebnis. Wenn sich die Durchschnittswerte um weniger als 2% unterscheiden, halte ich das für ein Unentschieden.
Wenn die Ausführung Ihrer Lösung länger als eine Stunde dauert, wird sie nicht offiziell bewertet. In diesen Fällen sind Sie dafür verantwortlich, den Computer, auf dem er ausgeführt wurde, und Ihre Punktzahl zu melden. Für einen optimierten Solver sollte dies kein Problem sein.
EDIT : Ich wurde darauf aufmerksam gemacht, dass das anstehende Problem zwar schwierig, aber nicht das schwierigste ist, das es gibt. Wenn Zeit zur Verfügung steht, werde ich versuchen, die hier vorgestellten Lösungen mit dem schwierigeren Puzzlesatz zu vergleichen und die Punktzahl zu jeder Einreichung hinzuzufügen. Dies wird jedoch keine offizielle Wertung sein und ist nur zum Spaß.
Nachprüfung
Ihre Lösung wird durch eine MD5 / SHA256-Prüfsumme verifiziert. Ihr Skript sollte in der Lage sein, eine Datei zu generieren, die alle Rätsel und ihre Lösungen enthält. Die Datei wird jedoch auch manuell überprüft. Versuchen Sie also nicht, eine Hash-Kollision zu erhalten. Ihre Ausgabedatei sollte übereinstimmen mit:
MD5: 41704fd7d8fd0723a45ffbb2dbbfa488
SHA256:0bc8dda364db7b99f389b42383e37b411d9fa022204d124cb3c8959eba252f05
Die Datei hat das Format:
<num_puzzles>
<unsolved_puzzle#1>,<solved_puzzle#1>
<unsolved_puzzle#2>,<solved_puzzle#2>
...
<unsolved_puzzle#n>,<solved_puzzle#n>
mit einem einzigen nachgestellten Zeilenumbruch.
Was ist nicht erlaubt
Es ist Ihnen in keiner Weise gestattet, Lösungen hart zu codieren . Ihr Algorithmus sollte auf alle Sudoku-Rätsel anwendbar sein, sowohl auf einfache als auch auf härtere Sudokus. Es ist jedoch völlig in Ordnung, wenn Ihre Lösung für einfachere Rätsel langsam ist.
Sie dürfen kein nicht deterministisches Programm haben . Sie können einen Zufallszahlengenerator verwenden, der Startwert des Generators sollte jedoch festgelegt sein. Diese Regel soll sicherstellen, dass die Messungen präziser sind und eine geringere Varianz aufweisen. (Danke an Peter Taylor für den Tipp)
Während der Laufzeit Ihres Programms dürfen Sie keine externen Ressourcen oder Webanforderungen verwenden. Alles sollte in sich geschlossen sein. Dies gilt nicht für installierte Bibliotheken und Pakete, die zulässig sind.
Andere Information
Wenn Sie möchten, dass ein anderer Testsatz Ihre Lösung überprüft, finden Sie hier 10000 einfachere Sudokus . Hier sind ihre Lösungen .
MD5: 3cb465ef6077c4fcab5bd6ae3bc50d62
SHA256:0bc8dda364db7b99f389b42383e37b411d9fa022204d124cb3c8959eba252f05
Wenn Sie Fragen haben, können Sie diese gerne stellen, und ich werde versuchen, etwaige Missverständnisse zu klären.
Antworten:
C ++ - 0.201s offizielle Punktzahl
Die Verwendung von Tdoku ( Code ; Design ; Benchmarks ) führt zu folgenden Ergebnissen:
Tdoku wurde für harte Sudoku-Instanzen optimiert. Beachten Sie jedoch, dass im Gegensatz zur Problemstellung 17 Hinweis-Rätsel weit vom schwierigsten Sudoku entfernt sind. Tatsächlich gehören sie zu den einfachsten, wobei die meisten überhaupt kein Zurückverfolgen erfordern. Sehen Sie sich einige der anderen Benchmark-Datensätze im Tdoku-Projekt an, um herauszufinden, welche Rätsel tatsächlich schwierig sind.
Beachten Sie auch, dass Tdoku für harte Rätsel zwar der schnellste Löser ist, für 17 Hinweis-Rätsel jedoch nicht der schnellste. Für diese denke ich, ist dieses Rostprojekt, ein Derivat von JCZSolve, das während der Entwicklung für 17 Hinweisrätsel optimiert wurde , das schnellste . Abhängig von der Plattform ist es bei diesen Rätseln möglicherweise 5-25% schneller als Tdoku.
quelle
Node.js ,
8.231s6.735s offizielle PunktzahlNimmt den Dateinamen als Argument. Die Eingabedatei enthält möglicherweise bereits die Lösungen in dem in der Abfrage beschriebenen Format. In diesem Fall vergleicht das Programm sie mit seinen eigenen Lösungen.
Die Ergebnisse werden in 'sudoku.log' gespeichert .
Code
Beispielausgabe
Getestet auf einem Intel Core i7 7500U bei 2,70 GHz.
quelle
Python 3 (mit dlx ) 4min 46.870s offizielle Punktzahl
(Single Core i7-3610QM hier)
Natürlich mit einer kompilierten Sprache wie C zu schlagen und Threading zu verwenden, aber es ist ein Anfang ...
sudoku
ist ein Modul, das ich auf Github platziert habe (kopiert in der Fußzeile dieses Beitrags) und dasdlx
unter der Haube verwendet wird.Verwendung
sudoku.py
irgendwo auf deinem Pfad (über den Git Hub Link oder kopiere ihn von unten)testSolver.py
irgendwo auf Ihrem PfadPipe-Ausgabe wie in der Abfragespezifikation erforderlich in eine Datei, falls erforderlich:
sudoku.py (ja, hier gibt es außer dem Lösen noch weitere Funktionen)
quelle
Python 3 + Z3 - 10min 45.657s offizielles Ergebnis
ungefähr 1000s auf meinem Laptop.
Installieren Sie die Abhängigkeit
Lauf
Ich bin mir nicht sicher, wie ich seine Leistung verbessern soll, da es einfach magisch gelöst wurde ...
quelle
C -
2.228s1.690s offizielle Punktzahlbasierend auf @ Arnauld's
kompilieren und ausführen:
quelle
C - 12min 28.374s offizielles Ergebnis
läuft für ca.
30m15m auf meinem i5-7200U und erzeugt den richtigen md5 Hashkompiliere (vorzugsweise mit clang v6) und führe aus:
quelle
memcpy
und einige Rekursionen. Ich werde heute versuchen, es zu überprüfen.Java - 4.056s offizielles Ergebnis
Die Hauptidee dabei ist, niemals Speicher zuzuweisen, wenn er nicht benötigt wird. Die einzige Ausnahme sind Primitive, die vom Compiler ohnehin optimiert werden sollten. Alles andere wird in Form von Masken und Anordnungen von Vorgängen gespeichert, die in jedem Schritt ausgeführt werden. Dies kann rückgängig gemacht werden, wenn der Rekursionsschritt abgeschlossen ist.
Etwa die Hälfte aller Sudokus werden vollständig ohne Zurückverfolgung gelöst, aber wenn ich diese Zahl höher lege, scheint die Gesamtzeit langsamer zu sein. Ich plane, dies in C ++ umzuschreiben und noch weiter zu optimieren, aber dieser Solver wird zu einem Giganten.
Ich wollte so viel Caching wie möglich implementieren, was zu einigen Problemen führte. Wenn sich zum Beispiel zwei Zellen in derselben Zeile befinden, die nur die Nummer 6 haben können, haben wir einen unmöglichen Fall erreicht und sollten zum Backtracking zurückkehren. Da ich jedoch alle Optionen in einem Durchlauf berechnet und dann Zahlen in Zellen mit nur einer Möglichkeit platziert habe, habe ich nicht doppelt überprüft, ob ich eine Zahl in derselben Zeile direkt zuvor platziert hatte. Dies führte zu unmöglichen Lösungen.
Da alles in den oben definierten Arrays enthalten ist, beläuft sich die Speichernutzung des eigentlichen Solvers auf etwa 216 KB. Der Hauptteil der Speichernutzung stammt aus dem Array, das alle Rätsel enthält, und den E / A-Handlern in Java.
BEARBEITEN : Ich habe eine Version, die jetzt in C ++ übersetzt ist, aber nicht viel schneller. Die offizielle Zeit beträgt ungefähr 3,5 Sekunden, was keine große Verbesserung darstellt. Ich denke, das Hauptproblem bei meiner Implementierung ist, dass ich meine Masken eher als Arrays als als Bitmasken behalte. Ich werde versuchen, Arnauld's Lösung zu analysieren, um herauszufinden, was getan werden kann, um sie zu verbessern.
quelle
C ++ mit Minisat (2.2.1-5) - 11.735s offizielles Ergebnis
Dies ist bei weitem nicht so schnell wie ein spezialisierter Algorithmus, aber es ist ein anderer Ansatz, ein interessanter Bezugspunkt und einfach zu verstehen.
$ clang ++ -o solve -lminisat solver_minisat.cc
quelle