Faites correspondre les cordes dont la longueur est une quatrième puissance


28

Dans le cadre de cette question, considérons uniquement les chaînes qui se composent du caractère xrépété arbitrairement un nombre de fois.

Par exemple:

<empty>
x
xx
xxxxxxxxxxxxxxxx

(Eh bien, en fait, cela ne doit pas nécessairement l'être x- n'importe quel caractère est correct tant que la chaîne entière n'a qu'un seul type de caractère)

Écrivez une expression régulière dans n'importe quelle saveur d'expression régulière de votre choix pour faire correspondre toutes les chaînes dont la longueur est n 4 pour un entier non négatif n (n> = 0). Par exemple, les chaînes de longueur 0, 1, 16, 81, etc. sont valides; les autres ne sont pas valides.

En raison de la limitation technique, les valeurs de n supérieures à 128 sont difficiles à tester. Cependant, votre regex devrait logiquement fonctionner correctement malgré tout.

Notez que vous n'êtes pas autorisé à exécuter du code arbitraire dans votre regex (pour les utilisateurs Perl). Toute autre syntaxe (look-around, back-reference, etc.) est autorisée.

Veuillez également inclure une brève explication de votre approche du problème.

(Veuillez ne pas coller d'explication de syntaxe de regex générée automatiquement, car elles sont inutiles)


"xx" n'est pas valide, n'est-ce pas?
Kendall Frey

@KendallFrey: Non. Ce n'est pas valable.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh pensez-vous qu'il existe une réponse possible à cela?
xem

1
@ Timwi: Oui. Java, PCRE (probablement Perl aussi, mais ne peut pas tester), .NET. Le mien ne fonctionne pas dans Ruby / JS, cependant.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Cette question a été ajoutée à la FAQ sur l'expression régulière Stack Overflow , sous "Advanced Regex-Fu".
aliteralmind

Réponses:


21

Cette expression (ir) régulière semble fonctionner.

^((?(1)((?(2)\2((?(3)\3((?(4)\4x{24}|x{60}))|x{50}))|x{15}))|x))*$

Cette expression régulière est compatible avec les saveurs PCRE, Perl, .NET.

Cela suit essentiellement un "arbre de différence" (je ne sais pas s'il y a un nom propre), qui indique au regex combien de x supplémentaires doivent correspondre pour la quatrième puissance suivante:

1     16    81    256   625   1296  2401 ...
   15    65    175   369   671   1105 ...
      50    110   194   302   434 ...
         60    84    108   132 ...
            24    24    24 ...  # the differences level out to 24 on the 4th iteration

\2, \3, Les \4magasins et les mises à jour la différence comme indiqué sur la 2e, 3e et 4e rangs, respectivement.

Cette construction peut facilement être étendue pour des puissances supérieures.

Certainement pas une solution élégante, mais cela fonctionne.


+1. Très bonne réponse. Bien que cette réponse soit différente de la mienne (elle utilise l'expression rationnelle conditionnelle, contrairement à la mienne), elle a le même esprit que ma solution (en exploitant l'arbre de différence et en utilisant la référence arrière déclarée vers l'avant de certains moteurs d'expression régulière).
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

bonne idée re arbre de différence. pour les carrés, l'arbre est 1 4 9 16 ... 3 5 7 ... 2 2 2, non?
Sparr

@Sparr merci, et oui
Volatilité

24

Une autre solution

C'est, à mon avis, l'un des problèmes les plus intéressants du site. Je dois remercier deadcode pour l'avoir remonté en haut.

^((^|xx)(^|\3\4\4)(^|\4x{12})(^x|\1))*$

39 octets , sans condition ni assertion ... en quelque sorte. Les alternances, telles qu'elles sont utilisées ( ^|), sont en quelque sorte un conditionnel, pour choisir entre «première itération» et «pas première itération».

Cette expression régulière fonctionne ici: http://regex101.com/r/qA5pK3/1

PCRE et Python interprètent correctement l'expression régulière, et elle a également été testée en Perl jusqu'à n = 128 , y compris n 4 -1 et n 4 +1 .


Définitions

La technique générale est la même que dans les autres solutions déjà publiées: définir une expression d'auto-référencement qui, à chaque itération suivante, correspond à une longueur égale au terme suivant de la fonction de différence directe, D f , avec un quantificateur illimité ( *). Une définition formelle de la fonction de différence directe:

Définition 1: fonction de différence directe

De plus, des fonctions de différence d'ordre supérieur peuvent également être définies:

Définition 2: deuxième fonction de différence directe

Ou, plus généralement:

Définition 3: kième fonction de différence directe

La fonction de différence directe a beaucoup de propriétés intéressantes; c'est aux séquences ce qu'est la dérivée aux fonctions continues. Par exemple, D f d'un polynôme d'ordre n sera toujours un polynôme d'ordre n-1 , et pour tout i , si D f i = D f i + 1 , alors la fonction f est exponentielle, de la même manière que la dérivée de e x est égale à elle-même. La fonction discrète la plus simple pour laquelle f = D f est 2 n .


f (n) = n 2

Avant d'examiner la solution ci-dessus, commençons par quelque chose d'un peu plus facile: une expression régulière qui correspond à des chaînes dont les longueurs sont un carré parfait. Examen de la fonction de différence directe:

DFF: n ^ 2

Cela signifie que la première itération doit correspondre à une chaîne de longueur 1 , la seconde une chaîne de longueur 3 , la troisième une chaîne de longueur 5 , etc., et en général, chaque itération doit correspondre à une chaîne deux plus longue que la précédente. L'expression rationnelle correspondante découle presque directement de cette déclaration:

^(^x|\1xx)*$

On peut voir que la première itération correspondra à une seule x, et chaque itération suivante correspondra à une chaîne deux plus longue que la précédente, exactement comme spécifié. Cela implique également un test carré parfait incroyablement court en perl:

(1x$_)=~/^(^1|11\1)*$/

Cette expression régulière peut être encore généralisée pour correspondre à n'importe quelle longueur n- régionale:

Nombres triangulaires:
^(^x|\1x{1})*$

Numéros carrés:
^(^x|\1x{2})*$

Nombres pentagonaux:
^(^x|\1x{3})*$

Nombres hexagonaux:
^(^x|\1x{4})*$

etc.


f (n) = n 3

Passons au n 3 , en examinant à nouveau la fonction de différence directe:

DFF: n ^ 3

Il n'est peut-être pas immédiatement évident de savoir comment implémenter cela, nous examinons donc également la deuxième fonction de différence:

FDF ^ 2: n ^ 3

Ainsi, la fonction de différence vers l'avant n'augmente pas d'une constante, mais plutôt d'une valeur linéaire. C'est bien que la valeur initiale (' -1 th') de D f 2 soit zéro, ce qui enregistre une initialisation à la deuxième itération. Le regex résultant est le suivant:

^((^|\2x{6})(^x|\1))*$

La première itération correspondra à 1 , comme précédemment, la seconde correspondra à une chaîne 6 plus longue ( 7 ), la troisième correspondra à une chaîne 12 plus longue ( 19 ), etc.


f (n) = n 4

La fonction de différence directe pour n 4 :

DFF: n ^ 4

La deuxième fonction de différence directe:

FDF ^ 2: n ^ 4

La troisième fonction de différence directe:

FDF ^ 3: n ^ 4

Maintenant, c'est moche. Les valeurs initiales pour D f 2 et D f 3 sont toutes les deux non nulles, 2 et 12 respectivement, qui devront être prises en compte. Vous avez probablement compris maintenant que l'expression régulière suivra ce modèle:

^((^|\2\3{b})(^|\3x{a})(^x|\1))*$

Parce que le D f 3 doit correspondre à une longueur de 12 à la deuxième itération, a est nécessairement 12 . Mais comme il augmente de 24 à chaque terme, l'imbrication plus profonde suivante doit utiliser sa valeur précédente deux fois, ce qui implique b = 2 . La dernière chose à faire est d'initialiser le D f 2 . Parce que D f 2 influence directement D f , ce qui est finalement ce que nous voulons faire correspondre, sa valeur peut être initialisée en insérant l'atome approprié directement dans l'expression régulière, dans ce cas (^|xx). Le regex final devient alors:

^((^|xx)(^|\3\4{2})(^|\4x{12})(^x|\1))*$

Ordres supérieurs

Un polynôme du cinquième ordre peut être mis en correspondance avec l'expression régulière suivante:
^((^|\2\3{c})(^|\3\4{b})(^|\4x{a})(^x|\1))*$

f (n) = n 5 est un exercice assez facile, car les valeurs initiales pour les deuxième et quatrième fonctions de différence directe sont nulles:

^((^|\2\3)(^|\3\4{4})(^|\4x{30})(^x|\1))*$

Pour six polynômes d'ordre:
^((^|\2\3{d})(^|\3\4{c})(^|\4\5{b})(^|\5x{a})(^x|\1))*$

Pour les polynômes du septième ordre:
^((^|\2\3{e})(^|\3\4{d})(^|\4\5{c})(^|\5\6{b})(^|\6x{a})(^x|\1))*$

etc.

Notez que tous les polynômes ne peuvent pas être appariés exactement de cette manière, si l'un des coefficients nécessaires n'est pas entier. Par exemple, n 6 requiert que a = 60 , b = 8 et c = 3/2 . Cela peut être contourné, dans ce cas:

^((^|xx)(^|\3\6\7{2})(^|\4\5)(^|\5\6{2})(^|\6\7{6})(^|\7x{60})(^x|\1))*$

Ici, j'ai changé b en 6 et c en 2 , qui ont le même produit que les valeurs indiquées ci-dessus. Il est important que le produit ne change pas, car un · b · c · ... contrôle la fonction de différence constante, qui pour un polynôme du sixième ordre est D f 6 . Il y a deux atomes d'initialisation présents: l'un pour initialiser D f à 2 , comme pour n 4 , et l'autre pour initialiser la cinquième fonction de différence à 360 , tout en ajoutant en même temps les deux manquants de b .


Sur quels moteurs avez-vous testé cela?
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Je comprends enfin ce qui se passe. En effet, la seule chose nécessaire est le support de la référence directe. +1
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

@nhahtdh ahh, vous avez raison. Les références directes ne sont pas nécessairement une fonctionnalité universelle non plus.
primo

1
Excellent! J'adore à quel point c'est court, simple et facile à comprendre. Avec son emboîtement peu profond, il est facile de calculer à la main comment il se comportera. En outre, il est aussi rapide que les solutions de Volatility et de nhahtdh . Et j'aime votre explication détaillée, y compris la démonstration que cela peut même être étendu aux polynômes. Je donnerais des points bonus si je le pouvais.
Deadcode

@Lynn merci! Je ne m'attendais pas à ce que ...
primo

13

Voici une solution qui n'utilise pas de conditionnelles, de références arrières déclarées ou imbriquées, de lookbehind, d'équilibrage de groupes ou de récursivité d'expression régulière. Il utilise uniquement des références et des références arrières standard, qui sont très largement prises en charge. J'ai été inspiré pour opérer dans ces limites en raison de Regex Golf , qui utilise le moteur regex ECMAScript.

Le fonctionnement de cette expression régulière de 50 octets est conceptuellement assez simple et complètement différent de toutes les autres solutions soumises à ce puzzle. Il était surprenant de découvrir que ce type de logique mathématique était exprimable dans une expression régulière.

      \2                     \4  \5
^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+)$)\5){4})*x?$

(Les groupes de capture sont étiquetés au-dessus de l'expression régulière)

L'expression rationnelle peut être généralisée à tout pouvoir simplement en remplaçant le 4dans {4}de la puissance désirée.

Essayez-le en ligne!

Il fonctionne en divisant à plusieurs reprises la plus petite quatrième puissance d'un nombre premier par laquelle la valeur actuelle est divisible. En tant que tel, le quotient à chaque étape est toujours une quatrième puissance, si la valeur d'origine était une quatrième puissance. Un dernier quotient de 1 indique que la valeur d'origine était bien une quatrième puissance; ceci termine le match. Zéro est également apparié.

Tout d'abord, il utilise un groupe de capture paresseux \2pour capturer le plus petit facteur du nombre supérieur à 1. En tant que tel, ce facteur est garanti d'être premier. Par exemple, avec 1296 (6 ^ 4), il capturera initialement \2= 2.

Ensuite, au début d'une boucle répétée 4 fois, il teste pour voir si le nombre courant est divisible par \2, avec (?=\2+$). La première fois dans cette boucle, ce test est inutile, mais son objectif apparaîtra plus tard.

Suivant l' intérieur de cette boucle interne, il utilise le groupe de capture avide \4de capturer le plus grand facteur du plus petit nombre que lui - même: (?=(x+)(\4+)$). En effet , cela divise le nombre par son plus petit facteur premier, \2; par exemple, 1296 sera initialement capturé comme \4= 1296/2 = 648. Notez que la division du nombre actuel par \2est implicite. Bien qu'il soit possible de diviser explicitement le nombre actuel par un nombre contenu dans un groupe de capture (que je n'ai découvert que quatre jours après la publication de cette réponse), cela ferait une expression régulière plus lente et plus difficile à comprendre, et ce n'est pas nécessaire, car le plus petit facteur d'un nombre supérieur à 1 correspondra toujours à son plus grand facteur plus petit que lui-même (de sorte que leur produit est égal au nombre lui-même).

Étant donné que ce type d'expression régulière ne peut que "ronger" la chaîne (la rendant plus petite) en laissant un résultat à la fin de la chaîne, nous devons "déplacer" le résultat de la division à la fin de la chaîne. Cela se fait en capturant le résultat de la soustraction (le nombre actuel moins \4), dans le groupe de capture \5, puis, en dehors de l'anticipation, en faisant correspondre une partie du début du nombre actuel correspondant à \5. Cela laisse la chaîne non traitée restante à la fin correspondant \4à la longueur.

Maintenant, il revient au début de la boucle intérieure, où il devient évident pourquoi il existe un test de divisibilité par le facteur premier. Nous venons de diviser par le plus petit facteur premier du nombre; si le nombre est encore divisible par ce facteur, cela signifie que le nombre d'origine peut être divisible par la quatrième puissance de ce facteur. La première fois que ce test est effectué, il est inutile, mais les 3 fois suivantes, il détermine si le résultat de la division implicite par \2est toujours divisible par \2. S'il est toujours divisible par \2au début de chaque itération de la boucle, cela prouve que chaque itération a divisé le nombre par \2.

Dans notre exemple, avec une entrée de 1296, cela se déroulera comme suit:

\2= 2
\4= 1296/2 = 648
\4= 648/2 = 324
\4= 324/2 = 162
\4= 162/2 = 81

Maintenant, l'expression régulière peut revenir en boucle à la première étape; c'est ce que fait la finale *. Dans cet exemple, 81 deviendra le nouveau numéro; la prochaine boucle se déroulera comme suit:

\2= 3
\4= 81/3 = 27
\4= 27/3 = 9
\4= 9/3 = 3
\4= 3/3 = 1

Il reviendra maintenant à la première étape, avec 1 comme nouveau numéro.

Le nombre 1 ne peut être divisé par aucun nombre premier, ce qui en ferait une non-correspondance (?=(xx+?)\2+$), il quitte donc la boucle de niveau supérieur (celle avec *à la fin). Il atteint maintenant le x?$. Cela ne peut correspondre qu'à zéro ou un. Le nombre actuel à ce stade sera 0 ou 1 si et seulement si le nombre d'origine était une quatrième puissance parfaite; si elle est 0 à ce stade, cela signifie que la boucle de niveau supérieur n'a jamais correspondu à rien, et si elle est 1, cela signifie que la boucle de niveau supérieur a divisé une quatrième puissance parfaite jusqu'à ce qu'elle ne soit plus divisible par rien (ou c'était 1 en premier lieu, ce qui signifie que la boucle de niveau supérieur ne correspondait à rien).

Il est également possible de résoudre cela en 49 octets en effectuant une division explicite répétée (qui est également généralisée pour toutes les puissances - remplacez la puissance souhaitée moins une dans la {3}), mais cette méthode est beaucoup, beaucoup plus lente et une explication de l'algorithme qu'elle utilise dépasse le cadre de cette réponse:

^((x+)((\2(x+))(?=(\4*)\2*$)\4*(?=\5$\6)){3})?x?$

Essayez-le en ligne!


D'après mes tests (jusqu'à la longueur 1024), il semble que ce soit correct. Cependant, l'expression régulière est trop lente - cela prend beaucoup de temps juste pour correspondre à la longueur 16 ^ 4, il est donc très difficile de vérifier pour un grand nombre. Mais comme les performances ne sont pas requises, je voterai quand je comprendrai votre expression régulière.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Vos regex et votre volatilité sont impressionnants. Leur vitesse et leur brièveté m'étonnent, tous deux correspondant à 100000000 en 7,5 secondes sur mon i7-2600k, beaucoup plus rapide que je ne l'aurais imaginé. Ma solution ici est d'un ordre de grandeur totalement différent, car il faut 12 secondes pour correspondre à 50625. Mais l'objectif avec le mien n'était pas la vitesse, mais plutôt, l'accomplissement du travail avec une longueur de code minimale en utilisant un ensemble d'opérations beaucoup plus limité.
Deadcode

Nos réponses sont rapides, car elles font à peine le recul. Le vôtre fait beaucoup de retour en arrière ((((x+)\5+)\4+)\3+)\2+$. Le vôtre est également étonnant à sa manière, car je ne peux même pas penser à la façon de faire correspondre un nombre carré sans référence inverse déclarée.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Soit dit en passant, cette question n'est pas du code-golf, mais un puzzle. Je ne juge pas la solution par la longueur du code.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

Oh. Cela explique pourquoi vous l'avez utilisé (?:). Dois-je donc modifier ma réponse pour faire de la version optimisée la principale?
Deadcode

8

Solution

^(?:(?=(^|(?<=^x)x|xx\1))(?=(^|\1\2))(^x|\3\2{12}xx))*$

Cette expression régulière est compatible avec les saveurs Java, Perl, PCRE et .NET. Cette expression régulière utilise toute une gamme de fonctionnalités: look-ahead, look-behind et forward-declare back-reference. Les types de référence arrière déclarés à l'avance limitent la compatibilité de cette expression régulière à quelques moteurs.

Explication

Cette solution utilise la dérivation suivante.

En étendant complètement la somme, nous pouvons prouver l'égalité suivante:

\ sum \ limits_ {i = 1} ^ n (i + 1) ^ 4 - \ sum \ limits_ {i = 1} ^ ni ^ 4 = (n + 1) ^ 4 - 1
\ sum \ limits_ {i = 1} ^ ni ^ 4 - \ sum \ limits_ {i = 1} ^ n (i-1) ^ 4 = n ^ 4

Combinons la sommation à gauche:

\ sum \ limits_ {i = 1} ^ n (4 (i + 1) ^ 3 - 6 (i + 1) ^ 2 + 4 (i + 1) - 1) = (n + 1) ^ 4 - 1
\ sum \ limits_ {i = 1} ^ n (4i ^ 3 - 6i ^ 2 + 4i - 1) = n ^ 4

Soustrayez les 2 équations (équation du haut moins équation du bas) puis combinez les sommations sur le côté gauche, puis simplifiez-la:

\ sum \ limits_ {i = 1} ^ n (12i ^ 2 + 2) = (n + 1) ^ 4 - n ^ 4 - 1

Nous obtenons la différence entre les quatrièmes puissances consécutives comme somme de puissance:

(n + 1) ^ 4 - n ^ 4 = \ somme \ limites_ {i = 1} ^ n (12i ^ 2 + 2) + 1

Cela signifie que la différence entre les quatrièmes puissances consécutives augmentera de (12n 2 + 2).

Pour faciliter la réflexion, en se référant à l' arbre de différence dans la réponse de Volatility :

  • Le côté droit de l'équation finale est la 2e ligne de l'arbre de différence.
  • L'incrément (12n 2 + 2) est la 3ème ligne de l'arbre de différence.

Assez de mathématiques. Retour à la solution ci-dessus:

  • Le 1er groupe de capture maintient une série de nombres impairs pour calculer i 2 comme le montre l'équation.

    Précisément, la longueur du 1er groupe de capture sera 0 (inutilisé), 1, 3, 5, 7, ... au fur et à mesure de l'itération de la boucle.

    (?<=^x)xdéfinit la valeur initiale de la série de nombres impairs. Le ^est juste là pour permettre à l'anticipation d'être satisfaite dans la première itération.

    xx\1 ajoute 2 et avance au nombre impair suivant.

  • Le 2e groupe de capture conserve la série de nombres carrés pour i 2 .

    Précisément, la longueur du 2ème groupe de capture sera 0, 1, 4, 9, ... au fur et à mesure que la boucle itère.

    ^in (^|\1\2)définit la valeur initiale de la série de nombres carrés. Et \1\2ajoute le nombre impair au nombre carré actuel pour le faire passer au nombre carré suivant.

  • Le troisième groupe de capture (en dehors de toute perspective et consommant réellement du texte) correspond à tout le côté droit de l'équation que nous avons dérivée ci-dessus.

    ^xin (^x|\3\2{12}xx)définit la valeur initiale, qui est + 1le côté droit de l'équation.

    \3\2{12}xxajoute l'augmentation de la différence (12n 2 + 2) en utilisant n 2 du groupe de capture 2, et correspond à la différence en même temps.

Cette disposition est possible car la quantité de texte mise en correspondance dans chaque itération est supérieure ou égale à la quantité de texte nécessaire pour exécuter l'anticipation pour construire n 2 .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.