Siguiendo la excelente tradición de preguntas como Encontrar el primo más grande cuya longitud, suma y producto es primo , esta es una variante del mayor desafío primo.
Entrada
Su código no debe tomar ninguna entrada.
Definición
Decimos que un primo p
es good
si p-1
tiene 2
factores primos exactamente distintos.
Salida
Su código debe generar la diferencia absoluta entre primos buenos consecutivos q
y, p
por lo tanto, |q-p|
es lo más grande posible y q
es el primo bueno más pequeño más grande que p
. Puede generar cualquier número de pares buenos y su último resultado se tomará como puntaje.
Ejemplo
La secuencia de los primeros 55 primos buenos es https://oeis.org/A067466 .
Puntuación
Su puntaje es simplemente |q-p|
por el par de buenos números primos que genera.
Idiomas y bibliotecas
Puede usar cualquier idioma o biblioteca que desee (que no fue diseñada para este desafío), excepto las funciones de biblioteca para pruebas de primalidad o factorización de enteros. Sin embargo, para fines de puntuación, ejecutaré su código en mi máquina, así que proporcione instrucciones claras sobre cómo ejecutarlo en Ubuntu.
Mi máquina Los tiempos se ejecutarán en mi máquina. Esta es una instalación estándar de Ubuntu en un procesador AMD FX-8350 de ocho núcleos de 8GB. Esto también significa que necesito poder ejecutar su código.
Detalles
- Mataré tu código después de 2 minutos a menos que comience a quedarse sin memoria antes de eso. Por lo tanto, debe asegurarse de generar algo antes del corte.
- No puede usar ninguna fuente externa de primos.
- Puede usar métodos de prueba primarios probabilísticos, aunque Mego me dice que con buenas tablas, Miller-Rabin puede probar hasta 341,550,071,728,321 (o incluso más) de manera determinista. Ver también http://miller-rabin.appspot.com/ .
Las mejores entradas que verifican todos los enteros de 1
- 756 por cat en Go
- 756 por El'endia Starman en Python
- 1932 por Adnan en C # (usando mono 3.2.8)
- 2640 por yeti en Python (usando pypy 4.01)
- 2754 por Reto Koradi en C ++
- 3486 de Peter Taylor en Java
- 3900 por primo en RPython (usando pypy 4.01)
- 4176 por The Coder en Java
Las mejores entradas que pueden omitir una gran cantidad de enteros para encontrar una gran brecha
- 14226 por Reto Koradi en C ++
- 22596 por primo en RPython (usando pypy 4.01). ¡Registro alcanzado después de 5 segundos!
fuente
Respuestas:
RPython (PyPy 4.0.1), 4032
RPython es un subconjunto restringido de Python, que puede traducirse a C y luego compilarse utilizando RPython Toolchain. Su propósito expreso es ayudar en la creación de intérpretes de lenguaje, pero también se puede usar para compilar programas simples.
Para compilar, descargue la fuente actual de PyPy (PyPy 4.0.1) y ejecute lo siguiente:
El ejecutable resultante será nombrado
good-primes-c
o similar en el directorio de trabajo actual.Notas de implementacion
El generador de números primos
primes
es un Tamiz ilimitado de Eratóstenes, que usa una rueda para evitar cualquier múltiplo de 2 , 3 , 5 o 7 . También se llama a sí mismo de forma recursiva para generar el siguiente valor a utilizar para marcar. Estoy bastante satisfecho con este generador. El perfil de línea revela que las dos líneas más lentas son:así que no creo que haya mucho margen de mejora, aparte de quizás usar una rueda más grande.
Para la verificación de "bondad", primero se eliminan todos los factores de dos de n-1 , utilizando un truco de bit-twiddling para encontrar la mayor potencia de dos, que es un divisor
(n-1 & 1-n)
. Debido a que p-1 es necesariamente par para cualquier primo p> 2 , se deduce que 2 debe ser uno de los factores primos distintos. Lo que queda se envía a lais_prime_power
función, que hace lo que su nombre implica. Verificar si un valor es una potencia primaria es "casi libre", ya que se realiza simultáneamente con la comprobación de primalidad, con a lo sumo operaciones O (log p n) , donde p es el factor primo más pequeño de n. La división de prueba puede parecer un poco ingenua, pero según mis pruebas, es el método más rápido para valores inferiores a 2 32 . Ahorro un poco reutilizando la rueda del tamiz. En particular:iterando sobre una rueda de longitud 48, el
p*p < n
cheque se saltará miles de veces, al precio bajo y bajo de no más de 48 operaciones de módulo adicionales. También omite más del 77% de todos los candidatos, en lugar del 50% al tomar solo probabilidades.Las últimas salidas son:
El código también es válido en Python, y debería alcanzar los 3588 ~ 3900 cuando se ejecuta con un intérprete reciente de PyPy.
RPython (PyPy 4.0.1), 22596
Este envío es ligeramente diferente de los otros publicados hasta ahora, ya que no verifica todos los números primos buenos, sino que realiza saltos relativamente grandes. Una desventaja de hacer esto es que los tamices no se pueden usar [¿Estoy corregido?] , Por lo que uno tiene que confiar completamente en las pruebas de primalidad, que en la práctica son bastante más lentas. También se puede encontrar un medio feliz entre la tasa de crecimiento y la cantidad de valores verificados cada vez. Los valores más pequeños son mucho más rápidos de verificar, pero los valores más grandes tienen más probabilidades de tener brechas más grandes.
Para apaciguar a los dioses matemáticos, he decidido seguir una secuencia similar a Fibonacci, teniendo el siguiente punto de partida como la suma de los dos anteriores. Si no se encuentran nuevos registros después de verificar 10 pares, el script se mueve en el siguiente.
Las últimas salidas son:
Cuando se compila, se usan enteros de 64 bits, aunque se supone que en algunos lugares se pueden agregar dos enteros sin desbordamiento, por lo que en la práctica solo 63 son utilizables. Al alcanzar 62 bits significativos, el valor actual se reduce a la mitad dos veces, para evitar el desbordamiento en el cálculo. El resultado es que el script baraja los valores en el rango 2 60 - 2 62 . No superar la precisión del entero nativo también hace que el script sea más rápido cuando se interpreta.
El siguiente script PARI / GP se puede utilizar para confirmar este resultado:
fuente
Probablemente 4032, tamiz mixto Atkin-Bernstein y Miller-Rabin "determinista"
Factorización de ruedas y buenos primos
Es muy obvio que con la excepción de 2, 3 y 5, cada primo es coprimo a 2 * 3 * 5 = 60. Hay 16 clases de equivalencia módulo 60 que son coprimo a 60, por lo que cualquier prueba de primalidad solo necesita verificar esos 16 casos.
Sin embargo, cuando buscamos primos "buenos" podemos reducir aún más el rebaño. Si
gcd(x, 60) = 1
, encontramos que en la mayoría de los casosgcd(x-1, 60)
es 6 o 10. Hay 6 valoresx
para los cuales es 2:Por lo tanto podemos calcular previamente los "buenos" primos de la forma
2^a 3^b + 1
y el2^a 5^b + 1
y los fundirse en el resultado de un generador de primera, que sólo tiene en cuenta el 10% de los números impares posibles primos.Notas de implementación
Como ya tenía una implementación de Java del tamiz Atkin-Bernstein, y que ya usa una rueda como componente clave, parecía natural eliminar los radios innecesarios y adaptar el código. Originalmente intenté usar una arquitectura productor-consumidor para explotar los 8 núcleos, pero la administración de memoria era demasiado desordenada.
Para probar si un primo es un "buen" primo, estoy usando una prueba "determinista" de Miller-Rabin (lo que realmente significa una prueba de Miller-Rabin que otra persona ha verificado previamente en una lista generada de forma determinista). Ciertamente, esto puede reescribirse para usar también Atkin-Bernstein, con algo de almacenamiento en caché para cubrir los rangos correspondientes a sqrt, cbrt, etc., pero no estoy seguro de si sería una mejora (porque estaría probando muchos números que No necesito probar), así que ese es un experimento para otro día.
En mi computadora bastante vieja esto corre a
en casi exactamente 2 minutos, y luego
a las 3:10, 3:20 y 3:30 respectivamente.
Guardar como
PPCG65876.java
, compilar comojavac PPCG65876.java
y ejecutar comojava -Xmx1G PPCG65876
.fuente
isGood
control.C ++, 2754 (todos los valores, prueba de primalidad de fuerza bruta)
Esta es la fuerza bruta, pero es un comienzo antes de que nuestros matemáticos residentes puedan trabajar con algo más eficiente.
Puedo agregar más explicaciones si es necesario, pero probablemente sea muy obvio por el código. Como si
p
es un primo distinto de 2, sabemos quep - 1
es par, y uno de los dos factores es siempre 2. Así que enumeramos los primos, reducimosp - 1
en todos los factores 2 y verificamos que el valor restante sea primo o que Todos sus factores son el mismo primo.Código:
El programa imprime la diferencia así como los dos primos válidos correspondientes cada vez que se encuentra una nueva diferencia máxima. Ejemplo de salida de la prueba ejecutada en mi máquina, donde el valor reportado de 2754 se encuentra después de aproximadamente 1:20 minutos:
fuente
C ++, 14226 (solo valores altos, prueba de Miller-Rabin)
Publicar esto por separado porque es completamente diferente de mi solución inicial, y no quería reemplazar por completo una publicación que había recibido una cantidad de votos a favor.
Gracias a @primo por señalar un problema con la versión original. Hubo un desbordamiento de grandes números en la prueba de números primos.
Esto aprovecha algunas ideas que se han obtenido durante la evolución de otras soluciones. Las principales observaciones son:
En base a esto, el método empleado aquí es bastante simple:
Resultados:
Código:
fuente
PyPy-2.4.0
Python-2
El
x
archivos...Episodio: "¡Mira mamá! ¡Ni una sola división!"
;-)
Lo probé en Debian8 con PyPy-2.4.0 y Python2 comenzó como:
Si realmente hay mucha RAM, la
del L[n]
línea puede ser eliminada.El generador de números primos básico es este:
Básicamente hace exactamente lo que hace el tamiz de Eratóstenes pero en un orden diferente.
L
es un diccionario pero puede verse como una lista (cinta) de listas de números. Las celdas inexistentesL[n]
se interpretan comon
no tiene divisores primos conocidos hasta ahora.El
while
bucle toma una decisión principal o no principal en cada turnoL[n]
.Si
L[n]
existe (igual quen in L
),P = L[n]
es una lista de distintos divisores primos den
. Entoncesn
no es un primo.Si
L[n]
no existe, no se encontró ningún divisor principal. Entoncesn
debe ser primo entonces conP = [n]
ser el divisor conocido.Ahora
P
es la lista de divisores primos conocidos para ambos casos.El
for p in P
bucle mueve cada entrada deP
avance por la distancia de su valor en la cinta de números.Así es como los divisores saltan sobre la cinta y esta es la razón por la cual estos números de salto tienen que ser primos. Los nuevos números solo
else
aparecen en la cinta por la decisión anterior y esos son números sin divisores conocidos aparte de ellos mismos. Los no primos nunca entran en estas listasL[n]
.Los números primos que saltan en la lista son distintos porque cada número
n
se mira solo una vez y solo se agrega como divisor (si no es primo :)0
o (si es primo :)1
veces. Los divisores primos conocidos solo avanzarán pero nunca se duplicarán. Asi queL[n]
lo siempre tendrá divisores primos distintos o estará vacío.De vuelta al programa superior para las brechas de primos buenos:
... mantiene los divisores primos de
n
enB
cuandon
se sabe que no es primordial.Si
n
se reconoce que es primo,B
contiene la lista de divisores primos del pase de bucle anterior mirandon-1
:... entonces
len(B) == 2
significan - 1
tiene dos divisores primos distintos.g
solo recuerda el último buen primo visto antes del nuevo,M
es la longitud del espacio máximo bueno primo anterior ym
la longitud del espacio recién encontrado.Final feliz.
fuente
C #, probablemente 1932
Descubrí que, cuanto más rápido sea tu algoritmo para encontrar números primos, mejor será tu puntaje. También estoy bastante seguro de que mi algoritmo no es el método más óptimo para la búsqueda principal.
fuente
Pitón 3, 546
... en dos minutos en mi máquina, que creo que es significativamente menos potente que la tuya.
Probablemente podría hacer esto más eficiente optimizando para el
x=2
caso, pero eh. Suficientemente bueno. :PAGSfuente
p: 2, q: 3, |q-p|: 1
para mí.Ve, probablemente 756
¡Para vergüenza! Soy tan novato que solo reutilicé ingenuamente un código antiguo y esperé que funcionara y fuera rápido. Si volviera a implementar esto y realmente lo construyera en torno a buenos números primos, sería mucho más rápido, pero, por desgracia, estoy aprendiendo. (Probablemente responderé nuevamente mañana con una solución totalmente reconstruida que está especialmente diseñada).
Utiliza concurrencia, obviamente.
fuente
Java, 4224 (99,29 s)
Tamiz muy personalizado de Eratóstenes con la ventaja de BitSet
El tiempo necesario depende del límite máximo de los números primos que se calcularán.
por
fuente
Pitón 3, 1464
Con la ayuda de Lembik , cuya idea era simplemente verificar los dos primeros números primos buenos después de un poder de dos y, cuando se encuentre, pasar inmediatamente al siguiente poder de dos. Si alguien puede usar esto como un punto de salto, siéntase libre. Una parte de mis resultados están a continuación después de ejecutar esto en IDLE. El código sigue.
Gracias a primo cuando agarré su lista de pequeños números primos para este código.
Editar: he editado el código para que se ajuste a las especificaciones reales del problema (dos divisores primos distintos, no exactamente dos divisores primos distintos), e implementé no pasar a la siguiente potencia de dos hasta que los primos buenos que el programa ha encontrado tienen un brecha mayor que la de los dos últimos números primos que encontró. También debería darle crédito a Peter Taylor , ya que usé su idea de que los buenos números primos solo podrían ser unos pocos valores mod 60.
Una vez más, he ejecutado esto en una computadora lenta en IDLE, por lo que los resultados pueden ser más rápidos en algo como PyPy, pero no he podido verificarlo.
Una muestra de mis resultados (p, q, qp, tiempo):
Mi código:
fuente
j
en4
lugar de hacerlo2
? Y parece rechazar sin condiciones sij-1
no es una principales veces una potencia de dos, donde usted debe comprobar que es que es de un primo de energía veces una potencia de dos.Ir: todos los enteros: 5112
good.go
:Salida:
A modo de comparación: peterSO max 5112 en 41.04s versus The Coder max 4176 en 51.97s.
Codificador: max | qp | 4176 q 1964330609 p 1964326433
Salida:
fuente