Ein Nonogramm ist ein japanisches Puzzlespiel, bei dem das Ziel darin besteht, ein Schwarzweißbild anhand einer Liste zusammenhängender Regionen zu zeichnen.
Definieren Sie die nicht- grafische Größe einer Zeile oder Spalte als die Anzahl der zusammenhängenden schwarzen Bereiche in dieser Zeile oder Spalte. Zum Beispiel hat die oberste Reihe eine nichtografische Größe von 1, da sich in dieser Reihe ein Bereich von 2 Quadraten befindet. Die achte Reihe hat eine nichtografische Größe von 3, weil sie 2, 2, 1 hat.
Eine leere Zeile oder Spalte hat eine nichtografische Größe von 0.
Ihre Aufgabe ist es, ein Programm zu schreiben, das ein Lösungsraster für ein Nonogramm verwendet und ein Lösungsraster mit möglichst wenigen ausgefüllten Quadraten erstellt wobei jede Zeile und Spalte das gleiche nichtografische Magnutid wie das angegebene Lösungsraster hat.
Ein Nonogramm-Raster mit allen ausgefüllten Quadraten hat beispielsweise in jeder Zeile oder Spalte eine nicht-grafische Größe von 1:
Die gleiche nicht-grafische Größe könnte erreicht werden, wenn nur ein diagonaler Streifen durch das Raster gezogen wird, wodurch die Anzahl der ausgefüllten Quadrate drastisch verringert wird:
Ihr Programm erhält eine Eingabe aus 50.000 Zeilen aus dieser Datei (1,32 MB tar.gz-Textdatei; 2,15 MB entpackt), die jeweils ein einzelnes 16 × 16-Nonogramm-Lösungsraster mit zufällig (80% schwarz) ausgefüllten Quadraten darstellt Geben Sie weitere 50.000 Zeilen aus, die jeweils das optimierte Lösungsraster für das entsprechende Eingaberaster enthalten.
Jedes Raster wird als base64-Zeichenfolge mit 43 Zeichen dargestellt (wobei die Quadrate von links nach rechts und dann von oben nach unten codiert werden), und Ihr Programm muss die Ausgabe im gleichen Format zurückgeben. Das erste Raster in der Datei lautet beispielsweise E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=
und wird wie folgt dargestellt :
Das Raster beginnt mit E
der Zuordnung zu 000100
, sodass die ersten sechs Zellen in der oberen Reihe bis auf die vierte weiß sind. Das nächste Zeichen ist /
das 111111
, dem es zugeordnet ist. Die nächsten 6 Zellen sind also alle schwarz - und so weiter.
Ihr Programm muss für jeden der 50.000 Testfälle ein Lösungsraster mit den korrekten nichtografischen Größen zurückgeben. Es ist zulässig, dasselbe Raster wie die Eingabe zurückzugeben, wenn nichts Besseres gefunden wurde.
Das Programm, das die wenigsten vollständig ausgefüllten Felder (in jeder Sprache) zurückgibt, ist der Gewinner, wobei der kürzere Code der Tiebreaker ist.
Aktueller Anzeiger:
- 3,637,260 - Sleafar, Java
- 7.270.894 - Flawr, Matlab
- 10.239.288 - Joe Z., Bash
quelle
Antworten:
Python 2 & PuLP - 2.644.688 Quadrate (optimal minimiert); 10.753.553 Quadrate (optimal maximiert)
Minimal auf 1152 Bytes golfen
(Hinweis: Die stark eingerückten Zeilen beginnen mit Tabulatoren und nicht mit Leerzeichen.)
Beispielausgabe: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing
Es stellt sich heraus, dass Probleme wie zum Beispiel leicht in ganzzahlige lineare Programme konvertierbar sind, und ich brauchte ein grundlegendes Problem, um zu lernen, wie man PuLP - eine Python-Schnittstelle für eine Vielzahl von LP-Solvern - für ein eigenes Projekt verwendet. Es stellt sich auch heraus, dass PuLP extrem einfach zu bedienen ist und der ungolfed LP-Builder beim ersten Versuch einwandfrei funktioniert hat.
Die zwei schönen Dinge beim Einsatz eines Branch-and-Bound-IP-Solvers, um die harte Arbeit zu erledigen, die ich für dieses Problem geleistet habe (abgesehen davon, dass ich keinen Branch-and-Bound-Solver implementieren muss), sind die folgenden
Wie man dieses Programm benutzt
Zunächst müssen Sie PuLP installieren.
pip install pulp
sollte den Trick machen, wenn Sie Pip installiert haben.Dann müssen Sie Folgendes in eine Datei mit dem Namen "c" einfügen: einfügen https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing
Führen Sie dieses Programm dann in einem beliebigen späten Python 2-Build aus demselben Verzeichnis aus. In weniger als einem Tag haben Sie eine Datei mit dem Namen "s", die 50.000 gelöste Nonogramm-Gitter (in lesbarem Format) mit der Gesamtzahl der darunter aufgeführten ausgefüllten Quadrate enthält.
Wenn Sie stattdessen die Anzahl der ausgefüllten Felder maximieren möchten, ändern Sie die
LpMinimize
Option in Zeile 8 inLpMaximize
. Sie erhalten eine sehr ähnliche Ausgabe: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharingEingabeformat
Dieses Programm verwendet ein modifiziertes Eingabeformat, da Joe Z. angab, dass wir das Eingabeformat neu codieren dürfen, wenn wir dies in einem Kommentar zum OP wünschen. Klicken Sie auf den Link oben, um zu sehen, wie es aussieht. Es besteht aus 10000 Zeilen mit jeweils 16 Ziffern. Die geradzahligen Linien sind die Beträge für die Zeilen einer bestimmten Instanz, während die ungeradzahligen Linien die Beträge für die Spalten derselben Instanz wie die darüber liegende Zeile sind. Diese Datei wurde vom folgenden Programm generiert:
(Dieses Programm zur Neucodierung gab mir außerdem die Möglichkeit, meine benutzerdefinierte BitQueue-Klasse zu testen, die ich für dasselbe oben erwähnte Projekt erstellt habe. Es handelt sich lediglich um eine Warteschlange, in die Daten als Folgen von Bits ODER Bytes übertragen werden können und aus der Daten stammen können entweder ein bisschen oder ein Byte auf einmal gepoppt werden. In diesem Fall hat es perfekt funktioniert.)
Ich habe die Eingabe aus dem speziellen Grund neu codiert, dass zum Erstellen eines ILP die zusätzlichen Informationen zu den Gittern, die zum Generieren der Größen verwendet wurden, vollkommen unbrauchbar sind. Die Größen sind die einzigen Einschränkungen, und deshalb brauche ich nur Zugriff auf die Größen.
Ungolfed ILP-Erbauer
Dies ist das Programm, das tatsächlich die oben verlinkte "Beispielausgabe" erzeugt hat. Daher die extra langen Saiten am Ende jedes Gitters, die ich beim Golfen abgeschnitten habe. (Die Golfversion sollte eine identische Ausgabe ohne die Wörter produzieren
"Filled squares for "
)Wie es funktioniert
Ich verwende ein 18x18-Raster, wobei der mittlere 16x16-Teil die eigentliche Rätsellösung ist.
cells
ist dieses Gitter. In der ersten Zeile werden 324 Binärvariablen erstellt: "cell_0_0", "cell_0_1" usw. Ich erstelle auch Gitter der "Räume" zwischen und um die Zellen im Lösungsteil des Gitters.rowseps
Verweist auf die 289 Variablen, die die Räume symbolisieren, die Zellen horizontal trennen, undcolseps
auf Variablen, die die Räume markieren, die Zellen vertikal trennen. Hier ist ein Unicode-Diagramm:Das
0
s und□
s sind die von dencell
Variablen verfolgten Binärwerte , das|
s sind die von denrowsep
Variablen verfolgten Binärwerte und das-
s sind die von dencolsep
Variablen verfolgten Binärwerte .Dies ist die Zielfunktion. Nur die Summe aller
cell
Variablen. Da es sich um binäre Variablen handelt, entspricht dies genau der Anzahl der ausgefüllten Quadrate in der Lösung.Dadurch werden die Zellen um den äußeren Rand des Gitters auf Null gesetzt (weshalb ich sie oben als Nullen dargestellt habe). Dies ist die zweckmäßigste Methode, um zu verfolgen, wie viele "Zellenblöcke" gefüllt sind, da so sichergestellt wird, dass jeder Wechsel von ungefüllt zu gefüllt (über eine Spalte oder Zeile) mit einem entsprechenden Wechsel von gefüllt zu ungefüllt (und umgekehrt) übereinstimmt ), auch wenn die erste oder letzte Zelle in der Zeile gefüllt ist. Dies ist der einzige Grund für die Verwendung eines 18x18-Rasters. Es ist nicht die einzige Möglichkeit, Blöcke zu zählen, aber ich denke, es ist die einfachste.
Dies ist das eigentliche Fleisch der Logik der ILP. Grundsätzlich ist es erforderlich, dass jede Zelle (mit Ausnahme der Zelle in der ersten Zeile und Spalte) das logische xor der Zelle und des Trennzeichens direkt links in der Zeile und direkt darüber in der Spalte ist. Ich habe die Einschränkungen, die ein xor innerhalb eines {0,1} Integer-Programms simulieren, aus dieser wunderbaren Antwort erhalten: /cs//a/12118/44289
Um ein bisschen mehr zu erklären: Diese xor-Einschränkung bewirkt, dass die Trennzeichen genau dann 1 sein können, wenn sie zwischen den Zellen 0 und 1 liegen (ein Wechsel von ungefüllt zu gefüllt oder umgekehrt). Somit gibt es in einer Zeile oder Spalte genau doppelt so viele einwertige Trennzeichen wie die Anzahl der Blöcke in dieser Zeile oder Spalte. Mit anderen Worten, die Summe der Trennzeichen in einer bestimmten Zeile oder Spalte ist genau doppelt so groß wie diese Zeile / Spalte. Daher die folgenden Einschränkungen:
Und das war's auch schon. Der Rest fordert den Standardlöser lediglich auf, das ILP zu lösen, und formatiert dann die resultierende Lösung, während sie in die Datei geschrieben wird.
quelle
Java,
6.093.0924.332.6563.637.260 Quadrate (minimiert),10.567.55010.567.69110.568.746 Quadrate (maximiert)Beide Varianten des Programms führen wiederholt Operationen am Quellgitter durch, ohne die Größe zu ändern.
Minimizer
schrumpfen()
Wenn ein schwarzes Quadrat 2 weiße Nachbarn und 2 schwarze Nachbarn in einem Winkel von 90 ° hat, kann es durch ein weißes Quadrat ersetzt werden.
moveLine ()
In Konfigurationen wie oben kann die schwarze Linie nach rechts verschoben werden. Dies geschieht wiederholt für alle 4 Linienrichtungen im und gegen den Uhrzeigersinn, um neue Schrumpfmöglichkeiten zu eröffnen.
Maximizer
Kommentieren Sie die Zeile aus
main()
und kommentieren Sie sie für diese Version aus.wachsen()
Wenn ein weißes Quadrat 2 weiße Nachbarn und 2 schwarze Nachbarn in einem Winkel von 90 ° hat, kann es durch ein schwarzes Quadrat ersetzt werden.
moveLine ()
Gleich wie im Minimizer.
Quelle
quelle
Bash - 10.239.288 Quadrate
Als letzte Referenzlösung:
Da Ihr Programm das gleiche Raster zurückgeben darf, wenn es keine bessere Lösung findet, ist es auch gültig, die gesamte Datei wörtlich auszudrucken.
Die Testdatei enthält insgesamt 10.239.288 schwarze Quadrate, was ziemlich nahe an den 10.240.000 liegt, die Sie von 80% der Quadrate erwarten, die in 50.000 Gittern mit jeweils 256 Quadraten ausgefüllt sind. Wie bei meinen Fragen zu Testbatterien üblich, habe ich die Anzahl der Testfälle mit der Erwartung ausgewählt, dass die optimalen Ergebnisse bei etwa 2 Millionen liegen, obwohl ich vermute, dass die Ergebnisse diesmal eher bei 4 oder 5 Millionen liegen werden .
Wenn jemand eine Lösung schaffen kann, die die schwarzen Quadrate maximiert, anstatt sie zu minimieren, und es schafft, über 10.240.000 zu erreichen, könnte ich in Betracht ziehen, ihr ein Kopfgeld zu geben.
quelle
Matlab, 7.270.894 Quadrate (~ 71% des Originals)
Idee ist eine einfache wiederholte gierige Suche: Versuchen Sie für jedes schwarze Quadrat, es auf Weiß zu setzen, ohne die nicht-grafischen Größen zu ändern. Wiederholen Sie dies zweimal. (Mit mehr Wiederholungen könnte man weitaus bessere Ergebnisse erzielen, aber nicht kostenlos: Das führt zu einer entsprechend längeren Laufzeit. Jetzt waren es ca. 80 Minuten. Ich würde das tun, wenn wir nicht alle 50k-Testfälle berechnen müssten ...)
Hier der Code (jede Funktion wie gewohnt in einer eigenen Datei.)
quelle