Die period
einer Zeichenfolge ist die kürzeste Verschiebung ungleich Null, sodass die Zeichenfolge mit sich selbst übereinstimmt und alle überhängenden Teile ignoriert werden. So hat zum Beispiel abcabcab
Punkt 3
. Konventionell sagen wir, dass wenn es keine solche Verschiebung gibt, eine Zeichenfolge eine Periode hat, die ihrer Länge entspricht. Also die Periode von abcde
ist 5
und die Periode von a
ist 1
.
In formaleren Begriffen ist die Periode eines Strings S
das Minimum, i > 0
so dass S[1,n-i] == S[i+1,n]
(Indizierung von 1
).
Für eine gegebene Zeichenfolge S mit einer Potenz von zwei Längen berechnen wir die Periode aller ihrer Präfixe der Potenz mit zwei Längen. Betrachten Sie zum Beispiel S = abcabcab
. Die Perioden, die wir berechnen werden, sind:
'a', 1
'ab', 2
'abca', 3
'abcabcab', 3
Wir werden in der Tat nur das Array von Perioden ausgeben, das heißt [1, 2, 3, 3]
.
n
Berücksichtigen Sie für eine gegebene positive Zweierpotenz alle möglichen binären Zeichenfolgen S
. Denken Sie daran, dass eine binäre Zeichenfolge einfach eine Zeichenfolge aus 1
s und 0
s ist, sodass es genau 2^n
solche Zeichenfolgen gibt (dh 2
nach Potenz n
). Für jeden können wir dieses Array von Perioden berechnen.
Die Herausforderung besteht darin, Code zu schreiben, der
n
(eine Zweierpotenz) als Eingabe verwendet und berechnet, wie viele unterschiedliche solche Arrays es gibt.
Die Antworten für n = 1, 2, 4, 8, 16, 32, 64, 128
sind:
1, 2, 6, 32, 320, 6025, 216854, 15128807
Der vollständige Satz unterschiedlicher Periodenarrays für n = 4
ist:
1, 1, 1
1, 1, 3
1, 1, 4
1, 2, 2
1, 2, 3
1, 2, 4
Ergebnis
Ich werde Ihren Code 10 Minuten lang auf meinem Computer mit Ubuntu ausführen. Ihre Punktzahl ist die größte, n
für die Ihr Code in dieser Zeit endet. Im Falle eines Unentschieden n
gewinnt die Antwort, die den gemeinsamen größten Schnellsten vervollständigt . Für den Fall, dass es innerhalb von 1 Sekunde zu einem Unentschieden kommt, gewinnt die erste Antwort.
Sprachen und Bibliotheken
Sie können jede verfügbare Sprache und Bibliothek verwenden, die Sie mögen. Bitte geben Sie eine vollständige Erklärung an, wie Sie Ihren Code unter Linux ausführen / kompilieren können, wenn dies überhaupt möglich ist. "
Ihr Code sollte tatsächlich die Antworten berechnen und nicht nur vorberechnete Werte ausgeben.
Führende Einträge
- 2 Minuten und 21 Sekunden für n = 128 in C # von Peter Taylor
- 9 Sekunden für n = 32 in Rust von isaacg
n
, würden Sie ihn akzeptieren? Es ist nicht genau definiert, wo die Grenze zwischen Hardcodierung und tatsächlichem Computing liegt.abcab
. Alle bis auf die letzten 3 Buchstaben sindabcab
. Diese stimmen überein, und das Entfernen einer kleineren Anzahl von Buchstaben stimmt nicht überein.Antworten:
C #, n = 128 in ungefähr 2:40
Das Erweitern auf n = 256 würde das Wechseln zu
BigInteger
für die Masken erfordern , was die Leistung wahrscheinlich zu sehr beeinträchtigt, als dass n = 128 ohne neue Ideen durchgehen könnte, geschweige denn n = 256.Kompilieren Sie unter Linux mit
mono-csc
und führen Sie mit ausmono
.Grundlegende Erklärung
Ich werde keine zeilenweise Dissektion durchführen, sondern nur einen Überblick über die Konzepte.
Als Faustregel gehe ich gerne in der Größenordnung von 2 50 Elementen in einem kombinatorischen Brute-Force-Programm durch. Um zu n = 128 zu gelangen, muss daher ein Ansatz verwendet werden, der nicht jede Bitfolge analysiert. Anstatt von Bitfolgen zu Periodensequenzen vorwärts zu arbeiten, arbeite ich rückwärts: Gibt es bei einer Periodensequenz eine Bitfolge, die dies realisiert? Für n = 2 x gibt es eine einfache Obergrenze von 2 x (x + 1) / 2 Periodensequenzen (vs 2 2 x Bitstrings).
Einige der Argumente verwenden das String-Periodizitäts-Lemma :
Wlog Ich gehe davon aus, dass alle betrachteten Bitstrings mit beginnen
0
.Bei einer Periodenfolge, in der die Periode des Präfixes der Länge 2 i ( immer) angegeben ist, gibt es einige einfache Beobachtungen zu möglichen Werten von :
[p1 p2 ... pk]
pi
p0 = 1
pk+1
pk+1 ≥ pk
da eine Periode eines StringsS
auch eine Periode eines beliebigen Präfixes von istS
.pk+1 = pk
ist immer eine mögliche Erweiterung: Wiederholen Sie einfach dieselbe primitive Zeichenfolge für doppelt so viele Zeichen.2k < pk+1 ≤ 2k+1
ist immer eine mögliche Erweiterung. Es reicht aus, dies zu zeigen, da eine aperiodische Zeichenfolge mit einer Länge zu einer aperiodischen Zeichenfolge mit Länge erweitert werden kann, indem ein Buchstabe angehängt wird, der nicht der erste Buchstabe ist.pk+1 = 2k+1
L
L+1
Nehmen Sie eine Zeichenfolge mit
Sx
einer Länge von 2 k, deren Periode ist, und betrachten Sie die Zeichenfolge mit einer Länge von 2 k + 1 . Offensichtlich hat eine Periode 2 k +1. Angenommen, seine Periode ist kleiner.pk
SxyS
SxyS
q
Dann ist also durch die Periodizität das Lemma auch eine Periode von , und da der größte Teiler kleiner oder gleich seinen Argumenten ist und die kleinste Periode ist, müssen wir ein geeigneter Faktor von 2 k + 1 sein. Da sein Quotient nicht 2 sein kann, haben wir .
2k+1 + q ≤ 2k+1+1 ≤ 2k+1 + gcd(2k+1, q)
gcd(2k+1, q)
SxyS
q
q
q ≤ (2k+1)/3
Nun, da ist eine Periode davon muss eine Periode von sein . Aber die Zeit von ist . Wir haben zwei Fälle:
q ≤ 2k
SxyS
Sx
Sx
pk
gcd(pk, q) = pk
oder gleichwertig genau in .pk
q
pk + q > 2k + gcd(pk, q)
damit das Periodizitäts-Lemma keine kleinere Periode erzwingt.Betrachten Sie zunächst den zweiten Fall. , im Widerspruch zur Definition von als der Zeitraum von . Deshalb sind wir zu dem Schluss gezwungen, dass dies ein Faktor ist .
pk > 2k + gcd(pk, q) - q ≥ 2k+1 - q ≥ 2k+1 - (2k+1)/3 ≥ 2q
pk
Sx
pk
q
Da es sich jedoch
q
um eine Periode vonSx
und um die Periode von handelt , ist das Präfix der Länge nur eine Kopie des Präfixes der Länge , sodass wir sehen, dass dies auch eine Periode von ist .pk
Sx
q
q/pk
pk
pk
SxyS
Daher ist die Periode von
SxyS
entweder oder 2 k + 1. Wir haben aber zwei Möglichkeiten für ! Höchstens eine Wahl von ergibt eine Periode , so dass mindestens eine Periode 2 k + 1 ergibt . QED.pk
y
y
pk
Das Periodizitäts-Lemma erlaubt es uns, einige der verbleibenden möglichen Erweiterungen abzulehnen.
Jede Erweiterung, die keinen Quick-Accept- oder Quick-Reject-Test bestanden hat, muss konstruktiv getestet werden.
Die Konstruktion eines Bitstrings bei gegebener Periodenfolge ist im Wesentlichen ein Problem der Erfüllbarkeit, hat jedoch eine große Struktur. Es gibt einfache Gleichheitsbeschränkungen, die von jeder Präfixperiode impliziert werden, daher verwende ich eine Union-Set- Datenstruktur, um Bits zu unabhängigen Clustern zu kombinieren. Dies war genug, um n = 64 anzugehen, aber für n = 128 war es notwendig, weiter zu gehen. Ich verwende zwei nützliche Argumentationslinien:
2k - pk
M
ist und das Präfix der Länge einen Punkt hat, muss das Präfix der Länge mit enden . Dies ist gerade in den Fällen am wirkungsvollsten, in denen sonst die meisten unabhängigen Cluster vorhanden wären, was praktisch ist.01M-1
L > M
L
L
1M
M
ist und das Präfix der Länge einen Punkt mit hat und mit endet , muss es tatsächlich mit enden . Dies ist im entgegengesetzten Extrem am stärksten, wenn die Periodensequenz mit vielen beginnt.0M
L > M
L - d
d < M
0d
10d
Wenn wir keinen unmittelbaren Widerspruch erhalten, indem wir den Cluster mit dem ersten Bit (angenommen Null) als Eins erzwingen, erzwingen wir (mit einigen Mikrooptimierungen) die möglichen Werte für die ungezwungenen Cluster. Beachten Sie, dass die Reihenfolge in absteigender Anzahl von Einsen ist, denn wenn das
i
th- Bit ein Eins ist, kann die Periode nicht sein,i
und wir möchten Perioden vermeiden, die kürzer sind als diejenigen, die bereits durch das Clustering erzwungen werden. Ein Abstieg erhöht die Wahrscheinlichkeit, frühzeitig einen gültigen Auftrag zu finden.quelle
Rust, 32, 10s
11s29sauf meinem LaptopNennen Sie es mit der Bitgröße als Befehlszeilenargument.
Clevere Techniken: Stellen Sie Bitstrings direkt als Zahlen dar und verwenden Sie Bittwiddling, um nach Zyklen zu suchen. Suchen Sie nur die erste Hälfte der Bitstrings, die mit 0 beginnen, da das Array der Perioden eines Bitstrings und seine Umkehrung (0s gegen 1s getauscht) identisch sind. Wenn bereits alle Möglichkeiten für die endgültige Position vorhanden sind, suche ich nicht danach.
Klügeres Zeug:
Um jeden Block zu deduplizieren (Strings, bei denen die erste Hälfte der Bits gleich ist), verwende ich einen Bitvektor, der viel schneller als ein Hashset ist, da die endgültigen Zykluslängen kein Hashing benötigen.
Außerdem überspringe ich die ersten Schritte der Zyklusprüfung, da ich weiß, dass der letzte Zyklus nicht kürzer sein kann als der vorletzte Zyklus.
Nach vielen Profilen kann ich jetzt feststellen, dass fast die gesamte Zeit produktiv genutzt wird, sodass von hier aus algorithmische Verbesserungen erforderlich sind, denke ich. Ich habe auch auf 32-Bit-Ganzzahlen umgestellt, um etwas mehr Zeit zu sparen.
bit-vec = "0.4.4"
Geben Sie Ihre Cargo.toml einWenn Sie dies ausführen möchten, klonen Sie Folgendes: github.com/isaacg1/cycle, dann
Cargo build --release
erstellen und dannCargo run --release 32
ausführen.quelle
time
gibt es 27 Benutzersekunden auf meinem Laptop.Rust , 16
Probieren Sie es online aus!
Zusammenstellung:
rustc -O <name>.rs
Die Zeichenfolge wird als Bool-Vektor implementiert.
Die
next
Funktion durchläuft die Kombinationen.Der
find_period
nimmt ein Bool-Slice und gibt den Punkt zurück.Das
compute_count_array
erledigt den Job für jede "Potenz von zwei" -Subsequenz jeder Kombination von Bools.Theoretisch wird kein Überlauf erwartet, bis
2^n
der u64-Maximalwert überschritten wird, dn > 64
. H. Diese Grenze könnte mit einem teuren Test für s = [wahr, wahr, ..., wahr] erreicht werden.Schlechte Nachrichten sind: Es gibt 317 für n = 16 zurück, aber ich weiß nicht warum. Ich weiß auch nicht, ob es in zehn Minuten für n = 32 sein wird, da das
Vec<bool>
nicht für diese Art der Berechnung optimiert ist.BEARBEITEN
Ich habe es geschafft, das Limit von 64 für zu entfernen
n
. Jetzt stürzt es erst ab, wennn
die maximale Ganzzahl usize größer ist.Ich habe herausgefunden, warum der vorherige Code 317 für zurückgegeben hat
n=32
. Ich zählte Sätze von Perioden und keine Anordnungen von Perioden. Es gab drei Arrays mit denselben Elementen:Jetzt funktioniert es. Es ist immer noch langsam, aber es funktioniert.
quelle
C - 16
Bei Werten über 16 cuz Überlauf schlägt dies fehl.
Ich habe keine Ahnung, wie schnell dies läuft, weil ich auf einem Chromebook bin, das es auf repl.it ausführt.
Implementiert einfach die Frage beim Lesen, durchläuft alle Bitfolgen, berechnet die Periodenarrays und prüft, ob sie bereits gezählt wurden.
Kompilieren Sie es einfach mit gcc usw.
quelle
16
dann , wenn der Code geändert wurde , so dass die beidenmalloc
s warenmalloc(...int*))
und...**
jeweils16
gedruckt ,Answer: 320
wie erwartet, jedoch32
gedrucktAnswer: 0
(und ziemlich schnell).n = 8
aber nachdem das Ergebnis gedruckt wurde, bedeutet dies, dass der Stapel beschädigt ist. Wahrscheinlich schreiben Sie irgendwo jenseits der zugewiesenen Speicherblöcke.Haskell
Kompilieren mit
ghc -O2
. Probieren Sie es online aus!Läuft in weniger als 0,1 Sekunden auf meiner 6 Jahre alten Laptop-Hardware für
n=16
.n=32
dauert9992min, also bin ich Faktor 9 oder 10 aus. Ich habe versucht, die Punkte in einer Nachschlagetabelle zwischenzuspeichern, damit ich sie nicht immer wieder neu berechnen muss, aber auf meinem 4-GB-Computer geht schnell der Speicher aus.quelle
Python 2 (PyPy), 16
quelle
osboxes@osboxes:~/python$ python ascii_user.py 16 None
[C ++], 32, 4 Minuten
quelle