Bei dieser Herausforderung geht es nicht um das Spiel Snake.
Stellen Sie sich eine 2D-Schlange vor, die durch Zeichnen einer horizontalen Längenlinie gebildet wird n
. An ganzzahligen Punkten entlang ihres Körpers kann diese Schlange ihren Körper um 90 Grad drehen. Wenn wir die Vorderseite der Schlange so definieren, dass sie sich ganz links befindet, bewegt die Drehung den hinteren Teil der Schlange und der vordere Teil bleibt stehen. Durch wiederholte Rotationen können viele verschiedene Schlangenkörperformen hergestellt werden.
Regeln
- Ein Teil des Körpers der Schlange kann einen anderen nicht überlappen.
- Es muss möglich sein, die endgültige Ausrichtung zu erreichen, ohne dass sich Teile des Körpers der Schlange dazwischen überlappen. Zwei Punkte, die sich berühren, werden in diesem Problem als überlappend gezählt.
- Ich betrachte eine Schlange und ihre Rückseite als dieselbe Form.
Aufgabe
Wie viele verschiedene Schlangenkörperformen können bis zu Rotation, Translation und Spiegelsymmetrie hergestellt werden?
Beispiel einer Rotation eines Teils des Schlangenkörpers. Stellen Sie sich vor n=10
und die Schlange befindet sich in ihrer Startausrichtung einer geraden Linie. Drehen Sie nun um 4
90 Grad gegen den Uhrzeigersinn. Wir lassen die Schlange von 4
bis 10
(den Schwanz der Schlange) vertikal liegen und die Schlange von 0
bis 4
horizontal liegen. Die Schlange hat jetzt einen rechten Winkel in ihrem Körper.
Hier einige Beispiele dank Martin Büttner.
Wir beginnen mit der horizontalen Schlange.
Jetzt drehen wir uns von Position 4.
Wir landen nach der Rotation in dieser Ausrichtung.
Betrachten wir nun diese Ausrichtung einer anderen Schlange.
Wir können jetzt eine illegale Bewegung sehen, bei der es während der Rotation zu einer Überlappung kommen würde.
Ergebnis
Ihre Punktzahl ist die größte, n
für die Ihr Code das Problem auf meinem Computer in weniger als einer Minute lösen kann.
Wenn eine Rotation stattfindet, bewegt sie eine Hälfte der Schlange mit. Wir müssen uns Sorgen machen, ob irgendein Teil dieses gedrehten Teils einen Teil der Schlange während der Drehung überlappen könnte. Der Einfachheit halber können wir annehmen, dass die Schlange die Breite Null hat. Sie können nur an einer bestimmten Stelle in der Schlange bis zu 90 Grad im Uhrzeigersinn oder gegen den Uhrzeigersinn drehen. Denn Sie können die Schlange niemals vollständig in zwei Teile falten, da dies zwei Umdrehungen am gleichen Punkt in die gleiche Richtung zur Folge gehabt hätte.
Formen, die nicht gemacht werden können
Ein einfaches Beispiel für eine Form, die nicht hergestellt werden kann, ist ein Kapital T
. Eine anspruchsvollere Version ist
(Vielen Dank an Harald Hanche-Olsen für dieses Beispiel)
In diesem Beispiel sind alle benachbarten horizontalen Linien 1 voneinander entfernt, ebenso wie die vertikalen. Es gibt daher keinen legalen Wechsel von dieser Position und da das Problem umkehrbar ist, gibt es keine Möglichkeit, von der Startposition dorthin zu gelangen.
Sprachen und Bibliotheken
Sie können jede Sprache verwenden, die einen frei verfügbaren Compiler / Interpreter / etc. für Linux und alle Bibliotheken, die auch für Linux frei verfügbar sind.
Meine Maschine Die Timings werden auf meiner Maschine ausgeführt. Dies ist eine Standard-Ubuntu-Installation auf einem AMD FX-8350 Eight-Core-Prozessor. Dies bedeutet auch, dass ich Ihren Code ausführen kann. Verwenden Sie daher nur leicht verfügbare freie Software und fügen Sie bitte vollständige Anweisungen zum Kompilieren und Ausführen Ihres Codes bei.
Antworten:
Python 3 - vorläufige Punktzahl: n = 11 (n = 13 mit PyPy *)
Da es in der ersten Woche keine Antworten gab, finden Sie hier ein Beispiel in Python, um den Wettbewerb zu fördern. Ich habe versucht, es einigermaßen lesbar zu machen, damit Ineffizienzen leicht identifiziert werden können, um Ideen für andere Antworten zu geben.
Ansatz
Code
(jetzt mit einigen Doktrinen und Behauptungen nach meinem falschen ersten Versuch)
Ergebnisse
Auf meinem Computer ist die längste Schlange, die in weniger als 1 Minute berechnet werden kann, Länge 11 (oder Länge 13 mit PyPy *). Dies ist offensichtlich nur eine vorläufige Punktzahl, bis wir herausfinden, wie die offizielle Punktzahl von Lembiks Maschine stammt.
Zum Vergleich sind hier die Ergebnisse für die ersten Werte von n:
Bitte lassen Sie mich wissen, wenn sich herausstellt, dass eine dieser Angaben falsch ist.
Wenn Sie ein Beispiel für eine Anordnung haben, die nicht entfaltet werden kann, können Sie die Funktion verwenden
neighbours(snake)
, um alle in einem Schritt erreichbaren Anordnungen als Test des Codes zu finden.snake
ist ein Tupel von Gelenkrichtungen (0 für im Uhrzeigersinn, 1 für gerade, 2 für gegen den Uhrzeigersinn). Zum Beispiel ist (1,1,1) eine gerade Schlange der Länge 4 (mit 3 Gelenken).Visualisierung
Um eine Schlange oder eine der von
neighbours
Ihnen zurückgegebenen Schlangen zu visualisieren , können Sie die Funktion verwendendisplay(snake)
. Dies akzeptiert ein Tupel wie die anderen Funktionen, aber da es nicht vom Hauptprogramm verwendet wird und daher nicht schnell sein muss, akzeptiert es auch eine Zeichenfolge.display((1,1,2,0))
ist äquivalent zudisplay("1120")
Wie Lembik in den Kommentaren erwähnt, sind meine Ergebnisse identisch mit denen von OEIS A037245, bei denen Schnittpunkte während der Rotation nicht berücksichtigt werden. Dies liegt daran, dass es für die ersten Werte von n keinen Unterschied gibt - alle Formen, die sich nicht selbst schneiden, können durch Falten einer geraden Schlange erreicht werden. Die Richtigkeit des Codes kann getestet werden, indem
neighbours()
mit einer Schlange aufgerufen wird, die ohne Kreuzung nicht entfaltet werden kann. Da es keine Nachbarn hat, trägt es nur zur OEIS-Sequenz bei und nicht zu dieser. Das kleinste mir bekannte Beispiel ist diese Schlange der Länge 31, die Lembik dank David K erwähnt hat :(1,1,1,1,2,1,2,1,1,1,1,1,1,2,1,1,1,2,1,1,2,2,1,0,1,0,1,1,1,1)
display('111121211111121112112210101111')
gibt folgende Ausgabe:Ich würde gerne von jedem hören, der ein kürzeres Beispiel ohne Nachbarn hat. Ich vermute, dass das kürzeste derartige Beispiel das kleinste n markiert, für das sich die beiden Sequenzen unterscheiden.
* Beachten Sie, dass jedes Inkrement von n ungefähr dreimal so lange dauert, sodass das Erhöhen von n = 11 auf n = 13 fast das Zehnfache der Zeit erfordert. Aus diesem Grund erlaubt PyPy nur, n um 2 zu erhöhen, obwohl es erheblich schneller als der Standard-Python-Interpreter läuft.
quelle
C ++ 11 - funktioniert fast :)
Nachdem ich diesen Artikel gelesen hatte , sammelte ich ein bisschen Weisheit von dem Typ, der anscheinend 25 Jahre lang an dem weniger komplizierten Problem gearbeitet hatte, selbstvermeidende Pfade auf einem quadratischen Gitter zu zählen.
Erstellen der ausführbaren Datei
Kompilieren mit Ich verwende MinGW unter Win7 mit g ++ 4.8 für "Linux" -Builds, sodass die Portabilität nicht zu 100% garantiert ist.
g++ -O3 -std=c++11
Es funktioniert auch (irgendwie) mit einem Standard-MSVC2013-Projekt
Durch Aufheben der Definition erhalten
NDEBUG
Sie Spuren der Algorithmusausführung und eine Zusammenfassung der gefundenen Konfigurationen.Aufführungen
Mit oder ohne Hash-Tabellen arbeitet der Microsoft-Compiler miserabel: Die Erstellung von g ++ ist dreimal schneller .
Der Algorithmus verwendet praktisch keinen Speicher.
Da die Kollisionsprüfung ungefähr in O (n) liegt, sollte die Rechenzeit in O (nk n ) liegen, wobei k etwas niedriger als 3 ist.
Auf meinem [email protected] dauert n = 17 ungefähr 1:30 (ungefähr 2 Millionen) Schlangen / Minute).
Ich bin noch nicht mit der Optimierung fertig, aber ich würde nicht mehr als einen x3-Gewinn erwarten, also kann ich im Grunde hoffen, unter einer Stunde vielleicht n = 20 oder unter einem Tag n = 24 zu erreichen.
Das Erreichen der ersten bekannten nicht biegbaren Form (n = 31) würde zwischen einigen Jahren und einem Jahrzehnt dauern, sofern keine Stromausfälle vorliegen.
Formen zählen
Eine Schlange der Größe N hat N-1- Gelenke.
Jedes Gelenk kann gerade gelassen oder nach links oder rechts gebogen werden (3 Möglichkeiten).
Die Anzahl der möglichen Faltungen beträgt somit 3 N-1 .
Kollisionen reduzieren diese Zahl etwas, sodass die tatsächliche Zahl nahe bei 2,7 N-1 liegt
Viele solcher Faltungen führen jedoch zu identischen Formen.
Zwei Formen sind identisch, wenn es entweder eine Rotation oder eine Symmetrie gibt , die sich ineinander verwandeln kann.
Definieren wir ein Segment als einen geraden Teil des gefalteten Körpers.
Zum Beispiel würde eine Schlange der Größe 5, die am 2. Gelenk gefaltet ist, 2 Segmente haben (eines 2 Einheiten lang und das zweite 3 Einheiten lang).
Das erste Segment wird Kopf und der letzte Schwanz genannt .
Konventionell richten wir den Kopf der Schlangen horizontal aus, wobei der Körper nach rechts zeigt (wie in der ersten Abbildung des OP).
Wir bezeichnen eine gegebene Figur mit einer Liste vorzeichenbehafteter Segmentlängen, wobei positive Längen eine rechte Faltung und negative eine linke Faltung anzeigen.
Die Anfangslänge ist gemäß Konvention positiv.
Segmente und Biegungen trennen
Wenn wir nur die verschiedenen Möglichkeiten betrachten, wie eine Schlange der Länge N in Segmente aufgeteilt werden kann, erhalten wir eine Aufteilung, die mit den Zusammensetzungen von N identisch ist .
Mit dem gleichen Algorithmus wie auf der Wiki-Seite gezeigt, ist es einfach, alle 2 N-1 möglichen Partitionen der Schlange zu generieren .
Jede Trennwand erzeugt wiederum alle möglichen Faltungen, indem entweder alle ihre Gelenke nach links oder rechts gebogen werden. Eine solche Faltung wird als Konfiguration bezeichnet .
Alle möglichen Partitionen können durch eine ganze Zahl von N-1 Bits dargestellt werden, wobei jedes Bit das Vorhandensein einer Verbindung darstellt. Wir werden diese ganze Zahl einen Generator nennen .
Trennwände
Wenn wir feststellen, dass das Biegen einer bestimmten Partition vom Kopf nach unten dem Biegen der symetrischen Partition vom Schwanz nach oben entspricht, können wir alle Paare symetrischer Partitionen finden und eins von zwei eliminieren.
Der Generator einer symmetrischen Partition ist der Generator der Partition, der in umgekehrter Bitreihenfolge geschrieben ist, was trivial einfach und billig zu erkennen ist.
Dadurch wird fast die Hälfte der möglichen Partitionen eliminiert, mit Ausnahme der Partitionen mit "palindromischen" Generatoren, die durch Bitumkehr unverändert bleiben (z. B. 00100100).
Auf horizontale Symetrien achten
Mit unseren Konventionen (eine Schlange zeigt nach rechts) erzeugt die allererste Biegung, die nach rechts angewendet wird, eine Familie von Faltungen, die horizontal symmetrisch zu denen sind, die sich nur durch die erste Biegung unterscheiden.
Wenn wir entscheiden, dass die erste Biegung immer rechts ist, eliminieren wir alle horizontalen Symmetrien auf einen Schlag.
Palindrome aufwischen
Diese beiden Schnitte sind effizient, aber nicht genug, um diese lästigen Palindrome zu behandeln.
Die gründlichste Überprüfung im allgemeinen Fall ist wie folgt:
Betrachten Sie eine Konfiguration C mit einer palindromischen Partition.
Wir könnten jede neue Konfiguration mit den 3 anderen vergleichen. Da wir jedoch bereits Konfigurationen generieren, die mit einer Rechtskurve beginnen, müssen wir nur eine mögliche Symmetrie überprüfen:
Dies ist die einzige Konfiguration, die wir möglicherweise duplizieren können.
Eliminieren von Duplikaten ohne Speicher
Mein ursprünglicher Ansatz bestand darin, alle Konfigurationen in einer riesigen Hash-Tabelle zu speichern, um Duplikate zu beseitigen, indem ich das Vorhandensein einer zuvor berechneten symetrischen Konfiguration überprüfe.
Dank des oben genannten Artikels wurde klar, dass Partitionen und Faltungen, die als Bitfelder gespeichert sind, wie jeder numerische Wert verglichen werden können.
Um ein Mitglied eines symetrischen Paares zu eliminieren, können Sie einfach beide Elemente vergleichen und systematisch das kleinste (oder das größte, wie Sie möchten) beibehalten.
Das Testen einer Konfiguration auf Duplizierung bedeutet also, die symetrische Partition und, wenn beide identisch sind, die Faltung zu berechnen. Es wird überhaupt kein Speicher benötigt.
Reihenfolge der Erzeugung
Die Kollisionsprüfung ist eindeutig der zeitaufwändigste Teil, daher ist die Reduzierung dieser Berechnungen eine große Zeitersparnis.
Eine mögliche Lösung besteht darin, eine "Ragdoll-Schlange" zu haben, die in einer flachen Konfiguration beginnt und allmählich gebogen wird, um zu vermeiden, dass die gesamte Schlangengeometrie für jede mögliche Konfiguration neu berechnet wird.
Durch Auswahl der Reihenfolge, in der die Konfigurationen getestet werden, sodass höchstens eine Stoffpuppe für jede Gesamtzahl von Gelenken gespeichert wird, können wir die Anzahl der Instanzen auf N-1 begrenzen.
Ich benutze einen rekursiven Scan des Sake vom Schwanz abwärts und füge auf jeder Ebene ein einzelnes Gelenk hinzu. Daher wird eine neue Ragdoll-Instanz auf der übergeordneten Konfiguration mit einer einzigen zusätzlichen Biegung erstellt.
Dies bedeutet, dass Biegungen in einer sequentiellen Reihenfolge angewendet werden, was in fast allen Fällen ausreicht, um Selbstkollisionen zu vermeiden.
Wenn eine Selbstkollision erkannt wird, werden die Biegungen, die zu der beleidigenden Bewegung führen, in allen möglichen Reihenfolgen angewendet, bis eine legitime Faltung gefunden wird oder alle Kombinationen erschöpft sind.
Statische Prüfung
Bevor ich überhaupt über bewegliche Teile nachdachte, fand ich es effizienter, die statische Endform einer Schlange auf Selbstüberschneidungen zu testen.
Dies geschieht durch Zeichnen der Schlange auf einem Gitter. Jeder mögliche Punkt wird vom Kopf abwärts aufgezeichnet. Wenn es eine Selbstüberschneidung gibt, fallen mindestens zwei Punkte auf dieselbe Stelle. Dies erfordert genau N Diagramme für jede Schlangenkonfiguration für eine konstante O (N) -Zeit.
Der Hauptvorteil dieses Ansatzes besteht darin, dass der statische Test allein einfach gültige selbstvermeidende Pfade auf einem quadratischen Gitter auswählt, wodurch der gesamte Algorithmus getestet werden kann, indem die dynamische Kollisionserkennung verhindert und sichergestellt wird, dass die richtige Anzahl solcher Pfade gefunden wird.
Dynamische Prüfung
Wenn sich eine Schlange um ein Gelenk faltet, fegt jedes gedrehte Segment einen Bereich, dessen Form alles andere als trivial ist.
Natürlich können Sie Kollisionen überprüfen, indem Sie die Einbeziehung in alle diese überstrichenen Bereiche einzeln testen. Eine globale Überprüfung wäre effizienter, aber angesichts der Komplexität der Bereiche kann ich mir keine vorstellen (außer vielleicht die Verwendung einer GPU, um alle Bereiche zu zeichnen und eine globale Trefferprüfung durchzuführen).
Da der statische Test die Start- und Endpositionen jedes Segments berücksichtigt, müssen wir nur die Schnittpunkte mit dem überprüfen Bögendie von jedem rotierenden Segment überstrichen werden.
Nach einer interessanten Diskussion mit Trichoplax und ein bisschen JavaScript , um mich zu orientieren, kam ich auf diese Methode:
Um zu versuchen, es in ein paar Worten auszudrücken, wenn Sie anrufen
(Quelle: free.fr )
Für jedes Segment, das nicht enthält I , wird der überstrichene Bereich durch 2 Bögen begrenzt (und 2 Segmente, die bereits durch die statische Prüfung ).
Wenn ich in das Segment falle, muss auch der von I überstrichene Bogen berücksichtigt werden.
Dies bedeutet, dass wir jedes unbewegliche Segment gegen jedes rotierende Segment mit 2 oder 3 Segment-mit-Bogen-Schnittpunkten prüfen können
Ich habe die Vektorgeometrie verwendet, um trigonometrische Funktionen insgesamt zu vermeiden.
Vektoroperationen erzeugen kompakten und (relativ) lesbaren Code.
Der Schnittpunkt von Segment zu Bogen erfordert einen Gleitkomma-Vektor, die Logik sollte jedoch immun gegen Rundungsfehler sein.
Ich fand diese elegante und effiziente Lösung in einem obskuren Forumsbeitrag. Ich frage mich, warum es nicht weiter verbreitet ist.
Funktioniert es?
Das Sperren der dynamischen Kollisionserkennung führt zu einer korrekten Anzahl von Pfaden mit Selbstvermeidung bis zu n = 19, daher bin ich ziemlich sicher, dass das globale Layout funktioniert.
Die dynamische Kollisionserkennung führt zu konsistenten Ergebnissen, obwohl die Überprüfung von Biegungen in unterschiedlicher Reihenfolge (vorerst) fehlt.
Infolgedessen zählt das Programm Schlangen, die vom Kopf nach unten gebogen werden können (dh mit gefalteten Gelenken in der Reihenfolge des zunehmenden Abstands vom Kopf).
quelle