¿Puedes usar Sieve of Eratosthenes para números mayores a 1 billón?

Para tamices monolíticos, ciertamente podemos. Sin embargo, hemos pasado el punto en el que los tamices segmentados son lo correcto. He comparado varios SoE y tengo resultados para 10 ^ 11 para siete tamices monolíticos. Varían en el tiempo de 1,5 minutos a 7 minutos. Los más simples (por ejemplo, aproximadamente 20 líneas con el clásico tamiz real de 4 líneas) usan 1 bit por número impar, es decir, aproximadamente 6.25GB. Algunos de ellos usan el popular método mod-30 que usa 8 bits por 30 valores, lo que significa aproximadamente 3.3GB. Esos son un poco más complicados, pero no tan horriblemente. Mod-6 (omitiendo múltiplos de 2 y 3 en lugar de solo 2 o 2,3,5) también es posible y hay un buen tamiz de Python que lo usa.

Por lo general, queremos usar un tamiz segmentado. Esto no solo funciona sustancialmente más rápido, sino que usa una cantidad de memoria enormemente menor. Una buena reseña está en el sitio de Kim Walisch: tamiz segmentado de Eratóstenes

Generar números primos a 10 ^ 11 requiere menos de 1 MB (suponiendo que hagamos algo con cada uno y luego pasar al siguiente) y tan poco como 20 segundos (más típicamente 30-60). Con los mejores primos de software, se pueden hacer 10 ^ 13 en menos de una hora, y algunos otros lo hacen en menos de 2 horas. El tamiz segmentado de Atkin realmente se está desacelerando en este punto y lleva más de 3 horas.

Una vez en los tamaños bastante grandes de 10 ^ 17 y más, las cosas comienzan a ser un poco más interesantes. Hay algunas soluciones que he visto.

  • Continúe usando el SoE segmentado estándar. Poca memoria e incluso podríamos reducirla aún más agregando otra capa (entonces [math] O (n ^ {. 25}) [/ math] en lugar de [math] O (n ^ {. 5}) [/ math]) , aunque nunca he visto a nadie meterse en problemas. Comienza a volverse bastante lento, especialmente si nuestros segmentos son cortos.
  • Usa el tamiz amigable de caché de Tomás Oliveira e Silva. Muy rápido una vez dentro de este rango, y muy popular para proyectos computacionales (por ejemplo, búsqueda de Goldbach, búsqueda de primer espacio, tamiz “grande” de primesieve). Una gran desventaja es que la memoria auxiliar crece rápidamente, por lo que estamos de vuelta en el rango de 3+ GB, aunque no estamos escaneando todo eso en cada iteración.
  • Tamiz híbrido / primalidad. Sabemos que el tamiz es excelente como método de división de prueba en masa. Sabemos que BPSW o Miller-Rabin de 7 bases es completamente determinista para todas las entradas de 64 bits y puede ejecutarse bastante rápido. Por lo tanto, tamizamos hasta un valor razonablemente grande y luego dejamos que la prueba de primalidad verifique todo lo que queda (que son primos o tienen factores muy grandes). Si esto es más rápido depende tanto del tamaño de entrada (por ejemplo, el número de bits que impacta la profundidad necesaria), el rango (cuántos números está sobrepasando nuestro tamizado) y las velocidades relativas del tamiz y la prueba de primalidad.

Un último comentario, que a veces nuestro problema no requiere tamizado. Por ejemplo, se puede contar primos hasta n sin generar la mayoría de ellos. También es estúpidamente más rápido. Puede sumar números primos de manera similar, haciendo posibles cosas como el recuento exacto de números primos hasta 10 ^ 26 o encontrando la suma exacta de todos los números primos menores que 10 ^ 25. Esto simplemente no es práctico mediante tamizado.

Me sorprende que se esté quedando sin memoria con un problema tan pequeño. En las computadoras modernas no debería ser un problema almacenar mil millones de enteros, si puede representarlos (presumiblemente usando entradas largas y largas, puede representar números de hasta 2 ^ 63–1). Y el Tamiz de Erastosthenes no requiere que almacene todos los enteros, solo el más grande probado hasta ahora, y el factor más grande probado hasta ahora. Los números primos que ha encontrado se pueden escribir en el disco, no se utilizan en el algoritmo después de ser encontrados. ¿Quizás no estás usando entradas largas o largas, o el lenguaje que estás usando no las admite?

Si realmente quiere volverse loco con el algoritmo, ¿por qué no prueba GMP, el Proyecto Gnu MultiPrecision? Esta extensión de C ++ permite que números enteros de tamaño arbitrario y dobles de precisión arbitraria se representen en su máquina. https://gmplib.org

Solo necesita un bit por número. O, si tiene ganas de optimizar, un bit por cada número impar . Después de esta optimización, necesitará aproximadamente 6.25 GB de memoria para almacenar todo el tamiz. Esto aún debería ser factible (posiblemente con algunos intercambios) en una computadora razonablemente moderna.

Por supuesto, hay mejores soluciones. Puede mejorar el consumo de memoria de lineal a [matemática] O (\ sqrt {n}) [/ matemática] utilizando un tamiz segmentado de Eratóstenes. (Para encontrar todos los números primos hasta [matemáticas] n [/ matemáticas], primero debe encontrar todos los números primos hasta [matemáticas] \ sqrt {n} [/ matemáticas], y luego puede hacer el resto del tamiz en bloques de tamaño [math] \ sqrt {n} [/ math].)

(Finalmente, un detalle de implementación: tenga en cuenta que 10 ^ 11 está fuera del rango de los enteros de 32 bits).

Observe un hecho interesante: cuando ha alcanzado [matemáticas] 7 [/ matemáticas], el tamiz ya ha identificado todos los números primos hasta [matemáticas] 120 [/ matemáticas].

En general, para encontrar números primos hasta [matemática] N [/ matemática], solo necesita tamizar utilizando primos hasta [matemática] \ sqrt N [/ matemática]. Entonces, por ejemplo, para encontrar números primos de hasta un billón, solo necesita almacenar números primos de hasta un millón.

Genere primos de hasta un millón, luego puede usarlos para tamizar cualquier intervalo que desee hasta un billón. O bien, si solo desea probar un número por debajo de un billón de primosidad, solo necesita probarlo con números primos menores o iguales a su raíz cuadrada.

Existen otras técnicas para encontrar números primos, por supuesto, y nadie usa el método de Erato para encontrar los realmente grandes.

Puedo aconsejar la optimización: mantenga dos matrices, una para números como 6k-1 y otra para números como 6k + 1, y úsela en estas matrices cada bit. Tendrás 2 casos excepcionales para los números 2 y 3.