¿Código más eficiente para los primeros 10000 números primos?

votos
49

Quiero imprimir los primeros 10000 números primos. ¿Alguien puede darme el código más eficiente para esto? Aclaraciones:

  1. No importa si tu código es ineficiente para n> 10000.
  2. El tamaño del código no importa.
  3. No puedes codificar los valores de ninguna manera.
Publicado el 03/08/2008 a las 06:45
fuente por usuario
En otros idiomas...                            


29 respuestas

votos
41

El Tamiz de Atkin es probablemente lo que estás buscando, su tiempo límite de ejecución es O (N / log log N).

Si solo ejecuta los números 1 más y 1 menos que los múltiplos de 6, podría ser aún más rápido, ya que todos los números primos por encima de 3 están a 1 de distancia de un múltiplo de seis. Recurso para mi declaración

Respondida el 03/08/2008 a las 07:03
fuente por usuario

votos
4

No es eficiente en absoluto, pero puede usar una expresión regular para probar los números primos.

/^1?$|^(11+?)\1+$/

Esto prueba si, para una cadena que consta de k " 1" s, k no es primo (es decir, si la cadena consta de un " 1" o cualquier cantidad de " 1" s que se pueda expresar como un producto n -ary).

Respondida el 03/08/2008 a las 19:52
fuente por usuario

votos
4

He adaptado el código encontrado en CodeProject para crear lo siguiente:

ArrayList primeNumbers = new ArrayList();

for(int i = 2; primeNumbers.Count < 10000; i++) {
    bool divisible = false;

    foreach(int number in primeNumbers) {
        if(i % number == 0) {
            divisible = true;
        }
    }

    if(divisible == false) {
        primeNumbers.Add(i);
        Console.Write(i + " ");
    }
}

Probar esto en mi Servidor ASP.NET tomó la rountine alrededor de 1 minuto para ejecutarse.

Respondida el 05/08/2008 a las 20:55
fuente por usuario

votos
35

Recomiendo un tamiz, ya sea el Tamiz de Eratóstenes o el Tamiz de Atkin.

El tamiz o Eratóstenes es probablemente el método más intuitivo para encontrar una lista de primos. Básicamente tú:

  1. Escriba una lista de números del 2 al límite que desee, digamos 1000.
  2. Tome el primer número que no esté tachado (para la primera iteración esto es 2) y tache todos los múltiplos de ese número de la lista.
  3. Repite el paso 2 hasta llegar al final de la lista. Todos los números que no están tachados son primos.

Obviamente hay bastantes optimizaciones que se pueden hacer para que este algoritmo funcione más rápido, pero esta es la idea básica.

El tamiz de Atkin usa un enfoque similar, pero desafortunadamente no sé lo suficiente como para explicarte. Pero sí sé que el algoritmo que he vinculado toma 8 segundos para calcular todos los números primos hasta 1000000000 en un antiguo Pentium II-350

Tamiz de Eratóstenes Código Fuente: http://web.archive.org/web/20140705111241/http://primes.utm.edu/links/programs/sieves/Eratosthenes/C_source_code/

Código fuente de Sieve of Atkin: http://cr.yp.to/primegen.html

Respondida el 06/08/2008 a las 00:49
fuente por usuario

votos
3

Sieve of Eratosthenes es el camino a seguir, debido a su simplicidad y velocidad. Mi implementación en C

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

int main(void)
{
    unsigned int lim, i, j;

    printf("Find primes upto: ");
    scanf("%d", &lim);
    lim += 1;
    bool *primes = calloc(lim, sizeof(bool));

    unsigned int sqrtlim = sqrt(lim);
    for (i = 2; i <= sqrtlim; i++)
        if (!primes[i])
            for (j = i * i; j < lim; j += i)
                primes[j] = true;

    printf("\nListing prime numbers between 2 and %d:\n\n", lim - 1);
    for (i = 2; i < lim; i++)
        if (!primes[i])
            printf("%d\n", i);

    return 0;
}

Tiempo de CPU para encontrar primos (en Pentium Dual Core E2140 1.6 GHz, utilizando un solo núcleo)

~ 4s por lim = 100,000,000

Respondida el 21/08/2008 a las 00:45
fuente por usuario

votos
11

GateKiller , ¿qué le parece agregarle breaka eso ifen el foreachbucle? Eso sería acelerar las cosas mucho , porque si como 6 es divisible por 2 que no es necesario comprobar con 3 y 5. (Votaría su solución de todos modos si tuviera la reputación suficiente :-) ...)

ArrayList primeNumbers = new ArrayList();

for(int i = 2; primeNumbers.Count < 10000; i++) {
    bool divisible = false;

    foreach(int number in primeNumbers) {
        if(i % number == 0) {
            divisible = true;
            break;
        }
    }

    if(divisible == false) {
        primeNumbers.Add(i);
        Console.Write(i + " ");
    }
}
Respondida el 27/08/2008 a las 21:26
fuente por usuario

votos
7

Usando GMP, uno podría escribir lo siguiente:

#include <stdio.h>
#include <gmp.h>

int main() {
  mpz_t prime;
  mpz_init(prime);
  mpz_set_ui(prime, 1);
  int i;
  char* num = malloc(4000);
  for(i=0; i<10000; i++) {
    mpz_nextprime(prime, prime);
    printf("%s, ", mpz_get_str(NULL,10,prime));
  }
}

En mi Macbook Pro a 2.33 GHz, se ejecuta de la siguiente manera:

time ./a.out > /dev/null

real    0m0.033s
user    0m0.029s
sys    0m0.003s

Cálculo de 1,000,000 primos en la misma computadora portátil:

time ./a.out > /dev/null

real    0m14.824s
user    0m14.606s
sys     0m0.086s

GMP está altamente optimizado para este tipo de cosas. A menos que realmente desee comprender los algoritmos escribiendo el suyo, se le aconsejará usar libGMP en C.

Respondida el 29/08/2008 a las 08:06
fuente por usuario

votos
17

Esto no es estrictamente contrario a la restricción de codificación rígida, pero llega terriblemente cerca. ¿Por qué no descargar programáticamente esta lista e imprimirla?

http://primes.utm.edu/lists/small/10000.txt

Respondida el 31/08/2008 a las 23:20
fuente por usuario

votos
9

@Matt: log (log (10000)) es ~ 2

Del artículo de wikipedia (que citó) Sieve of Atkin :

Este tamiz calcula números primos hasta N usando O(N/log log N)operaciones con solo N 1/2 + o (1) bits de memoria. Eso es un poco mejor que el tamiz de Eratóstenes que usa O(N) operaciones y O (N 1/2 (log log N) / log N) bits de memoria (AOL Atkin, DJ Bernstein, 2004) . Estas complejidades computacionales asintóticas incluyen optimizaciones simples, como la factorización de las ruedas, y la división del cálculo en bloques más pequeños.

Dadas las complejidades computacionales asintóticas a lo largo O(N)(para Eratóstenes) y O(N/log(log(N)))(para Atkin) no podemos decir (para las pequeñas N=10_000) qué algoritmo, si se implementa, será más rápido.

Achim Flammenkamp escribió en El tamiz de Eratóstenes :

citado por:

@ num1

Para intervalos mayores alrededor de 10 ^ 9, seguramente para aquellos> 10 ^ 10, el Tamiz de Eratóstenes es superado por el Tamiz de Atkins y Bernstein que usa formas cuadráticas binarias irreductibles. Consulte su documento para obtener información de antecedentes, así como el párrafo 5 del Ph.D. de W. Galway. tesis.

Por lo tanto, para 10_000Sieve of Eratosthenes puede ser más rápido que Sieve of Atkin.

Para responder a OP el código es prime_sieve.c (citado por num1)

Respondida el 06/10/2008 a las 21:03
fuente por usuario

votos
2

Adaptando y siguiendo de GateKiller , aquí está la versión final que he usado.

    public IEnumerable<long> PrimeNumbers(long number)
    {
        List<long> primes = new List<long>();
        for (int i = 2; primes.Count < number; i++)
        {
            bool divisible = false;

            foreach (int num in primes)
            {
                if (i % num == 0)
                    divisible = true;

                if (num > Math.Sqrt(i))
                    break;
            }

            if (divisible == false)
                primes.Add(i);
        }
        return primes;
    }

Es básicamente lo mismo, pero agregué la sugerencia "break on Sqrt" y cambié algunas de las variables para que me quedara mejor. (Estaba trabajando en Euler y necesitaba el primo 10001)

Respondida el 16/02/2009 a las 06:17
fuente por usuario

votos
2

El tamiz parece ser la respuesta incorrecta. El tamiz te da los números primos hasta un número N , no los primeros N primos. Ejecute @Imran o @Andrew Szeto, y obtendrá los números primos hasta N.

El tamiz podría ser útil si sigues probando tamices para números cada vez más grandes hasta que alcances un determinado tamaño de tu conjunto de resultados, y utilizas un poco de almacenamiento en caché de los números ya obtenidos, pero creo que todavía no sería más rápido que una solución como @ Pat's .

Respondida el 19/06/2009 a las 19:12
fuente por usuario