Ich habe eine Lösung gefunden, die wahrscheinlich nicht die effizienteste ist, aber gut genug funktioniert. Grundsätzlich:
- Sortieren Sie alle Wörter nach Länge und absteigend.
- Nimm das erste Wort und lege es an die Tafel.
- Nimm das nächste Wort.
- Durchsuchen Sie alle Wörter, die sich bereits an der Tafel befinden, und prüfen Sie, ob es mögliche Schnittpunkte (gemeinsame Buchstaben) mit diesem Wort gibt.
- Wenn es einen möglichen Ort für dieses Wort gibt, durchlaufen Sie alle Wörter auf der Tafel und prüfen Sie, ob das neue Wort stört.
- Wenn dieses Wort die Tafel nicht beschädigt, platzieren Sie es dort und fahren Sie mit Schritt 3 fort. Andernfalls suchen Sie weiter nach einem Ort (Schritt 4).
- Setzen Sie diese Schleife fort, bis alle Wörter entweder platziert sind oder nicht mehr platziert werden können.
Dies macht ein funktionierendes, aber oft recht schlechtes Kreuzworträtsel. Es gab eine Reihe von Änderungen, die ich am obigen Grundrezept vorgenommen habe, um ein besseres Ergebnis zu erzielen.
- Geben Sie am Ende der Generierung eines Kreuzworträtsels eine Punktzahl an, die darauf basiert, wie viele Wörter platziert wurden (je mehr desto besser), wie groß die Tafel ist (je kleiner desto besser) und wie hoch das Verhältnis zwischen Höhe und Breite ist (je näher) zu 1 desto besser). Generieren Sie eine Reihe von Kreuzworträtseln, vergleichen Sie deren Ergebnisse und wählen Sie das beste aus.
- Anstatt eine beliebige Anzahl von Iterationen auszuführen, habe ich beschlossen, so viele Kreuzworträtsel wie möglich in einer beliebigen Zeitspanne zu erstellen. Wenn Sie nur eine kleine Wortliste haben, erhalten Sie in 5 Sekunden Dutzende möglicher Kreuzworträtsel. Ein größeres Kreuzworträtsel kann nur aus 5-6 Möglichkeiten ausgewählt werden.
- Wenn Sie ein neues Wort platzieren, anstatt es sofort nach dem Finden einer akzeptablen Position zu platzieren, geben Sie dieser Wortposition eine Bewertung, die darauf basiert, um wie viel das Raster vergrößert wird und wie viele Schnittpunkte es gibt (idealerweise möchten Sie, dass jedes Wort ist gekreuzt von 2-3 anderen Wörtern). Behalten Sie alle Positionen und ihre Punktzahlen im Auge und wählen Sie dann die beste aus.
Ich habe erst kürzlich meine eigene in Python geschrieben. Sie finden es hier: http://bryanhelmig.com/python-crossword-puzzle-generator/ . Es werden nicht die dichten Kreuzworträtsel im NYT-Stil erstellt, sondern die Kreuzworträtsel, die Sie möglicherweise im Puzzlespielbuch eines Kindes finden.
Im Gegensatz zu einigen Algorithmen, die ich dort herausgefunden habe und die eine zufällige Brute-Force-Methode zum Platzieren von Wörtern implementiert haben, wie einige vorgeschlagen haben, habe ich versucht, einen etwas intelligenteren Brute-Force-Ansatz bei der Wortplatzierung zu implementieren. Hier ist mein Prozess:
Am Ende haben Sie ein anständiges Kreuzworträtsel oder ein Wortsuchrätsel, da sie ungefähr gleich sind. Es läuft in der Regel recht gut, aber lassen Sie mich wissen, wenn Sie Verbesserungsvorschläge haben. Größere Gitter laufen exponentiell langsamer. größere Wortlisten linear. Größere Wortlisten haben auch eine viel höhere Chance auf bessere Wortplatzierungsnummern.
quelle
array.sort(key=f)
ist stabil, was (zum Beispiel) bedeutet, dass durch einfaches Sortieren einer alphabetischen Wortliste nach Länge alle 8-Buchstaben-Wörter alphabetisch sortiert bleiben.Ich habe vor ungefähr zehn Jahren tatsächlich ein Programm zur Generierung von Kreuzworträtseln geschrieben (es war kryptisch, aber für normale Kreuzworträtsel gelten dieselben Regeln).
Es hatte eine Liste von Wörtern (und zugehörigen Hinweisen) in einer Datei gespeichert, sortiert nach absteigender Verwendung bis heute (so dass weniger verwendete Wörter oben in der Datei standen). Eine Vorlage, im Grunde eine Bitmaske, die die schwarzen und freien Quadrate darstellt, wurde zufällig aus einem vom Client bereitgestellten Pool ausgewählt.
Dann wurde für jedes nicht vollständige Wort im Puzzle (im Grunde genommen das erste leere Quadrat finden und prüfen, ob das rechte (quer zum Wort) oder das darunter liegende (das untere Wort) ebenfalls leer ist) eine Suche durchgeführt Die Datei sucht nach dem ersten passenden Wort und berücksichtigt dabei die Buchstaben, die bereits in diesem Wort enthalten sind. Wenn es kein passendes Wort gab, haben Sie das ganze Wort als unvollständig markiert und sind weitergegangen.
Am Ende wären einige unvollständige Wörter, die der Compiler ausfüllen müsste (und das Wort und einen Hinweis zur Datei hinzufügen, falls gewünscht). Wenn sie keine Ideen haben, können sie das Kreuzworträtsel manuell bearbeiten, um Einschränkungen zu ändern, oder einfach eine vollständige Neuerstellung anfordern.
Sobald die Wort- / Hinweisdatei eine bestimmte Größe erreicht hatte (und für diesen Client täglich 50 bis 100 Hinweise hinzugefügt wurden), gab es selten mehr als zwei oder drei manuelle Korrekturen, die für jedes Kreuzworträtsel durchgeführt werden mussten .
quelle
Dieser Algorithmus erstellt in 60 Sekunden 50 dichte Kreuzworträtsel mit 6 x 9 Pfeilen . Es verwendet eine Wortdatenbank (mit Wort + Tipps) und eine Board-Datenbank (mit vorkonfigurierten Boards).
Eine größere Wortdatenbank verkürzt die Generierungszeit erheblich und einige Boards sind schwerer zu füllen! Größere Bretter benötigen mehr Zeit, um richtig gefüllt zu werden!
Beispiel:
Vorkonfigurierte 6x9-Karte:
(# bedeutet eine Spitze in einer Zelle,% bedeutet zwei Spitzen in einer Zelle, Pfeile nicht gezeigt)
Generiertes 6x9 Board:
Tipps [Zeile, Spalte]:
quelle
Obwohl dies eine ältere Frage ist, werde ich versuchen, eine Antwort zu finden, die auf ähnlichen Arbeiten basiert, die ich durchgeführt habe.
Es gibt viele Ansätze zur Lösung von Einschränkungsproblemen (die im Allgemeinen in der NPC-Komplexitätsklasse liegen).
Dies hängt mit der kombinatorischen Optimierung und der Einschränkungsprogrammierung zusammen. In diesem Fall sind die Einschränkungen die Geometrie des Gitters und die Anforderung, dass Wörter eindeutig sind usw.
Randomisierungs- / Annealing-Ansätze können ebenfalls funktionieren (wenn auch innerhalb der richtigen Einstellung).
Effiziente Einfachheit könnte die ultimative Weisheit sein!
Die Anforderungen waren für einen mehr oder weniger vollständigen Kreuzworträtsel-Compiler und einen (visuellen WYSIWYG-) Builder.
Abgesehen vom WYSIWYG-Builder-Teil lautete die Compiler-Gliederung wie folgt:
Laden Sie die verfügbaren Wortlisten (sortiert nach Wortlänge, dh 2,3, .., 20).
Suchen Sie die Wortschlitze (dh Gitterwörter) im vom Benutzer erstellten Gitter (z. B. Wort bei x, y mit der Länge L, horizontal oder vertikal) (Komplexität O (N)).
Berechnen Sie die Schnittpunkte der Gitterwörter (die gefüllt werden müssen) (Komplexität O (N ^ 2))
Berechnen Sie die Schnittpunkte der Wörter in den Wortlisten mit den verschiedenen Buchstaben des verwendeten Alphabets (dies ermöglicht die Suche nach übereinstimmenden Wörtern mithilfe einer Vorlage, z. B. einer von cwc verwendeten Sik-Cambon-These ) (Komplexität O (WL * AL)).
Mit den Schritten .3 und .4 können Sie diese Aufgabe ausführen:
ein. Die Schnittpunkte der Gitterwörter mit sich selbst ermöglichen es, eine "Vorlage" für den Versuch zu erstellen, Übereinstimmungen in der zugehörigen Wortliste der verfügbaren Wörter für dieses Gitterwort zu finden (indem die Buchstaben anderer sich überschneidender Wörter mit diesem Wort verwendet werden, die bereits zu einem bestimmten Zeitpunkt gefüllt sind Schritt des Algorithmus)
b. Die Schnittpunkte der Wörter in einer Wortliste mit dem Alphabet ermöglichen es, passende (Kandidaten-) Wörter zu finden, die einer bestimmten "Vorlage" entsprechen (z. B. "A" an erster Stelle und "B" an dritter Stelle usw.)
Mit diesen implementierten Datenstrukturen wurde folgender Algorithmus verwendet:
HINWEIS: Wenn das Raster und die Wortdatenbank konstant sind, können die vorherigen Schritte nur einmal ausgeführt werden.
Der erste Schritt des Algorithmus besteht darin, einen leeren Wortschlitz (Gitterwort) zufällig auszuwählen und ihn mit einem Kandidatenwort aus der zugehörigen Wortliste zu füllen (die Zufallsgenerierung ermöglicht es, bei aufeinanderfolgenden Ausführungen des Algorithmus unterschiedliche Lösungen zu erzeugen) (Komplexität O (1) oder O () N))
Berechnen Sie für jeden noch leeren Wortschlitz (der Schnittpunkte mit bereits gefüllten Wortschlitzen aufweist) ein Einschränkungsverhältnis (dies kann variieren, etw ist einfach die Anzahl der verfügbaren Lösungen in diesem Schritt) und sortieren Sie die leeren Wortschlitze nach diesem Verhältnis (Komplexität O (NlogN) ) oder O (N))
Durchlaufen Sie die leeren Wortfelder, die im vorherigen Schritt berechnet wurden, und versuchen Sie für jedes einzelne eine Reihe von Stornierungslösungen (stellen Sie sicher, dass die "Lichtbogenkonsistenz erhalten bleibt", dh das Raster hat nach diesem Schritt eine Lösung, wenn dieses Wort verwendet wird) und sortieren Sie sie nach maximale Verfügbarkeit für den nächsten Schritt (dh der nächste Schritt hat eine maximal mögliche Lösung, wenn dieses Wort zu diesem Zeitpunkt an diesem Ort verwendet wird usw.) (Komplexität O (N * MaxCandidatesUsed))
Füllen Sie dieses Wort aus (markieren Sie es als gefüllt und fahren Sie mit Schritt 2 fort).
Wenn kein Wort gefunden wird, das die Kriterien von Schritt .3 erfüllt, versuchen Sie, zu einer anderen Kandidatenlösung eines vorherigen Schritts zurückzukehren (Kriterien können hier variieren) (Komplexität O (N)).
Wenn ein Backtrack gefunden wurde, verwenden Sie die Alternative und setzen Sie optional bereits gefüllte Wörter zurück, die möglicherweise zurückgesetzt werden müssen (markieren Sie sie erneut als nicht gefüllt) (Komplexität O (N)).
Wenn kein Backtrack gefunden wird, kann keine Lösung gefunden werden (zumindest mit dieser Konfiguration, dem anfänglichen Startwert usw.)
Andernfalls haben Sie eine Lösung, wenn alle Wordlots gefüllt sind
Dieser Algorithmus führt einen zufälligen konsistenten Durchlauf des Lösungsbaums des Problems durch. Wenn es irgendwann eine Sackgasse gibt, wird ein vorheriger Knoten zurückverfolgt und eine andere Route verfolgt. Bis entweder eine gefundene Lösung oder die Anzahl der Kandidaten für die verschiedenen Knoten erschöpft ist.
Der Konsistenzteil stellt sicher, dass eine gefundene Lösung tatsächlich eine Lösung ist, und der zufällige Teil ermöglicht es, unterschiedliche Lösungen in unterschiedlichen Ausführungen zu erzeugen und im Durchschnitt auch eine bessere Leistung zu erzielen.
PS. All dies (und andere) wurden in reinem JavaScript (mit paralleler Verarbeitung und WYSIWYG) implementiert
PS2. Der Algorithmus kann leicht parallelisiert werden, um mehr als eine (unterschiedliche) Lösung gleichzeitig zu erzeugen
Hoffe das hilft
quelle
Warum nicht einfach einen zufälligen probabilistischen Ansatz verwenden? Beginnen Sie mit einem Wort und wählen Sie dann wiederholt ein zufälliges Wort aus und versuchen Sie, es in den aktuellen Status des Puzzles zu integrieren, ohne die Einschränkungen für die Größe usw. zu brechen. Wenn Sie versagen, beginnen Sie einfach von vorne.
Sie werden überrascht sein, wie oft ein solcher Monte-Carlo-Ansatz funktioniert.
quelle
Hier ist ein JavaScript-Code, der auf der Antwort von Nickf und dem Python-Code von Bryan basiert. Posten Sie es einfach für den Fall, dass jemand anderes es in js benötigt.
quelle
Ich würde zwei Zahlen generieren: Length und Scrabble Score. Angenommen, eine niedrige Scrabble-Punktzahl bedeutet, dass das Beitreten einfacher ist (niedrige Punktzahl = viele gebräuchliche Buchstaben). Sortieren Sie die Liste nach absteigender Länge und aufsteigender Scrabble-Punktzahl.
Als nächstes gehen Sie einfach die Liste durch. Wenn sich das Wort nicht mit einem vorhandenen Wort kreuzt (überprüfen Sie jedes Wort anhand seiner Länge bzw. Scrabble-Punktzahl), stellen Sie es in die Warteschlange und überprüfen Sie das nächste Wort.
Spülen und wiederholen, und dies sollte ein Kreuzworträtsel erzeugen.
Natürlich bin ich mir ziemlich sicher, dass dies O (n!) Ist und es nicht garantiert ist, dass das Kreuzworträtsel für Sie vervollständigt wird, aber vielleicht kann es jemand verbessern.
quelle
Ich habe über dieses Problem nachgedacht. Meiner Meinung nach können Sie nicht hoffen, dass Ihre begrenzte Wortliste ausreicht, um ein wirklich dichtes Kreuzworträtsel zu erstellen. Daher möchten Sie möglicherweise ein Wörterbuch in eine "Trie" -Datenstruktur einfügen. Auf diese Weise können Sie leicht Wörter finden, die die verbleibenden Leerzeichen ausfüllen. In einem Versuch ist es ziemlich effizient, eine Durchquerung zu implementieren, die Ihnen beispielsweise alle Wörter der Form "c? T" gibt.
Mein allgemeiner Gedanke lautet also: Erstellen Sie einen relativ Brute-Force-Ansatz, wie hier beschrieben, um ein Kreuz mit niedriger Dichte zu erstellen, und füllen Sie die Lücken mit Wörterbuchwörtern.
Wenn jemand diesen Ansatz gewählt hat, lassen Sie es mich bitte wissen.
quelle
Ich habe mit der Kreuzworträtsel-Generator-Engine herumgespielt und fand dies am wichtigsten:
0.
!/usr/bin/python
ein.
allwords.sort(key=len, reverse=True)
b. Erstellen Sie ein Element / Objekt wie einen Cursor, der zur einfachen Orientierung um die Matrix herumgeht, es sei denn, Sie möchten später durch zufällige Auswahl iterieren.
das erste, nimm das erste Paar und platziere es quer und runter von 0,0; Speichern Sie den ersten als unseren aktuellen Kreuzworträtsel-Anführer.
Bewegen Sie den Cursor in diagonaler oder zufälliger Reihenfolge mit größerer diagonaler Wahrscheinlichkeit zur nächsten leeren Zelle
Durchlaufen Sie die Wörter wie und verwenden Sie die Länge des freien Speicherplatzes, um die maximale Wortlänge zu definieren:
temp=[] for w_size in range( len( w_space ), 2, -1 ) : # t for w in [ word for word in allwords if len(word) == w_size ] : # if w not in temp and putTheWord( w, w_space ) : # temp.append( w )
Um das Wort mit dem freien Speicherplatz zu vergleichen, habe ich Folgendes verwendet:
Ändern Sie nach jedem erfolgreich verwendeten Wort die Richtung. Schleife, während alle Zellen gefüllt sind ODER Ihnen die Wörter ausgehen ODER durch Iterationsbegrenzung, dann:
# CHANGE ALL WORDS LIST inexOf1stWord = allwords.index( leading_w ) allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]
... und wieder neues Kreuzworträtsel wiederholen.
Machen Sie das Bewertungssystem durch einfaches Füllen und einige Schätzungsberechnungen. Geben Sie die Punktzahl für das aktuelle Kreuzworträtsel an und schränken Sie die spätere Auswahl ein, indem Sie es in die Liste der erstellten Kreuzworträtsel einfügen, wenn die Punktzahl von Ihrem Punktesystem erfüllt wird.
Nach der ersten Iterationssitzung iterieren Sie erneut aus der Liste der erstellten Kreuzworträtsel, um den Job zu beenden.
Durch die Verwendung von mehr Parametern kann die Geschwindigkeit um einen großen Faktor verbessert werden.
quelle
Ich würde einen Index jedes Buchstabens erhalten, der von jedem Wort verwendet wird, um mögliche Kreuze zu kennen. Dann würde ich das größte Wort wählen und es als Basis verwenden. Wählen Sie den nächsten großen aus und kreuzen Sie ihn. Spülen und wiederholen. Es ist wahrscheinlich ein NP-Problem.
Eine andere Idee ist die Erstellung eines genetischen Algorithmus, bei dem die Stärke-Metrik angibt, wie viele Wörter Sie in das Raster einfügen können.
Der schwierige Teil, den ich finde, ist, wenn man weiß, dass eine bestimmte Liste unmöglich gekreuzt werden kann.
quelle
Ich habe eine JavaScript / jQuery-Lösung für dieses Problem codiert:
Beispieldemo: http://www.earthfluent.com/crossword-puzzle-demo.html
Quellcode: https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator
Die Absicht des von mir verwendeten Algorithmus:
Ich werde den Algorithmus beschreiben, den ich verwendet habe:
Gruppieren Sie die Wörter nach denen, die einen gemeinsamen Buchstaben haben.
Erstellen Sie aus diesen Gruppen Sätze einer neuen Datenstruktur ("Wortblöcke"), bei der es sich um ein primäres Wort (das alle anderen Wörter durchläuft) und dann um die anderen Wörter (die das primäre Wort durchlaufen) handelt.
Beginnen Sie das Kreuzworträtsel mit dem allerersten dieser Wortblöcke ganz oben links im Kreuzworträtsel.
Bewegen Sie sich für den Rest der Wortblöcke ausgehend von der Position ganz rechts unten des Kreuzworträtsels nach oben und links, bis keine freien Plätze mehr verfügbar sind. Wenn mehr leere Spalten nach oben als nach links vorhanden sind, bewegen Sie sich nach oben und umgekehrt.
quelle
var crosswords = generateCrosswordBlockSources(puzzlewords);
. Protokollieren Sie diesen Wert einfach in der Konsole. Vergiss nicht, es gibt einen "Cheat-Modus" im Spiel, in dem du einfach auf "Antwort anzeigen" klicken kannst, um den Wert sofort zu erhalten.Dieser erscheint als Projekt im AI CS50-Kurs von Harvard. Die Idee ist, das Kreuzworträtsel-Generierungsproblem als ein Problem der Einschränkungszufriedenheit zu formulieren und es durch Zurückverfolgen mit verschiedenen Heuristiken zu lösen, um den Suchraum zu reduzieren.
Zunächst benötigen wir einige Eingabedateien:
`
`
Ein Eingabevokabular (Wortliste / Wörterbuch), aus dem die Kandidatenwörter ausgewählt werden (wie das folgende gezeigt).
a abandon ability able abortion about above abroad absence absolute absolutely ...
Nun ist der CSP wie folgt definiert und zu lösen:
Das Folgende zeigt die Ausgabe, die unter Verwendung einer Implementierung des CSP-Lösungsalgorithmus erhalten wurde:
`
Die folgende Animation zeigt die Schritte zum Zurückverfolgen:
Hier ist eine andere mit einer Wortliste in Bangla (Bengali):
quelle