J'ai trouvé l'exemple de code suivant pour Java on RosettaCode :
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
Comment .?|(..+?)\\1+
correspond-il aux nombres premiers?
Vous avez dit que vous comprenez cette partie, mais pour souligner, la chaîne générée a une longueur égale au nombre fourni. La chaîne a donc trois caractères si et seulement si n == 3
.
.?
La première partie de l'expression rationnelle dit "n'importe quel caractère, zéro ou une fois". Donc, fondamentalement, y a-t-il zéro ou un caractère-- ou, d'après ce que j'ai mentionné ci-dessus, n == 0 || n == 1
. Si nous avons la correspondance, renvoyez la négation de cela. Cela correspond au fait que zéro et un ne sont PAS premiers.
(..+?)\\1+
La deuxième partie de l'expression régulière est un peu plus délicate, en s'appuyant sur les groupes et les références. Un groupe est n'importe quoi entre parenthèses, qui sera ensuite capturé et stocké par le moteur d'expression régulière pour une utilisation ultérieure. Une référence arrière est un groupe apparié qui est utilisé plus tard dans la même expression régulière.
Le groupe capture 1 personnage, puis 1 ou plus de n'importe quel personnage. (Le caractère + signifie un ou plusieurs, mais UNIQUEMENT du caractère ou groupe précédent. Il ne s'agit donc pas de "deux ou quatre ou six etc. caractères", mais plutôt de "deux ou trois etc.". Le +? Est comme +, mais il essaie de faire correspondre le moins de caractères possible. + essaie normalement de gober la chaîne entière s'il le peut, ce qui est mauvais dans ce cas car cela empêche la partie de référence arrière de fonctionner.)
La partie suivante est la référence arrière: ce même ensemble de caractères (deux ou plus), réapparaissant. Cette rétro-référence apparaît une ou plusieurs fois.
Alors. Le groupe capturé correspond à un nombre naturel de caractères (à partir de 2) capturés. Ledit groupe apparaît alors un certain nombre de fois naturel (également à partir de 2). S'il y a IS une correspondance, cela implique qu'il est possible de trouver un produit de deux nombres supérieur ou égal à 2 qui correspondent à la chaîne de longueur n ... ce qui signifie que vous avez un n composite. Encore une fois, retournez la négation de la correspondance réussie: n n'est PAS premier.
Si aucune correspondance ne peut être trouvée, alors vous ne pouvez pas trouver un produit de deux nombres naturels supérieur ou égal à 2 ... et vous avez à la fois une non-correspondance et un nombre premier, d'où le retour de la négation du résultat du match.
Le voyez-vous maintenant? C'est incroyablement délicat (et coûteux en calcul!) Mais c'est aussi assez simple en même temps, une fois que vous l'avez obtenu. :-)
Je peux élaborer si vous avez d'autres questions, comme sur la façon dont fonctionne réellement l'analyse syntaxique des expressions rationnelles. Mais j'essaie de garder cette réponse simple pour l'instant (ou aussi simple que possible).
Je vais expliquer la partie regex en dehors du test de primalité: la regex suivante, étant donné un String s
Qui consiste à répéter String t
, Trouve t
.
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
La façon dont cela fonctionne est que l'expression régulière capture (.*)
Dans \1
, Puis voit s'il y a \1+
À la suite. L'utilisation de ^
Et $
Garantit qu'une correspondance doit être de la chaîne entière.
Donc, d'une certaine manière, on nous donne String s
, Qui est un "multiple" de String t
, Et l'expression régulière trouvera un tel t
(le plus long possible, puisque \1
Est gourmand).
Une fois que vous comprenez pourquoi cette expression régulière fonctionne, alors (en ignorant la première alternative dans l'expression régulière d'OP pour l'instant), expliquer comment il est utilisé pour le test de primalité est simple.
n
, commencez par générer un String
de longueur n
(rempli avec le même char
)String
d'une certaine longueur (disons k
) dans \1
, Et essaie de faire correspondre \1+
Au reste du String
n
est un multiple propre de k
, et donc n
n'est pas un nombre premier.k
n'existe pas qui divise n
, et n
est donc un nombre premierComment
.?|(..+?)\1+
correspond-il aux nombres premiers?
En fait, ce n'est pas le cas! Il correspondString
dont la longueur n'est PAS première!
.?
: La première partie de l'alternance correspond à String
de longueur 0
Ou 1
(PAS d'amorce par définition)(..+?)\1+
: La deuxième partie de l'alternance, une variation de l'expression rationnelle expliquée ci-dessus, correspond à String
de longueur n
qui est "un multiple" d'un String
de longueur k >= 2
(c'est-à-dire que n
est un composite, PAS un nombre premier). ?
N'est en fait pas nécessaire pour l'exactitude, mais il peut aider à accélérer le processus en essayant d'abord un plus petit k
Notez l'opérateur de complément !
boolean
dans l'instruction return
: il annule matches
. C'est lorsque l'expression régulière NE correspond PAS , n
est premier! C'est une logique à double négatif, donc pas étonnant que ce soit un peu déroutant !!
Voici une simple réécriture du code pour le rendre plus lisible:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
Ce qui précède est essentiellement le même que le code Java Java original, mais divisé en plusieurs instructions avec des affectations à des variables locales pour rendre la logique plus facile à comprendre.
Nous pouvons également simplifier l'expression régulière, en utilisant la répétition finie, comme suit:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Encore une fois, étant donné un String
de longueur n
, rempli du même char
,
.{0,1}
Vérifie si n = 0,1
, NE PAS amorcer(.{2,})\1+
Vérifie si n
est un multiple propre de k >= 2
, PAS premierÀ l'exception du modificateur réticent ?
Sur \1
(Omis pour plus de clarté), l'expression rationnelle ci-dessus est identique à l'original.
Le regex suivant utilise une technique similaire; il doit être éducatif:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Belle astuce regex (bien que très inefficace) ... :)
La regex définit les non-nombres premiers comme suit:
N n'est pas premier si et seulement si N <= 1 OR N est divisible par un K> 1.
Au lieu de transmettre la simple représentation numérique de N au moteur d'expression rationnelle, il est alimenté avec une séquence de longueur N, composée d'un caractère répétitif. La première partie de la disjonction vérifie N = 0 ou N = 1, et la seconde recherche un diviseur K> 1, en utilisant des références arrières. Il force le moteur d'expression régulière à trouver une sous-séquence non vide qui peut être répétée au moins deux fois afin de former la séquence. Si une telle sous-séquence existe, cela signifie que sa longueur divise N, donc N n'est pas premier.
/^1?$|^(11+?)\1+$/
Appliquer aux nombres après conversion en base 1 (1 = 1, 2 = 11, 3 = 111, ...). Les non-premiers correspondront à cela. S'il ne correspond pas, il est premier.
Explication ici .