Regex (sabor ECMAScript), 392 358 328 224 206 165 bytes
As técnicas que precisam entrar em jogo para combinar os números de Fibonacci com um regex ECMAScript (em unário) estão muito distantes de como é feito melhor na maioria dos outros sabores de regex. A falta de referências ou recursões avançadas / aninhadas significa que é impossível contar diretamente ou manter um total em execução de qualquer coisa. A falta de atenção faz com que muitas vezes seja um desafio ter espaço suficiente para trabalhar.
Muitos problemas devem ser abordados de uma perspectiva totalmente diferente e parecem insolúveis até a chegada de algumas informações importantes. Obriga você a lançar uma rede muito mais ampla para descobrir quais propriedades matemáticas dos números com os quais você está trabalhando podem ser usadas para tornar um problema específico solucionável.
Em março de 2014, foi o que aconteceu com os números de Fibonacci. Olhando para a página da Wikipedia, inicialmente não consegui descobrir uma maneira, embora uma propriedade em particular parecesse tentadoramente próxima. Então o matemático teukon delineou um método que deixou bem claro que seria possível fazer isso, usando essa propriedade junto com outra. Ele estava relutante em realmente construir o regex. Sua reação quando fui em frente e fiz isso:
Você é louco! ... Eu pensei que você poderia fazer isso.
Como nas minhas outras postagens de regex matemática unária do ECMAScript, darei um aviso: eu recomendo aprender como resolver problemas matemáticos unários no regex do ECMAScript. Foi uma jornada fascinante para mim, e não quero estragá-la para quem potencialmente queira experimentá-la, especialmente aqueles interessados na teoria dos números. Consulte essa publicação para obter uma lista de problemas recomendados consecutivamente identificados por spoilers para resolver um por um.
Portanto , não leia mais, se não quiser que você estrague uma mágica de expressões regulares unárias . Se você quiser tentar descobrir essa mágica, eu recomendo começar resolvendo alguns problemas no regex ECMAScript, conforme descrito no post acima.
O desafio que enfrentei inicialmente: Um número inteiro positivo x é um número de Fibonacci se e somente se 5x 2 + 4 e / ou 5x 2 - 4 é um quadrado perfeito. Mas não há espaço para calcular isso em uma regex. O único espaço em que temos que trabalhar é o próprio número. Nem sequer temos espaço suficiente para multiplicar por 5 ou pegar o quadrado, quanto mais os dois.
A idéia da teukon sobre como resolvê-lo ( originalmente publicada aqui ):
O regex é apresentado com uma string do formulário ^x*$
, seja z o seu comprimento. Verifique se z é ou não um dos primeiros números de Fibonacci à mão (até 21 devem fazer). Se não é:
- Leia alguns números, a <b, de modo que b não seja maior que 2a.
- Use previsões futuras para criar a 2 , ab e b 2 .
- Afirme que 5a 2 + 4 ou 5a 2 - 4 é um quadrado perfeito (portanto, deve ser F n-1 para alguns n).
- Afirme que 5b 2 + 4 ou 5b 2 + 4 é um quadrado perfeito (então b deve ser F n ).
- Verifique se z = F 2n + 3 ou z = F 2n + 4 usando o anteriormente criado a 2 , ab e b 2 , e as identidades:
- F 2n-1 = F n 2 + F n-1 2
- F 2n = (2F n-1 + F n ) F n
Em resumo: essas identidades nos permitem reduzir o problema de verificar se um determinado número é Fibonacci e verificar se um par de números muito menores é Fibonacci. Um pouco de álgebra mostrará que para n grande o suficiente (n = 3 deve funcionar), F 2n + 3 > F n + 5F n 2 + 4, portanto sempre deve haver espaço suficiente.
E aqui está uma maquete do algoritmo em C que escrevi como teste antes de implementá-lo em regex.
Portanto, sem mais delongas, aqui está o 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})$
Experimente online!
E a versão comentada e bem impressa:
^(
(?=
(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.
)$
O algoritmo de multiplicação não é explicado nesses comentários, mas é brevemente explicado em um parágrafo dos meus abundantes números regex post .
Eu mantinha seis versões diferentes do regex de Fibonacci: quatro que aumentam do menor para a velocidade mais rápida e usam o algoritmo explicado acima, e outras duas que usam um algoritmo diferente, muito mais rápido, mas muito mais longo, que, como eu descobri, pode realmente retornar o índice de Fibonacci como uma correspondência (explicando que o algoritmo aqui está além do escopo deste post, mas é explicado na discussão original Gist ). Eu não acho que manteria tantas versões muito semelhantes de um regex novamente, porque na época eu estava fazendo todos os meus testes no PCRE e Perl, mas meu mecanismo de regex é rápido o suficiente para que as preocupações com a velocidade não sejam mais tão importantes (e se uma construção específica estiver causando um gargalo, posso adicionar uma otimização) - embora eu provavelmente mantenha novamente uma versão mais rápida e uma versão mais curta, se a diferença em velocidade eram grandes o suficiente.
A versão "retorne o índice de Fibonacci menos 1 como uma correspondência" (não muito golfe):
Experimente online!
Todas as versões estão no github com o histórico completo de consolidação das otimizações de golfe:
regex para correspondência de números de Fibonacci - curto, velocidade 0.txt (o menor, mas mais lento, como neste post)
regex para correspondência de números de Fibonacci - curto, velocidade 1.txt
regex para correspondência de números de Fibonacci - curto, velocidade 2.txt
regex para números Fibonacci correspondentes - curto, velocidade 3.txt
regex para números Fibonacci correspondentes - mais rápido.txt
regex para números Fibonacci correspondentes - retorno index.txt