Regex (sapore ECMAScript), 392 358 328 224 206 165 byte
Le tecniche che devono entrare in gioco per abbinare i numeri di Fibonacci con una regex ECMAScript (in unario) sono molto lontane da come è meglio farlo nella maggior parte degli altri sapori regex. La mancanza di backreferenze o ricorsioni in avanti / nidificate significa che è impossibile contare direttamente o mantenere un totale parziale di qualsiasi cosa. La mancanza di lookbehind rende spesso una sfida anche avere abbastanza spazio per lavorare.
Molti problemi devono essere affrontati da una prospettiva completamente diversa e sembrano irrisolvibili fino all'arrivo di alcune intuizioni chiave. Ti costringe a lanciare una rete molto più ampia per scoprire quali proprietà matematiche dei numeri con cui stai lavorando potrebbero essere utilizzate per rendere risolvibile un particolare problema.
Nel marzo 2014, questo è ciò che è accaduto per i numeri di Fibonacci. Guardando la pagina di Wikipedia, inizialmente non riuscivo a trovare un modo, sebbene una particolare proprietà sembrasse allettante. Quindi il matematico Teukon ha delineato un metodo che ha chiarito che sarebbe possibile farlo, usando quella proprietà insieme a un'altra. Era riluttante a costruire effettivamente la regex. La sua reazione quando sono andato avanti e l'ho fatto:
Sei pazzo! ... Pensavo che potresti farlo.
Come con gli altri miei post di regex matematici unari ECMAScript, darò un avvertimento: consiglio vivamente di imparare a risolvere i problemi matematici unari in regex ECMAScript. È stato un viaggio affascinante per me e non voglio rovinarlo per nessuno che potrebbe potenzialmente provarlo da solo, in particolare quelli con un interesse per la teoria dei numeri. Vedi quel post per un elenco di problemi raccomandati consecutivamente con tag spoiler da risolvere uno per uno.
Quindi non leggere oltre se non vuoi che qualche magia regex unaria sia viziata per te . Se vuoi fare un tentativo per capire da solo questa magia, ti consiglio vivamente di iniziare risolvendo alcuni problemi nella regex di ECMAScript come indicato in quel post collegato sopra.
La sfida che ho affrontato inizialmente: un numero intero positivo x è un numero di Fibonacci se e solo se 5x 2 + 4 e / o 5x 2 - 4 è un quadrato perfetto. Ma non c'è spazio per calcolare questo in una regex. L'unico spazio in cui dobbiamo lavorare è il numero stesso. Non abbiamo nemmeno abbastanza spazio per moltiplicare per 5 o prendere il quadrato, figuriamoci entrambi.
L'idea di teukon su come risolverlo ( originariamente pubblicato qui ):
Il regex è presentato con una stringa del modulo ^x*$
, sia z la sua lunghezza. Controlla se z è uno dei primi numeri di Fibonacci a mano (dovrebbero fare fino a 21). Se non è:
- Leggi un paio di numeri, a <b, in modo tale che b non sia maggiore di 2a.
- Usa le previsioni per costruire a 2 , ab e b 2 .
- Asserire che 5a 2 + 4 o 5a 2 - 4 è un quadrato perfetto (quindi a deve essere F n-1 per qualche n).
- Asserire che 5b 2 + 4 o 5b 2 + 4 è un quadrato perfetto (quindi b deve essere F n ).
- Verifica che z = F 2n + 3 o z = F 2n + 4 utilizzando il precedente a a 2 , ab e b 2 e le identità:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n-1 + F n ) F n
In breve: queste identità ci consentono di ridurre il problema di verificare che un dato numero sia Fibonacci, controllando che una coppia di numeri molto più piccoli sia Fibonacci. Una piccola algebra mostrerà che per n abbastanza grande (n = 3 dovrebbe fare), F 2n + 3 > F n + 5F n 2 + 4, quindi dovrebbe esserci sempre spazio sufficiente.
Ed ecco un mockup dell'algoritmo in C che ho scritto come test prima di implementarlo in regex.
Quindi, senza ulteriori indugi, ecco 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})$
Provalo online!
E la versione stampata e commentata:
^(
(?=
(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'algoritmo di moltiplicazione non è spiegato in quei commenti, ma è brevemente spiegato in un paragrafo dei miei post postgex di numeri abbondanti .
Stavo mantenendo sei diverse versioni del regex di Fibonacci: quattro che cricchettano dalla lunghezza più breve alla velocità più veloce e usano l'algoritmo spiegato sopra, e altri due che usano un algoritmo diverso, molto più veloce ma molto più lungo, che come ho trovato può effettivamente tornare l'indice di Fibonacci come una corrispondenza (spiegando che l'algoritmo qui va oltre lo scopo di questo post, ma è spiegato nella discussione originale Gist ). Non credo che manterrei di nuovo molte versioni molto simili di un regex, perché al momento stavo facendo tutti i miei test su PCRE e Perl, ma il mio motore regex è abbastanza veloce che i problemi di velocità non sono più così importanti (e se un particolare costrutto sta causando un collo di bottiglia, posso aggiungere un'ottimizzazione per esso) - anche se probabilmente manterrei di nuovo una versione più veloce e una versione più corta, se la differenza in velocità erano abbastanza grandi.
La versione "restituisce l'indice di Fibonacci meno 1 come una partita" (non fortemente giocata a golf):
Provalo online!
Tutte le versioni sono su github con la cronologia completa delle ottimizzazioni del golf:
regex per l'abbinamento dei numeri di Fibonacci - short, speed 0.txt (il più breve ma più lento, come in questo post)
regex per l'abbinamento dei numeri di Fibonacci - short, speed 1.txt
regex per l'abbinamento dei numeri di Fibonacci - short, speed 2.txt
regex per numeri di Fibonacci corrispondenti - breve,
regex 3.txt di velocità per i numeri di Fibonacci corrispondenti - regex fast.txt
per numeri di Fibonacci corrispondenti - return index.txt