J'expliquerai la partie regex en dehors des tests de primalité: la regex suivante, étant donné une String squi 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 une \1+suite. L'utilisation de ^et $garantit qu'une correspondance doit concerner 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 rationnelle le trouvera t(le plus long possible, car il \1est gourmand).
Une fois que vous avez compris pourquoi cette expression régulière fonctionne, alors (en ignorant la première alternative dans l'expression régulière d'OP pour le moment) expliquer comment elle est utilisée pour les tests de primalité est simple.
- Pour tester la primalité de
n, commencez par générer un Stringde longueur n(rempli avec le même char)
- Le regex capture un
Stringd'une longueur (disons k) dans \1, et essaie de correspondre \1+au resteString
- S'il y a correspondance, alors
nest un multiple propre de k, et nn'est donc pas premier.
- S'il n'y a pas de correspondance, alors il n'y en a pas
kqui divise n, et nest donc un
Comment faire .?|(..+?)\1+correspondre les nombres premiers?
En fait, ce n'est pas le cas! Il correspond String dont la longueur n'est PAS prime!
.?: La première partie de l'alternance correspond à la Stringlongueur 0ou 1(PAS prime par définition)
(..+?)\1+: La deuxième partie de l'alternance, une variation de l'expression rationnelle expliquée ci-dessus, correspond à une Stringlongueur nqui est "un multiple" de a Stringde longueur k >= 2(c'est n-à- dire est un composite, PAS un premier).
- Notez que le modificateur réticent
?n'est en fait pas nécessaire pour l'exactitude, mais il peut aider à accélérer le processus en essayant d' kabord plus petit
Notez l' ! booleanopérateur complément dans l' returninstruction: il annule le matches. C'est quand le regex NE correspond PAS , nc'est primordial! C'est une logique double négative, donc pas étonnant que ce soit un peu déroutant !!
Simplification
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 d'origine, mais divisé en plusieurs instructions avec des affectations à des variables locales pour rendre la logique plus facile à comprendre.
Nous pouvons également simplifier l'expression rationnelle, en utilisant la répétition finie, comme suit:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Encore une fois, étant donné une Stringlongueurn , remplie de la même chose char,
.{0,1}vérifie si n = 0,1, PAS amorcer
(.{2,})\1+vérifie si nest un multiple correct dek >= 2 , PAS prime
À l'exception du modificateur réticent ?sur\1 (omis pour plus de clarté), l'expression rationnelle ci - dessus est identique à l'original.
Plus de regex amusant
L'expression régulière suivante utilise une technique similaire; il doit être éducatif:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Voir également
!new String(new char[n]).matches(".?|(..+?)\\1+")équivaut à!((new String(new char[n])).matches(".?|(..+?)\\1+")).