Encontré el siguiente ejemplo de código para Java en RosettaCode :
public static boolean prime(int n) {
return !new String(new char[n]).matches(".?|(..+?)\\1+");
}
- No conozco Java en particular, pero entiendo todos los aspectos de este fragmento, excepto la expresión regular en sí.
- Tengo conocimiento básico a básico-avanzado de Regex tal como lo encuentra en las funciones integradas de PHP
¿Cómo .?|(..+?)\\1+
coincide los números primos?
!new String(new char[n]).matches(".?|(..+?)\\1+")
es equivalente a!((new String(new char[n])).matches(".?|(..+?)\\1+"))
.Respuestas:
Dijiste que entiendes esta parte, pero solo para enfatizar, la cadena generada tiene una longitud igual al número proporcionado. Entonces la cadena tiene tres caracteres si y solo si
n == 3
.La primera parte de la expresión regular dice: "cualquier carácter, cero o una vez". Así que, básicamente, no es cero o uno o character--, por lo que he mencionado anteriormente,
n == 0 || n == 1
. Si tenemos el partido, entonces devuelve la negación de eso. Esto corresponde con el hecho de que cero y uno NO son primos.La segunda parte de la expresión regular es un poco más complicada, ya que depende de grupos y referencias. Un grupo es cualquier cosa entre paréntesis, que luego será capturado y almacenado por el motor de expresiones regulares para su uso posterior. Una referencia inversa es un grupo coincidente que se usa más adelante en la misma expresión regular.
El grupo captura 1 personaje, luego 1 o más de cualquier personaje. (El carácter + significa uno o más, pero SOLO del carácter o grupo anterior. Por lo tanto, no se trata de "dos o cuatro o seis caracteres, etc.", sino más bien "dos o tres, etc." El +? Es como +, pero intenta hacer coincidir la menor cantidad posible de caracteres. + normalmente intenta engullir toda la cadena si puede, lo cual es malo en este caso porque evita que la parte de referencia inversa funcione).
La siguiente parte es la referencia: ese mismo conjunto de caracteres (dos o más), que aparecen nuevamente. Dicha referencia aparece una o más veces.
Entonces. El grupo capturado corresponde a un número natural de caracteres (de 2 en adelante) capturados. Dicho grupo aparece entonces un número natural de veces (también de 2 en adelante). Si hay una coincidencia, esto implica que es posible encontrar un producto de dos números mayor o igual a 2 que coincida con la cadena de longitud n ... lo que significa que tiene una n compuesta. Entonces, nuevamente, devuelva la negación de la coincidencia exitosa: n NO es primo.
Si no se puede encontrar una coincidencia, entonces no puede obtener un producto de dos números naturales mayores o iguales a 2 ... y tiene tanto una no coincidencia como un primo, por lo tanto, nuevamente el retorno de la negación del resultado del partido.
lo ves ahora? Es increíblemente complicado (¡y computacionalmente costoso!) Pero también es algo simple al mismo tiempo, una vez que lo obtienes. :-)
Puedo elaborar si tiene más preguntas, como sobre cómo funciona realmente el análisis de expresiones regulares. Pero estoy tratando de mantener esta respuesta simple por ahora (o lo más simple posible).
fuente
Explicaré la parte de la expresión regular fuera de la prueba de primalidad: la siguiente expresión regular, dada una
String s
que consiste en repetirString t
, encuentrat
.La forma en que funciona es que las capturas de expresiones regulares
(.*)
en\1
, y luego ve si hay\1+
tras ella. El uso de^
y$
asegura que una coincidencia debe ser de toda la cadena.Entonces, en cierto modo, se nos da
String s
, que es un "múltiplo" deString t
, y la expresión regular encontrará talt
(el más largo posible, ya que\1
es codicioso).Una vez que comprenda por qué funciona esta expresión regular, entonces (ignorando la primera alternativa en la expresión regular de OP por ahora) explicando cómo se usa para la prueba de primalidad es simple.
n
, primero genere unString
de longitudn
(rellenado con el mismochar
)String
poco de longitud (digamosk
)\1
e intenta hacer coincidir\1+
con el resto de laString
n
es un múltiplo adecuado dek
y, porn
lo tanto, no es primo.k
existe tal cosa que se dividan
y,n
por lo tanto , sea primordialEn realidad, no lo hace! ¡ Coincide con la
String
longitud que NO es primo!.?
: La primera parte de las coincidenciasString
de alternancia de longitud0
o1
(NO primo por definición)(..+?)\1+
: La segunda parte de la alternancia, una variación de la expresión regular explicada anteriormente, coincide con laString
longitudn
que es "un múltiplo" de unaString
longitudk >= 2
(n
es decir, es un compuesto, NO un primo).?
no es realmente necesario para la corrección, pero puede ayudar a acelerar el proceso al intentark
primero más pequeñoTenga en cuenta el
!
boolean
operador de complemento en lareturn
declaración: niega elmatches
. Es cuando la expresión regular NO coincide, ¡n
es primordial! Es una lógica doblemente negativa, ¡así que no es de extrañar que sea un poco confuso!Simplificación
Aquí hay una reescritura simple del código para hacerlo más legible:
Lo anterior es esencialmente el mismo que el código Java original, pero se divide en varias declaraciones con asignaciones a variables locales para facilitar la comprensión de la lógica.
También podemos simplificar la expresión regular, utilizando la repetición finita, de la siguiente manera:
De nuevo, dado un
String
largon
, lleno de lo mismochar
,.{0,1}
comprueba sin = 0,1
, NO cebar(.{2,})\1+
comprueba sin
es un múltiplo adecuado dek >= 2
, NO cebarCon la excepción del modificador reacios
?
en\1
(omitido para mayor claridad), la expresión regular anterior es idéntica a la original.Más diversión regex
La siguiente expresión regular utiliza una técnica similar; debería ser educativo:
Ver también
fuente
[Populist]
algún día de esto.Buen truco regex (aunque muy ineficiente) ... :)
La expresión regular define los no primos de la siguiente manera:
N no es primo si y solo si N <= 1 O N es divisible por alguna K> 1.
En lugar de pasar la simple representación digital de N al motor de expresiones regulares, se alimenta con una secuencia de longitud N, compuesta de un carácter repetitivo. La primera parte de la disyunción verifica N = 0 o N = 1, y la segunda busca un divisor K> 1, utilizando referencias posteriores. Obliga al motor regex a encontrar alguna subsecuencia no vacía que se puede repetir al menos dos veces para formar la secuencia. Si existe tal subsecuencia, significa que su longitud divide N, por lo tanto, N no es primo.
fuente
Aplicar a los números después de la conversión a la base 1 (1 = 1, 2 = 11, 3 = 111, ...). Los no primos coincidirán con esto. Si no coincide, es primo.
Explicación aquí .
fuente