Explicarei a parte da regex fora do teste de primalidade: a seguinte regex, dada uma String sque 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 Stringcomprimento n(preenchido com o mesmo char)
- O regex captura um
Stringpouco de comprimento (digamosk ) \1e tenta corresponder \1+ao resto doString
- Se houver uma correspondência, então
n é um múltiplo adequado de ke, portanto, nnão é primo.
- Se não houver correspondência, não
kexiste uma que se divida ne, nportanto , é 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 0ou 1(NÃO é definido por definição)
(..+?)\1+: A segunda parte da alternância, uma variação do regex explicado acima, corresponde a um Stringcomprimento n"múltiplo" de um Stringcomprimento 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 kprimeiro menor
Observe o ! booleanoperador complemento na returndeclaraçã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 Stringcomprimento 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+")).