Regex (ECMAScript-Variante), 392 358 328 224 206 165 Bytes
Die Techniken, die erforderlich sind, um Fibonacci-Zahlen mit einem ECMAScript-Regex (in Unary) abzugleichen, sind weit davon entfernt, wie es in den meisten anderen Regex-Varianten am besten funktioniert. Das Fehlen von vorwärts / verschachtelten Rückverweisen oder Rekursionen bedeutet, dass es unmöglich ist, eine laufende Summe von irgendetwas direkt zu zählen oder zu behalten. Das Fehlen von Lookbehind macht es oft zu einer Herausforderung, auch genügend Platz zum Arbeiten zu haben.
Viele Probleme müssen aus einer ganz anderen Perspektive angegangen werden und scheinen unlösbar, bis einige wichtige Erkenntnisse vorliegen. Es zwingt Sie, ein viel breiteres Netz zu ziehen, um herauszufinden, welche mathematischen Eigenschaften der Zahlen, mit denen Sie arbeiten, verwendet werden können, um ein bestimmtes Problem lösbar zu machen.
Im März 2014 passierte dies für Fibonacci-Zahlen. Auf der Wikipedia-Seite konnte ich anfangs keinen Weg finden, obwohl eine bestimmte Eigenschaft verblüffend nahe lag. Dann skizzierte der Mathematiker Teukon eine Methode, die klar machte, dass es möglich wäre, diese Eigenschaft zusammen mit einer anderen zu verwenden. Er zögerte, den Regex tatsächlich zu konstruieren. Seine Reaktion, als ich weitermachte und es tat:
Du bist verrückt! ... Ich dachte, Sie könnten das tun.
Wie bei meinen anderen unären ECMAScript-Postings für mathematische Regex werde ich eine Warnung geben: Ich empfehle dringend, zu lernen, wie man unäre mathematische Probleme in ECMAScript-Regex löst. Es war eine faszinierende Reise für mich, und ich möchte sie keinem verderben, der sie möglicherweise selbst ausprobieren möchte, insbesondere nicht jenen, die sich für Zahlentheorie interessieren. In diesem Beitrag finden Sie eine Liste der nacheinander mit Spoiler-Tags gekennzeichneten empfohlenen Probleme, die Sie nacheinander lösen können.
So liest keine weiteren , wenn Sie nicht einig einstellige regex Magie für Sie verwöhnen wollen . Wenn Sie versuchen möchten, diese Magie selbst herauszufinden, empfehle ich dringend, zunächst einige Probleme in ECMAScript regex zu lösen, wie in dem oben verlinkten Beitrag beschrieben.
Die Herausforderung, vor der ich anfangs stand: Eine positive ganze Zahl x ist genau dann eine Fibonacci-Zahl, wenn 5x 2 + 4 und / oder 5x 2 - 4 ein perfektes Quadrat sind. Aber es gibt keinen Raum, um dies in einer Regex zu berechnen. Der einzige Raum, in dem wir arbeiten müssen, ist die Nummer selbst. Wir haben nicht einmal genug Platz, um mit 5 zu multiplizieren oder das Quadrat zu nehmen, geschweige denn mit beiden.
Teukon's Idee, wie man es löst ( ursprünglich hier gepostet ):
Der reguläre Ausdruck wird mit einer Zeichenfolge der Form dargestellt ^x*$
, z sei seine Länge. Prüfen Sie, ob z eine der ersten Fibonacci-Zahlen ist (bis zu 21 sollten reichen). Wenn es das nicht ist:
- Lesen Sie ein paar Zahlen, a <b, so dass b nicht größer als 2a ist.
- Verwenden Sie die Vorausschau, um a 2 , ab und b 2 zu erstellen .
- Stellen Sie sicher, dass entweder 5a 2 + 4 oder 5a 2 - 4 ein perfektes Quadrat ist (also muss a für einige n F n-1 sein ).
- Stellen Sie sicher, dass entweder 5b 2 + 4 oder 5b 2 + 4 ein perfektes Quadrat ist (also muss b F n sein ).
- Überprüfen Sie, ob z = F 2n + 3 oder z = F 2n + 4 ist, indem Sie die zuvor erstellten a 2 , ab und b 2 und die Identitäten verwenden:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n - 1 + F n ) F n
Kurz gesagt: Mit diesen Identitäten können wir das Problem der Überprüfung, ob eine bestimmte Zahl Fibonacci ist, auf die Überprüfung reduzieren, ob es sich bei einem Paar sehr viel kleinerer Zahlen um Fibonacci handelt. Eine kleine Algebra wird zeigen, dass für groß genug n (n = 3 sollte reichen) F 2n + 3 > F n + 5F n 2 + 4, so dass immer genügend Platz vorhanden sein sollte.
Und hier ist ein Modell des Algorithmus in C, das ich als Test geschrieben habe, bevor ich ihn in Regex implementiert habe.
Also ohne weiteres, hier ist der reguläre Ausdruck:
^((?=(x*).*(?=x{4}(x{5}(\2{5}))(?=\3*$)\4+$)(|x{4})(?=xx(x*)(\6x?))\5(x(x*))(?=(\8*)\9+$)(?=\8*$\10)\8*(?=(x\2\9+$))(x*)\12)\7\11(\6\11|\12)|x{0,3}|x{5}|x{8}|x{21})$
Probieren Sie es online!
Und die schön gedruckte, kommentierte Fassung:
^(
(?=
(x*) # \2+1 = potential number for which 5*(\2+1)^2 ± 4
# is a perfect square; this is true iff \2+1 is a Fibonacci
# number. Outside the surrounding lookahead block, \2+1 is
# guaranteed to be the largest number for which this is true
# such that \2 + 5*(\2+1)^2 + 4 fits into the main number.
.*
(?= # tail = (\2+1) * (\2+1) * 5 + 4
x{4}
( # \3 = (\2+1) * 5
x{5}
(\2{5}) # \4 = \2 * 5
)
(?=\3*$)
\4+$
)
(|x{4}) # \5 = parity - determined by whether the index of Fibonacci
# number \2+1 is odd or even
(?=xx (x*)(\6 x?)) # \6 = arithmetic mean of (\2+1) * (\2+1) * 5 and \8 * \8,
# divided by 2
# \7 = the other half, including remainder
\5
# require that the current tail is a perfect square
(x(x*)) # \8 = potential square root, which will be the square root
# outside the surrounding lookahead; \9 = \8-1
(?=(\8*)\9+$) # \10 = must be zero for \8 to be a valid square root
(?=\8*$\10)
\8*
(?=(x\2\9+$)) # \11 = result of multiplying \8 * (\2+1), where \8 is larger
(x*)\12 # \12 = \11 / 2; the remainder will always be the same as it
# is in \7, because \8 is odd iff \2+1 is odd
)
\7\11
(
\6\11
|
\12
)
|
x{0,3}|x{5}|x{8}|x{21} # The Fibonacci numbers 0, 1, 2, 3, 5, 8, 21 cannot be handled
# by our main algorithm, so match them here; note, as it so
# happens the main algorithm does match 13, so that doesn't
# need to be handled here.
)$
Der Multiplikationsalgorithmus wird in diesen Kommentaren nicht erläutert, sondern in einem Abschnitt meines Postens mit vielen regulären Zahlen kurz erläutert .
Ich habe sechs verschiedene Versionen des Fibonacci-Regex beibehalten: vier, die von der kürzesten Länge zur schnellsten Geschwindigkeit ratschen und den oben erläuterten Algorithmus verwenden, und zwei andere, die einen anderen, viel schnelleren, aber viel längeren Algorithmus verwenden, den ich tatsächlich zurückgeben kann Der Fibonacci-Index als Übereinstimmung (die Erklärung dieses Algorithmus hier würde den Rahmen dieses Beitrags sprengen, wird jedoch in der ursprünglichen Diskussion Gist erläutert ). Ich glaube nicht, dass ich so viele sehr ähnliche Versionen eines regulären Ausdrucks wieder beibehalten würde, weil ich zu der Zeit alle meine Tests in PCRE und Perl durchgeführt habe, aber meine reguläre Ausdrucksmaschine ist schnell genug, dass Geschwindigkeitsbedenken nicht mehr so wichtig sind (und wenn ein bestimmtes Konstrukt einen Engpass verursacht, kann ich eine Optimierung hinzufügen) - obwohl ich wahrscheinlich wieder eine schnellste und eine kürzeste Version beibehalten würde, wenn der Unterschied wäre in der Geschwindigkeit waren groß genug.
Die Version "Gib den Fibonacci-Index minus 1 als Match zurück" (nicht stark golfen):
Probieren Sie es online!
Alle Versionen sind auf Github mit der vollständigen Commit-Historie der Golfoptimierungen:
regex zum Abgleichen Fibonacci - Zahlen - kurze, Geschwindigkeit 0.txt (kürzesten aber langsamste, wie in dieser Veröffentlichung)
regex zum Abgleichen Fibonacci - Zahlen - kurzen, Geschwindigkeit 1.txt
regex zum Abgleichen Fibonacci - Zahlen - kurze, Geschwindigkeit 2.txt
regex für passende Fibonacci-Zahlen - kurz, Geschwindigkeit 3.txt regulärer Ausdruck
für passende Fibonacci-Zahlen - schnellste.txt regulärer Ausdruck
für passende Fibonacci-Zahlen - Rückgabe index.txt