Encontrei o seguinte exemplo de código para Java no RosettaCode :
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
- Não conheço Java em particular, mas entendo todos os aspectos desse snippet, exceto o próprio regex
- Eu tenho conhecimentos básicos e básicos avançados do Regex, como você o encontra nas funções PHP integradas
Como .?|(..+?)\\1+
corresponde aos números primos?
!new String(new char[n]).matches(".?|(..+?)\\1+")
é equivalente a!((new String(new char[n])).matches(".?|(..+?)\\1+"))
.Respostas:
Você disse que entende esta parte, mas apenas para enfatizar, a String gerada tem um comprimento igual ao número fornecido. Portanto, a cadeia tem três caracteres se e somente se
n == 3
.A primeira parte do regex diz: "qualquer caractere, zero ou uma vez". Então, basicamente, existe zero ou um caractere - ou, pelo que mencionei acima
n == 0 || n == 1
,. Se tivermos a correspondência, retorne a negação disso. Isso corresponde ao fato de que zero e um NÃO são primos.A segunda parte do regex é um pouco mais complicada, contando com grupos e referências anteriores. Um grupo é qualquer coisa entre parênteses, que será capturado e armazenado pelo mecanismo regex para uso posterior. Uma referência anterior é um grupo correspondido que é usado posteriormente no mesmo regex.
O grupo captura 1 caractere e depois 1 ou mais de qualquer caractere. (O caractere + significa um ou mais, mas SOMENTE do caractere ou grupo anterior. Portanto, não são "dois, quatro, seis, etc. caracteres", mas sim "dois ou três, etc." O +? É como +, mas ele tenta corresponder o mínimo de caracteres possível. + normalmente tenta devorar toda a cadeia, se puder, o que é ruim neste caso, porque impede que a parte de referência anterior funcione.)
A próxima parte é a referência anterior: o mesmo conjunto de caracteres (dois ou mais), aparecendo novamente. A referida referência anterior aparece uma ou mais vezes.
Assim. O grupo capturado corresponde a um número natural de caracteres (de 2 em diante) capturados. O referido grupo aparece então um número natural de vezes (também a partir de 2). Se houver uma correspondência, isso implica que é possível encontrar um produto com dois números maiores ou iguais a 2 que correspondam à cadeia de comprimento n ... o que significa que você tem um n composto. Então, novamente, retorne a negação da correspondência bem-sucedida: n NÃO é primo.
Se nenhuma correspondência puder ser encontrada, você não poderá criar um produto com dois números naturais maior ou igual a 2 ... e você terá uma correspondência e uma primo, portanto, novamente o retorno da negação do resultado da partida.
Você vê isto agora? É incrivelmente complicado (e computacionalmente caro!), Mas também é meio simples ao mesmo tempo, quando você o entende. :-)
Posso elaborar se você tiver mais perguntas, como sobre como a análise de expressões regulares realmente funciona. Mas estou tentando manter essa resposta simples por enquanto (ou o mais simples possível).
fonte
Explicarei a parte da regex fora do teste de primalidade: a seguinte regex, dada uma
String s
que consiste em repetirString t
, encontrat
.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" deString 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.
n
, primeiro gere umString
comprimenton
(preenchido com o mesmochar
)String
pouco de comprimento (digamosk
)\1
e tenta corresponder\1+
ao resto doString
n
é um múltiplo adequado dek
e, portanto,n
não é primo.k
existe uma que se dividan
e,n
portanto , é a principalNa verdade, não! ele corresponde
String
cujo comprimento não é primo!.?
: A primeira parte da alternância correspondeString
ao comprimento0
ou1
(NÃO é definido por definição)(..+?)\1+
: A segunda parte da alternância, uma variação do regex explicado acima, corresponde a umString
comprimenton
"múltiplo" de umString
comprimentok >= 2
(iston
é, um composto, NÃO um primo).?
não é realmente necessário para a correção, mas pode ajudar a acelerar o processo tentandok
primeiro menorObserve o
!
boolean
operador complemento nareturn
declaração: ele nega omatches
. É 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:
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:
Novamente, dado um
String
comprimenton
, preenchido com o mesmochar
,.{0,1}
verifica sen = 0,1
, NÃO prime(.{2,})\1+
verifica sen
é um múltiplo adequado dek >= 2
, NÃO é primoCom 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:
Veja também
fonte
[Populist]
dia com isso.Bom truque regex (embora muito ineficiente) ... :)
O regex define não primos da seguinte maneira:
N não é primo se e somente se N <= 1 OR N é divisível por alguns K> 1.
Em vez de passar a representação digital simples de N para o mecanismo de expressão regular, ela é alimentada com uma sequência de comprimento N, composta por um caractere repetido. A primeira parte da disjunção verifica N = 0 ou N = 1, e a segunda procura um divisor K> 1, usando referências anteriores. Força o mecanismo regex a encontrar uma sub-sequência não vazia que pode ser repetida pelo menos duas vezes para formar a sequência. Se existe uma subsequência, significa que seu comprimento divide N, portanto N não é primo.
fonte
Aplique aos números após a conversão na base 1 (1 = 1, 2 = 11, 3 = 111, ...). Não primos corresponderão a isso. Se não corresponder, é primo.
Explicação aqui .
fonte