Retina , 66 63 45 43 36 bytes
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Apesar do título dizer Retina, este é apenas um regex .NET simples que aceita representações unárias de números Loeschianos.
As entradas 999 e 1000 demoram menos de um segundo.
Experimente online! (A primeira linha permite um conjunto de testes separado por avanço de linha, e as próximas duas cuidam da conversão para unárias por conveniência.)
Explicação
A solução é baseada na classificação de que a entrada pode ser escrita como i*i + j*(i + j)
positiva i
e não negativa j
(já que não precisamos lidar com entrada 0
), e essa n*n
é apenas a soma dos primeiros n
números inteiros ímpares. Jogar golfe foi um exercício interessante nas referências futuras.
Uma "referência direta" é quando você coloca uma referência retroativa dentro do grupo a que se refere. É claro que isso não funciona quando o grupo é usado pela primeira vez, pois ainda não há nada a ser referenciado novamente, mas se você colocar isso em um loop, a referência anterior receberá a captura da iteração anterior a cada vez. Por sua vez, vamos criar uma captura maior a cada iteração. Isso pode ser usado para criar padrões muito compactos para coisas como números triangulares, quadrados e números de Fibonacci.
Como exemplo, usando o fato de que os quadrados são apenas somas dos primeiros n
números inteiros ímpares, podemos combinar uma entrada quadrada como esta:
(^.|..\1)+$
Na primeira iteração, ..\1
não pode funcionar, porque \1
ainda não tem um valor. Então começamos com a ^.
captura de um único personagem em grupo 1
. Nas iterações subseqüentes, ^.
não corresponde mais devido à âncora, mas agora ..\1
é válido. Ele corresponde a mais dois caracteres que a iteração anterior e atualiza a captura. Dessa forma, combinamos números ímpares crescentes, obtendo um quadrado após cada iteração.
Agora, infelizmente, não podemos usar essa técnica como está. Após a correspondência i*i
, precisamos obtê-lo i
também, para que possamos multiplicá-lo j
. Uma maneira simples (mas longa) de fazer isso é usar o fato de que a correspondência i*i
exige i
iterações, para que capturemos as i
coisas em grupo 1
. Agora poderíamos usar grupos de balanceamento para extrair isso i
, mas como eu disse, isso é caro.
Em vez disso, descobri uma maneira diferente de escrever essa "soma de números inteiros ímpares consecutivos" que também resulta i
em um grupo de captura no final. Claro que o i
número ímpar é justo 2i-1
. Isso nos permite aumentar a referência direta apenas 1 em cada iteração. Essa é essa parte:
^()(\1(?<1>.\1))+
Isso ()
apenas envia uma captura vazia para o grupo 1
(inicializando i
para 0
). Isso é praticamente equivalente ao da ^.|
solução simples acima, mas usar |
neste caso seria um pouco mais complicado.
Então nós temos o loop principal (\1(?<1>.\1))
. \1
corresponde ao anterior i
, (?<1>.\1)
em seguida, atualiza grupo 1
com i+1
. Em termos do novo i
, acabamos de combinar 2i-1
personagens. Exatamente o que precisamos.
Quando terminamos, combinamos algum quadrado i*i
e o grupo 1
ainda possui i
caracteres.
A segunda parte está mais próxima da correspondência quadrada simples que mostrei acima. Vamos ignorar a referência anterior 1
por enquanto:
(.(?(4).\1))*
É basicamente o mesmo que (^.|..\4)*
, exceto que não podemos fazer uso ^
porque não estamos no início da string. Em vez disso, usamos um condicional, para corresponder ao adicional .\1
somente quando já usamos o grupo 4
. Mas, na verdade, isso é exatamente o mesmo. Isso nos dá j*j
.
A única coisa que falta é o j*i
termo. Combinamos isso com o j*j
uso do fato de que a j*j
computação ainda faz j
iterações. Portanto, para cada iteração, também avançamos o cursor i
com \1
. Só precisamos ter certeza de não escrever isso em grupo 4
, porque isso interferiria na correspondência de números ímpares consecutivos. É assim que chegamos ao:
(\1(.(?(4).\1)))*