Retina , 66 63 45 43 36 octets
^()(\1(?<1>.\1))+(\1(.(?(4).\4)))*$
Malgré le titre disant Retina, il s’agit simplement d’une expression rationnelle .NET qui accepte les représentations unaires des nombres de Loeschian.
Les entrées 999 et 1000 prennent bien moins d'une seconde.
Essayez-le en ligne! (La première ligne active une suite de tests séparée par des sauts de ligne et les deux suivantes traitent de la conversion en unaire pour des raisons de commodité.)
Explication
La solution est basée sur la classification selon laquelle l'entrée peut être écrite de manière i*i + j*(i + j)
positive i
et non négative j
(puisque nous n'avons pas à gérer l'entrée 0
), et c'est n*n
simplement la somme des premiers n
entiers impairs. Le golf était un exercice intéressant dans les références en avant.
Une "référence en aval" est une référence en arrière dans le groupe auquel elle fait référence. Bien sûr, cela ne fonctionne pas lorsque le groupe est utilisé pour la première fois, car il n’ya pas encore de référence en retour, mais si vous mettez cela dans une boucle, la référence en arrière obtient à chaque fois la capture de l’itération précédente. Cela vous permet d’accumuler une capture plus grande à chaque itération. Ceci peut être utilisé pour créer des motifs très compacts pour des choses comme des nombres triangulaires, des carrés et des nombres de Fibonacci.
Par exemple, en utilisant le fait que les carrés ne sont que la somme des premiers n
entiers impairs, nous pouvons faire correspondre une entrée carrée comme celle-ci:
(^.|..\1)+$
À la première itération, ..\1
ne peut pas fonctionner car il \1
n'a pas encore de valeur. Nous commençons donc par ^.
capturer un seul personnage dans un groupe 1
. Lors des itérations suivantes, ^.
ne correspond plus en raison de l'ancre, mais ..\1
est maintenant valide. Il correspond à deux caractères de plus que l'itération précédente et met à jour la capture. De cette façon, nous faisons correspondre des nombres impairs croissants, en obtenant un carré après chaque itération.
Malheureusement, nous ne pouvons pas utiliser cette technique telle quelle. Après l’appariement i*i
, nous devons i
aussi obtenir le résultat pour pouvoir le multiplier par j
. Un moyen simple (mais long) de faire cela consiste à utiliser le fait que l'appariement i*i
prend des i
itérations, de sorte que nous avons capturé des i
éléments en groupe 1
. Nous pourrions maintenant utiliser des groupes d'équilibrage pour extraire cela i
, mais comme je l'ai dit, cela coûte cher.
Au lieu de cela, j'ai imaginé une manière différente d'écrire cette "somme d'entiers impairs consécutifs" qui aboutit également i
à un groupe de capture à la fin. Bien sûr, le i
troisième chiffre est juste 2i-1
. Cela nous donne un moyen d'incrémenter la référence en avant uniquement de 1 à chaque itération. C'est cette partie:
^()(\1(?<1>.\1))+
Cela ()
pousse simplement une capture vide sur le groupe 1
(initialisation i
à 0
). Ceci est à peu près équivalent à la ^.|
solution simple ci-dessus, mais l’utilisation |
dans ce cas serait un peu plus délicate.
Ensuite, nous avons la boucle principale (\1(?<1>.\1))
. \1
correspond au précédent i
, (?<1>.\1)
puis met à jour le groupe 1
avec i+1
. En termes de nouveauté i
, nous venons d'associer des 2i-1
personnages. Exactement ce dont nous avons besoin.
Lorsque nous avons terminé, nous avons identifié un carré i*i
et le groupe 1
contient toujours des i
personnages.
La deuxième partie est plus proche de la simple correspondance carrée que j'ai montrée ci-dessus. Ignorons la référence arrière 1
pour l'instant:
(.(?(4).\1))*
C'est fondamentalement la même chose (^.|..\4)*
, sauf que nous ne pouvons pas utiliser ^
car nous ne sommes pas au début de la chaîne. Au lieu de cela, nous utilisons un conditionnel, pour faire correspondre le complément .\1
uniquement lorsque nous avons déjà utilisé le groupe 4
. Mais en réalité, c'est exactement la même chose. Cela nous donne j*j
.
La seule chose qui manque, c'est le j*i
terme. Nous combinons cela avec le j*j
en utilisant le fait que le j*j
calcul prend encore des j
itérations. Donc, pour chaque itération, nous faisons également avancer le curseur i
avec \1
. Nous devons simplement nous assurer de ne pas écrire cela dans le groupe 4
, car cela gâcherait la correspondance de nombres impairs consécutifs. C'est comme ça qu'on arrive au:
(\1(.(?(4).\1)))*