Spiegherò la parte regex al di fuori del test di primalità: la seguente regex, data una String s
che 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 String
lunghezza n
(riempita con la stessachar
)
- Il regex cattura un
String
po '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 n
non è primo.
- Se non c'è corrispondenza, allora non
k
esiste 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 String
di lunghezza 0
o 1
(NON primo per definizione)
(..+?)\1+
: La seconda parte dell'alternanza, una variazione della regex spiegata sopra, corrispondenze String
di lunghezza n
che è "un multiplo" di una String
lunghezza 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 k
prima a ridurre le dimensioni
Nota l' !
boolean
operatore complemento return
nell'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 String
lunghezza 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+"))
.