Spiegherò la parte regex al di fuori del test di primalità: la seguente regex, data una String sche consiste nel ripetere String t, trova t.
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
Il modo in cui funziona è che la cattura regex (.*)in \1, e poi vede se c'è \1+lo seguono. Utilizzando il ^e$ garantisce che una corrispondenza debba essere dell'intera stringa.
Quindi, in un certo senso, ci viene dato String s, che è un "multiplo" di String t, e il regex lo troverà t(il più lungo possibile, poiché \1è avido).
Una volta compreso il motivo per cui questo regex funziona, allora (ignorare il primo alternato nel regex di OP per ora) spiegare come viene utilizzato per il test di primalità è semplice.
- Per testare la primalità di
n, prima genera una Stringlunghezza n(riempita con la stessachar )
- Il regex cattura un
Stringpo 'di lunghezza (diciamo k) in \1, e cerca di abbinarsi \1+al resto delString
- Se c'è una corrispondenza, allora
nè un multiplo corretto di k, e quindi nnon è primo.
- Se non c'è corrispondenza, allora non
kesiste tale che divide n, ed nè quindi un numero primo
In che modo .?|(..+?)\1+corrispondono i numeri primi?
In realtà no! Si abbina String la cui lunghezza non è primo!
.?: La prima parte delle alternanze corrisponde Stringdi lunghezza 0o 1(NON primo per definizione)
(..+?)\1+: La seconda parte dell'alternanza, una variazione della regex spiegata sopra, corrispondenze Stringdi lunghezza nche è "un multiplo" di una Stringlunghezza k >= 2(cioèn è un composto, NON un numero primo).
- Si noti che il modificatore riluttante
?non è in realtà necessario per la correttezza, ma può aiutare ad accelerare il processo provando kprima a ridurre le dimensioni
Nota l' ! booleanoperatore complemento returnnell'istruzione: annulla il matches. È quando il regex NON corrisponde, nè primo! È una logica a doppio negativo, quindi non c'è da meravigliarsi che sia un po 'confuso !!
Semplificazione
Ecco una semplice riscrittura del codice per renderlo più leggibile:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
Quanto sopra è essenzialmente uguale al codice Java originale, ma suddiviso in più istruzioni con assegnazioni a variabili locali per facilitare la comprensione della logica.
Possiamo anche semplificare la regex, usando la ripetizione finita, come segue:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Ancora una volta, data una Stringlunghezza n, riempito con lo stesso char,
.{0,1}controlla se n = 0,1, NON primo
(.{2,})\1+controlla se nè un multiplo corretto di k >= 2, NON primo
Con l'eccezione del modificatore riluttante ?su \1(omesso per chiarezza), la regex sopra è identica all'originale.
Più regex divertente
Il regex seguente usa una tecnica simile; dovrebbe essere educativo:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Guarda anche
!new String(new char[n]).matches(".?|(..+?)\\1+")è equivalente a!((new String(new char[n])).matches(".?|(..+?)\\1+")).