Regex (saveur ECMAScript), 392 358 328 224 206 165 octets
Les techniques qui doivent être utilisées pour faire correspondre les nombres de Fibonacci avec une expression rationnelle ECMAScript (unaire) sont bien loin de la meilleure façon de la faire dans la plupart des autres saveurs d’expression rationnelle. L'absence de références arrière ou de récursion imbriquées / imbriquées signifie qu'il est impossible de compter directement ou de conserver un total cumulé. Le manque de repères rend souvent difficile le fait de disposer de suffisamment d’espace pour travailler.
De nombreux problèmes doivent être abordés sous un angle totalement différent et ne semblent pas résolus avant l’arrivée de certaines informations essentielles. Cela vous oblige à utiliser un réseau beaucoup plus large pour déterminer quelles propriétés mathématiques des nombres avec lesquels vous travaillez pourraient être utilisées pour résoudre un problème particulier.
En mars 2014, c'est ce qui s'est passé pour les chiffres de Fibonacci. En regardant la page Wikipedia, je ne pouvais pas au départ trouver un moyen, même si une propriété en particulier semblait extrêmement proche. Ensuite, le mathématicien Teukon a présenté une méthode indiquant clairement qu'il serait possible de le faire, en utilisant cette propriété avec une autre. Il était réticent à réellement construire la regex. Sa réaction quand je suis allé de l'avant et l'a fait:
Vous êtes fou! ... Je pensais que tu pourrais faire ça.
Comme pour mes autres publications ECMAScript sur les expressions mathématiques unaires, je tiens à vous avertir: je vous recommande vivement d'apprendre à résoudre des problèmes mathématiques unaires dans ECMAScript regex. Ce voyage a été fascinant pour moi et je ne veux pas le gâter pour ceux qui voudraient éventuellement l'essayer eux-mêmes, en particulier ceux qui s'intéressent à la théorie des nombres. Voir ce post pour une liste de problèmes recommandés consécutivement étiquetés spoiler à résoudre un par un.
Donc , ne lisez pas plus loin si vous ne voulez pas que la magie des expressions rationnelles unaires vous soit gâtée . Si vous voulez vraiment essayer de découvrir cette magie vous-même, je vous recommande vivement de commencer par résoudre certains problèmes de la regex ECMAScript, comme indiqué dans l'article ci-dessus.
Le défi que j’avais initialement relevé: un entier positif x est un nombre de Fibonacci si et seulement si 5x 2 + 4 et / ou 5x 2 - 4 est un carré parfait. Mais il n'y a pas de place pour calculer cela dans une regex. Le seul espace dans lequel nous devons travailler est le numéro lui-même. Nous n'avons même pas assez de place pour multiplier par 5 ou prendre la place, sans parler des deux.
L'idée de teukon sur la façon de le résoudre ( posté à l'origine ici ):
Le regex est présenté avec une chaîne de la forme ^x*$
, soit z sa longueur. Vérifiez si z est ou non l’un des premiers nombres de Fibonacci à la main (jusqu’à 21 devrait suffire). Si ce n'est pas le cas:
- Lire quelques nombres, a <b, tels que b n’est pas plus grand que 2a.
- Utilisez des prévisions pour construire un 2 , un ab et un b 2 .
- Affirmer que 5a 2 + 4 ou 5a 2 - 4 est un carré parfait (il faut donc être F n-1 pour certains n).
- Affirmez que 5b 2 + 4 ou 5b 2 + 4 est un carré parfait (donc b doit être F n ).
- Vérifiez que z = F 2n + 3 ou z = F 2n + 4 en utilisant les versions antérieures a 2 , ab et b 2 et les identités:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n-1 + F n ) F n
En bref: ces identités nous permettent de réduire le problème de vérification d’un nombre donné de Fibonacci, mais également de vérifier qu’une paire de nombres beaucoup plus petits est bien de Fibonacci. Un peu d'algèbre montrera que pour n assez grand (n = 3 devrait faire), F 2n + 3 > F n + 5F n 2 + 4 donc il devrait toujours y avoir assez d'espace.
Et voici une maquette de l'algorithme en C que j'ai écrit comme test avant de l'implémenter dans une regex.
Donc, sans plus tarder, voici la regex:
^((?=(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})$
Essayez-le en ligne!
Et la jolie version commentée et commentée:
^(
(?=
(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.
)$
L'algorithme de multiplication n'est pas expliqué dans ces commentaires, mais est brièvement expliqué dans un paragraphe de mon article sur les regex en nombres abondants .
Je maintenais six versions différentes de la regex de Fibonacci: quatre qui vont du plus court au plus rapide et qui utilisent l'algorithme expliqué ci-dessus, et deux autres qui utilisent un algorithme différent, beaucoup plus rapide mais beaucoup plus long, qui peut effectivement renvoyer l'index de Fibonacci en tant que correspondance (expliquer cet algorithme dépasse le cadre de cet article, mais il est expliqué dans la discussion d'origine Gist ). Je ne pense pas que je maintiendrais autant de versions très similaires d’une regex, car à l’époque, je faisais tous mes tests sous PCRE et Perl, mais mon moteur de regex est assez rapide pour que les soucis de vitesse ne soient plus aussi importants (et si une construction en particulier est à l'origine d'un goulot d'étranglement, je peux lui ajouter une optimisation) - bien que je conserverais probablement encore une version la plus rapide et une version la plus courte, à la différence en vitesse étaient assez gros.
La version "retourne l'indice de Fibonacci moins 1 en match" (peu joué au golf):
Essayez-le en ligne!
Toutes les versions sont sur github avec l'historique complet des optimisations de golf:
regex pour faire correspondre les nombres de Fibonacci - court, vitesse 0.txt (le plus court mais le plus lent, comme dans cet article)
regex pour faire correspondre les nombres de Fibonacci - court, vitesse 1.txt
regex pour faire correspondre les nombres de Fibonacci - court, vitesse 2.txt
regex pour numéros
correspondants de Fibonacci - expression rationnelle courte et rapide 3.txt pour les numéros correspondants de Fibonacci - regex plus rapide.txt
pour les numéros correspondants de Fibonacci - retour index.txt