Explicarei a parte da regex fora do teste de primalidade: a seguinte regex, dada uma String s
que consiste em repetir String t
, encontra t
.
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
O modo como funciona é que a captura de regex (.*)
para \1
, em seguida, vê se há \1+
que se lhe segue. O uso de ^
e $
garante que uma correspondência deve ser de toda a cadeia.
Então, de certa forma, nos é dado String s
, que é um "múltiplo" de String t
, e o regex o encontrará t
(o mais longo possível, pois \1
é ganancioso).
Depois de entender por que esse regex funciona, então (ignorando a primeira alternativa no regex do OP por enquanto) é simples explicar como é usado para o teste de primalidade.
- Para testar a primalidade de
n
, primeiro gere um String
comprimento n
(preenchido com o mesmo char
)
- O regex captura um
String
pouco de comprimento (digamosk
) \1
e tenta corresponder \1+
ao resto doString
- Se houver uma correspondência, então
n
é um múltiplo adequado de k
e, portanto, n
não é primo.
- Se não houver correspondência, não
k
existe uma que se divida n
e, n
portanto , é a principal
Como .?|(..+?)\1+
corresponde aos números primos?
Na verdade, não! ele corresponde String
cujo comprimento não é primo!
.?
: A primeira parte da alternância corresponde String
ao comprimento 0
ou 1
(NÃO é definido por definição)
(..+?)\1+
: A segunda parte da alternância, uma variação do regex explicado acima, corresponde a um String
comprimento n
"múltiplo" de um String
comprimento k >= 2
(isto n
é, um composto, NÃO um primo).
- Observe que o modificador relutante
?
não é realmente necessário para a correção, mas pode ajudar a acelerar o processo tentando k
primeiro menor
Observe o !
boolean
operador complemento na return
declaração: ele nega o matches
. É quando o regex NÃO corresponde, n
é primo! É uma lógica dupla-negativa, então não admira que seja meio confuso !!
Simplificação
Aqui está uma reescrita simples do código para torná-lo mais legível:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
O acima é essencialmente o mesmo que o código Java original, mas dividido em várias instruções com designações para variáveis locais para facilitar a compreensão da lógica.
Também podemos simplificar a regex, usando repetição finita, da seguinte maneira:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
Novamente, dado um String
comprimento n
, preenchido com o mesmo char
,
.{0,1}
verifica se n = 0,1
, NÃO prime
(.{2,})\1+
verifica se n
é um múltiplo adequado de k >= 2
, NÃO é primo
Com a excepção do modificador relutantes ?
em \1
(omitidas para maior clareza), a expressão regular acima é idêntico ao original.
Regex mais divertido
O regex a seguir usa técnica semelhante; deve ser educacional:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
Veja também
!new String(new char[n]).matches(".?|(..+?)\\1+")
é equivalente a!((new String(new char[n])).matches(".?|(..+?)\\1+"))
.