Foros del Web » Programación para mayores de 30 ;) » C/C++ »

Buscando números primos, con la criba de Eratóstenes

Estas en el tema de Buscando números primos, con la criba de Eratóstenes en el foro de C/C++ en Foros del Web. Os presento un código donde se aplica a la criba de Eratóstones. Es una forma de encontrar números primos. También hay una forma de aplicación ...
  #1 (permalink)  
Antiguo 25/06/2014, 06:14
Avatar de edumurru  
Fecha de Ingreso: diciembre-2011
Mensajes: 10
Antigüedad: 13 años
Puntos: 0
Buscando números primos, con la criba de Eratóstenes

Os presento un código donde se aplica a la criba de Eratóstones. Es una forma de encontrar números primos. También hay una forma de aplicación por fuerza bruta:

Buscando números primos con la criba de Eratóstenes en C/C++: http://www.brainum.es/code/article/buscando-nmeros-primos-con-la-criba-de-eratstenes
  #2 (permalink)  
Antiguo 25/06/2014, 08:36
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Tiene un fallo: declaras los arrays sin antes dimensionarlos.

Por otro lado se puede mejorar su eficiencia yendo del 3 a n de dos en dos, ya que los pares no son primos

Y si sólo se trata de imprimir, nos podríamos ahorrar el array prims.

El no uso de memoria dinámica limita bastante el valor máximo de n (en mi caso a 100000), cuando con malloc se podría llegar a 100000000.

Con el n corregido y sin prims:

Código C++:
Ver original
  1. #include <stdio.h>
  2. #define N 100 /* hasta aqui calculamos los primos */
  3.  
  4. int main() {
  5.   int i, j, nums[N+1],  x = 1;
  6.   puts("\n\tSon PRIMOS:\n");
  7.   printf( "\t%4d:::%5d\n", x++, 2 );
  8.   for( i = 3; i <= N; i += 2 )
  9.     if( nums[i] != 1 ) {
  10.       printf( "\t%4d:::%5d\n", x++, i );
  11.       for( j = 2; ( i * j ) <= N; j++)
  12.         nums[i * j] = 1;
  13.     }
  14.   printf("\n  Hasta %d hay %d numeros primos\n", N, --x);
  15.   return 0;
  16. }

Sobre le Criba de Eratóstenes se puede mirar estos enlaces:

Criba de Eratóstenes

Criba de Eratóstenes Wikipedia

¡¡¡Saluditos!!!


Última edición por leosansan; 29/06/2014 a las 09:32
  #3 (permalink)  
Antiguo 03/07/2014, 11:06
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 18 años, 5 meses
Puntos: 32
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
El no uso de memoria dinámica limita bastante el valor máximo de n (en mi caso a 100000), cuando con malloc se podría llegar a 100000000.
Prueba de hacer la comparación así:
Código C:
Ver original
  1. j <= N/i
en vez de así:
Código C:
Ver original
  1. ( i * j ) <= N
Y quizá puedas calcular más números.
__________________
github.com/xgbuils | npm/xgbuils
  #4 (permalink)  
Antiguo 03/07/2014, 14:45
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por Pantaláimon Ver Mensaje
Prueba de hacer la comparación así:
Código C:
Ver original
  1. j <= N/i
en vez de así:
Código C:
Ver original
  1. ( i * j ) <= N
Y quizá puedas calcular más números.
Lo tendré en cuenta "Gran Maestro".

Pero el problema radica en el tamaño del array:

Código C++:
Ver original
  1. nums[i * j]

Un fuerte saludo de tu más ferviente discípulo..... porque eres "tú", ¿verdad?. No se me ocurre que "otro" te halla tomado prestado el nick.

¡¡¡Saluditos!!!

  #5 (permalink)  
Antiguo 03/07/2014, 19:53
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 16 años, 6 meses
Puntos: 61
Respuesta: Buscando números primos, con la criba de Eratóstenes

Existe una correccion y unas optimizaciones posibles aun:
- nums deberia inicializarse, de otro modo la instruccion que compara su contenido (linea 9 en el post mostrado previamente) se encontrará con basura.
- El ciclo al interior multiplica 2 veces, una para comparar (linea 11), otra para asignar (linea 12).
Este par de multiplicaciones se puede reemplazar por 1 suma, debido a que el ciclo lo que hace es calcular los factores del numero i, es decir calcula 2*i, 3*i, 4*i, ... ( y asi sucesivamente ) y ese valor utiliza.

En vez de multiplicar todas las veces, se puede guardar el valor anterior y descubrir el siguiente sólo sumando i, asi (ver variable "multiplo"):

Código C++:
Ver original
  1. #include <stdio.h>
  2. #include <memory.h>
  3. #define N 100 /* hasta aqui calculamos los primos */
  4.  
  5. int main() {
  6.   int i, multiplo, nums[N+1],  x = 1;
  7.   // Importante inicializar el arreglo de marcas
  8.   memset( nums, 0, (N + 1) * sizeof(int) );
  9.   puts("\n\tSon PRIMOS:\n");
  10.   printf( "\t%4d:::%5d\n", x++, 2 );
  11.   for( i = 3; i <= N; i += 2 ) {
  12.     if( nums[i] == 0 ) {
  13.        printf( "\t%4d:::%5d\n", x++, i );
  14.        for( multiplo = i+i; multiplo <= N; multiplo += i ) {
  15.             nums[multiplo] = 1;
  16.        }
  17.     }
  18.   }
  19.  
  20.   printf("\n  Hasta %d hay %d numeros primos\n", N, --x);
  21.   return 0;
  22. }

Se puede ver en ejecucion aqui: http://goo.gl/SqVW20
__________________
Visita mi perfil en LinkedIn
  #6 (permalink)  
Antiguo 03/07/2014, 23:54
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por CalgaryCorpus Ver Mensaje
...............................
Este par de multiplicaciones se puede reemplazar por 1 suma, debido a que el ciclo lo que hace es calcular los factores del numero i, es decir calcula 2*i, 3*i, 4*i, ... ( y asi sucesivamente ) y ese valor utiliza.

En vez de multiplicar todas las veces, se puede guardar el valor anterior y descubrir el siguiente sólo sumando i, asi (ver variable "multiplo"):
...............................
Bien observado lo de la multiplicaciónCalgaryCorpus, ya lo he puesto en práctica.

Y lo de inicializar fue un fallo de pasar un código previo, pero también bien observado.

Lo dejo con calloc para además de declarar, inicializar y con N de 10^8 con lo que tarda unos 7 segundos en mostrar el total de primos. Desactivo los printf para que "no pierda tiempo" en calcular el total de primos hasta N:

Código C++:
Ver original
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define N 100000000 /* hasta aqui calculamos los primos */
  4.  
  5. int main() {
  6.   int i, j, z, x = 0;
  7.   int  *nums = calloc( N+1, sizeof ( int ));
  8.   int *prims = calloc( N+1, sizeof ( int ));
  9.   ///puts("\n\tSon PRIMOS:\n");
  10.   ///printf( "\t%4d:::%5d\n", x, 2 );
  11.   prims[x] = 2;
  12.   x++;
  13.   for( i = 3; i <= N; i += 2 )
  14.     if( nums[i] == 0 ) {
  15.       prims[x] = i;
  16.       ///printf( "\t%4d:::%5d\n", x, i );
  17.       x++;
  18.       for( j = ( i <= N ); ( z = ( i * j ) )  <= N; j+=2)
  19.         nums[z] = 1;
  20.     }
  21.   /*for( i = 0; i < x; i ++ )
  22.     printf( "%4d", prims[i] );*/
  23.   printf( "\n  Hasta %d hay %d numeros primos\n\n", N, x );
  24.   free ( nums );
  25.   free ( prims );
  26.   return 0;
  27. }
  28. }


EDITADO:

Mirándolo bien amigo CalgaryCorpus acabo de darme cuenta de un par de detalles. En tu for vas de multiplo = i+i con incrementos de multiplo += i.

Como ejemplo, supongamos para i los valores de 3 y de 5. Aplicándoles el for los valores que tomarían serían de:

3+3=6______5+5=10
+3=9-_____+5=15
+3=12_____+5=20
+3=15_____+5=25
+3=18_____+5=30
.....................

donde se observa que toman de forma inútil valores pares, que de antemano sabemos que no son primos y arrancan siempre con valor par al ser i+i. La forma de evitarlo sería con un paso de 2*i y un valor inicial de 3*i .

Y como ves tu intento de mejora para evitar la multiplicación de i * j se traduce en la introducción de nuevas multiplicaciones. Claro que al ser una de ellas de multiplicar por dos se podría optimizar más fácilmente.

En cualquier caso es de agradecer y motiva que haya gente por ahí que se moleste en mirar con tanto detenimiento los códigos. Gracias por la atención que le has prestado.

Un fuerte saludo.


Última edición por leosansan; 04/07/2014 a las 11:37
  #7 (permalink)  
Antiguo 04/07/2014, 20:07
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 16 años, 6 meses
Puntos: 61
Respuesta: Buscando números primos, con la criba de Eratóstenes

El método de la criba asume que se quiere conocer los números primos y propone una manera de conocerlos.

Pienso que si se implementa haciendo suposiciones (como que los números pares no son primos (lo que es cierto, pero la idea del método es justamente hacer estos descartes en medio del funcionamiento, no en la implementación)), ya no se está enfrente de la implementación de ese método, sino de una optimización que el señor Eratostenes reclamaría como "impura"

Ahora, puesto que a optimizar hemos venido, y hacemos observaciones que permiten reducir el tiempo de ejecución, aquí una nueva versión que utiliza lo que indico:

- No es suficientemente óptimo partir desde 3*i el ciclo interno. El factor a aplicar debe ser el nro primo actual.
- Sumar 2i en cada vuelta no requiere multiplicar en cada vuelta, basta calcularlo 1 vez antes de iterar.

Con estas "mejoras" el calculo demora varios segundos menos. Si se cronometra la ejecución tanto del codigo enviado previamente enviado como éste, son notorias las diferencias.

Código C:
Ver original
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #define N 100000000 /* hasta aqui calculamos los primos */
  5.  
  6. int main() {
  7.   int i, j, z, x = 0;
  8.   int i2;
  9.   int *nums  = (int *) calloc( N+1, sizeof ( int ));
  10.   int *prims = (int *) calloc( N+1, sizeof ( int ));
  11.   ///puts("\n\tSon PRIMOS:\n");
  12.   ///printf( "\t%4d:::%5d\n", x, 2 );
  13.   prims[x] = 2;
  14.   x++;
  15.   int sq = (int) (sqrt(N) + 1);
  16.  
  17.   for( i = 3; i <= N; i += 2 ) {
  18.     if( nums[i] ) continue;
  19.  
  20.     prims[x] = i;
  21.     ///printf( "\t%4d:::%5d\n", x, i );
  22.     x++;
  23.  
  24.     // no tiene sentido realizar los cálculos de abajo si estamos mas alla de la raiz de N
  25.     if( i > sq ) continue;  
  26.  
  27.     i2 = i + i;
  28.     // comenzar desde mas adelante, y sumar 2i cada vez
  29.     for( j = i*i ; j <= N; j += i2) {  
  30.         nums[j] = 1;
  31.     }
  32.   }
  33. //  for( i = 0; i < x; i ++ ) printf( "%4d", prims[i] );
  34.   printf( "\n  Hasta %d hay %d numeros primos\n\n", N, x );
  35.   free ( nums );
  36.   free ( prims );
  37.   return 0;
  38. }
__________________
Visita mi perfil en LinkedIn
  #8 (permalink)  
Antiguo 04/07/2014, 23:18
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por CalgaryCorpus Ver Mensaje
.........................................
Pienso que si se implementa haciendo suposiciones (como que los números pares no son primos (lo que es cierto, pero la idea del método es justamente hacer estos descartes en medio del funcionamiento, no en la implementación)), ya no se está enfrente de la implementación de ese método, sino de una optimización que el señor Eratostenes reclamaría como "impura"
.................................
.

Ahora me has convencido, ya había usado lo de sqrt en la clásica función esPrimo pero se me había pasado aquí.

Sólo quedaría por mejorar el que no "tache" más de una vez un número. Pero eso ya es otra historia....

¡Pero que diablos!, que todo sea por unas décimas de menos:

Código C++:
Ver original
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #define N 100000000 /* hasta aqui calculamos los primos */
  5.  
  6. int main() {
  7.   int i, j, x = 0, repes = 0;
  8.   int i2, sq = (int) (sqrt(N) + 1);
  9.   int *nums  = (int *) calloc( N+1, sizeof ( int ));
  10.   int *prims = (int *) calloc( N+1, sizeof ( int ));
  11.   ///puts("\n\tSon PRIMOS:\n");
  12.   ///printf( "\t%4d:::%5d\n", x, 2 );
  13.   prims[x] = 2;
  14.   x++;
  15.   for( i = 3; i <= N; i += 2 ) {
  16.     if( nums[i] )
  17.       continue;
  18.     prims[x] = i;
  19.     ///printf( "\t%4d:::%5d\n", x, i );
  20.     x++;
  21.     if( i > sq )
  22.       continue;
  23.     i2 = i + i;
  24.     for( j = i * i ; j <= N; j += i2) {
  25.       nums[j] ++;
  26.       if ( nums[j] > 1 )
  27.         repes += nums[j]-1;
  28.         ///printf( "\ti=%4d:::j=%5d   nums[%3d] = %2d\n", i, j,j,nums[j] );
  29.       while( ( j + i2 ) <= N && (nums[ j + i2 ] == 1 ) )
  30.           j += i2;
  31.     }
  32.   }
  33.   ///for( i = 0; i < x; i ++ ) printf( "%4d", prims[i] );
  34.   printf("\n  Hasta %d hay %d numeros primos   repetidos = %d\n\n", N, x, repes);
  35.   free ( nums );
  36.   free ( prims );
  37.   return 0;
  38. }



Y otra vez gracias por la atención prestada.

¡¡¡Saluditos!!!


Última edición por leosansan; 05/07/2014 a las 13:42
  #9 (permalink)  
Antiguo 07/07/2014, 09:34
 
Fecha de Ingreso: junio-2008
Ubicación: Seattle, USA
Mensajes: 733
Antigüedad: 16 años, 6 meses
Puntos: 61
Respuesta: Buscando números primos, con la criba de Eratóstenes

No me gusta que asignar un valor a una variable, por ejemplo

Código C++:
Ver original
  1. x = 1

tome mas tiempo que preguntar que valor tiene y luego asignarla, por ejemplo

Código C++:
Ver original
  1. if( x == 0 )
  2.    x = 1;

Pero el programa mostrado previamente es una muestra que este comportamiento se da, entonces, tomando partido de eso mismo, y otros cambios, se puede bajar aun mas el tiempo de ejecución

- nums en vez de hacerlo un arreglo de enteros, hacerlo un arreglo de bool
- Preguntar si ya se tiene el valor true, y solo asignárselo cuando no lo sea

Entonces la nueva versión queda así:

Código C++:
Ver original
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #define N 100000000 /* hasta aqui calculamos los primos */
  5.  
  6. int main() {
  7.   int i, j, x = 0;
  8.   int i2, sq = (int) (sqrt(N) + 1);
  9.   bool *nums  = (bool *) calloc( N+1, sizeof ( bool ));
  10.   int *prims = (int *) calloc( N+1, sizeof ( int ));
  11.   ///puts("\n\tSon PRIMOS:\n");
  12.   ///printf( "\t%4d:::%5d\n", x, 2 );
  13.   prims[x++] = 2;
  14.   for( i = 3; i <= N; i += 2 ) {
  15.     if( nums[i] )
  16.       continue;
  17.     prims[x] = i;
  18.     ///printf( "\t%4d:::%5d\n", x, i );
  19.     x++;
  20.     if( i > sq )
  21.       continue;
  22.     i2 = i + i;
  23.     for( j = i * i ; j <= N; j += i2) {
  24.       if( !nums[j] )
  25.           nums[j] = true;
  26.     }
  27.   }
  28.   ///for( i = 0; i < x; i ++ ) printf( "%4d", prims[i] );
  29.   printf("\n  Hasta %d hay %d numeros primos\n\n", N, x );
  30.   free ( nums );
  31.   free ( prims );
  32.   return 0;
  33. }

Y esta version deberia tomar menos tiempo que todas las anteriores... creo
Al menos en mis pruebas, asi es
__________________
Visita mi perfil en LinkedIn
  #10 (permalink)  
Antiguo 11/07/2014, 08:25
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por CalgaryCorpus Ver Mensaje
No me gusta que asignar un valor a una variable, por ejemplo

Código C++:
Ver original
  1. x = 1

tome mas tiempo que preguntar que valor tiene y luego asignarla, por ejemplo

Código C++:
Ver original
  1. if( x == 0 )
  2.    x = 1;

Pero el programa mostrado previamente es una muestra que este comportamiento se da, entonces, tomando partido de eso mismo, y otros cambios, se puede bajar aun mas el tiempo de ejecución

- nums en vez de hacerlo un arreglo de enteros, hacerlo un arreglo de bool
- Preguntar si ya se tiene el valor true, y solo asignárselo cuando no lo sea

Entonces la nueva versión queda así:
...................................
Y esta version deberia tomar menos tiempo que todas las anteriores... creo
Al menos en mis pruebas, asi es
Creo que has llevado mi idea de no tachar más de una vez al éxito total. Lo has bordado con el uso de bool en lugar de int.

Y tanto que toma menos tiempo. Para el caso de 10^8 lo baja en casi 1 segundo.

Ya hemos exprimido al máximo la Criba de Erastótenes.... ¿o tal vez no?.

A bote pronto veo un desperdicio de memoria en los arrays usados:

* nums está dimensionado a N y, como sólo usamos los impares, podríamos bajarlo a N/2.

* prims está dimensionado a N y los primos son sólo una fracción de los impares hasta N.

Con estas dos ideas básicas puede aumentar el límite de primos, antes de que cruja el programa, de 10^8 a 2.745 x 10^9, lo que no está nada mal:

Código C++:
Ver original
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. #include <stdbool.h>
  5. #define N 100000000 /** 2745000000: hasta aqui mi PC calcula los primos **/
  6. #define N_medio N/2  /**........ por ahora...y con este metodo.......... **/
  7.  
  8. int main(){
  9.   unsigned int  i, j , z , x = 0 , N_PRIMS , rq ;
  10.   unsigned int potencias[]={100,1000,10000,100000,1000000,10000000,100000000,1000000000};
  11.   unsigned int Relacion, relacion[]={4,5,8,10,12,15,17,19};
  12.   rq = (int) ( sqrt( N ) );
  13. /** por ejemplo sqrt(1.600.000.000) = 40.000 ==> solo llegamos a 39.999 **/
  14.   if ( rq % 2 == 0 )
  15.     rq--;
  16.   for ( i = 0 ; i < 8 ; i++ )
  17.     if ( N / potencias[i] >= 1 && N / potencias[i] < 10 ){
  18.       Relacion = relacion[i];
  19.       break;
  20.     }
  21.   N_PRIMS = ( N / Relacion );
  22.   bool *nums  = calloc( N_medio , sizeof ( bool ) );
  23.   if( !nums ){
  24.     printf("nums::No hay espacio suficiente en memoria");
  25.     return EXIT_FAILURE;
  26.   }
  27.   unsigned int *prims = calloc( N_PRIMS , sizeof ( unsigned int ) );
  28.   if( !prims ){
  29.     printf("prims::No hay espacio suficiente en memoria");
  30.     return EXIT_FAILURE;
  31.   }
  32.   prims[x] = 2;
  33.   for( i = 3  , z = 1 ; i <= N ; i += 2 , z++ ){
  34.     if( nums[z] )
  35.       continue;
  36.     prims[++x] = i ;
  37.     if( i > rq )
  38.       continue;
  39.     for( j = z + z * i ; j <= N_medio ; j += i ){
  40.       if( !nums[j] )
  41.         nums[j] = true;
  42.     }
  43.   }
  44.   printf( "\n\tHasta %u hay %u numeros primos\n" , N , x + 1 );
  45.   printf( "\n\t\tEl ultimo es: %u \n" , prims[x] );
  46.  
  47.         /***** PARA IMPRIMIR POR GRUPOS *****/
  48.  
  49. /** quita el "+" que aparece al final de la siguiente linea  **/
  50.  
  51. /************************************************************+/
  52.   system ( "pause" );
  53.   char cifra[10] , cad1 [] = "%" , cad2 [] = "u" ;
  54.   unsigned int k = 1 , n1 = N , lineas = 0;
  55.   for(  ;  n1 ; k ++, n1 /= 10 );
  56.   sprintf( cifra , "%s%u%s" , cad1 , k , cad2);
  57.   for( i = 0 , j = 1 ; i <= x ; i ++ , j ++ ){
  58.     if( 90 / j == k )
  59.       putchar ( '\n' ) , j = 1 , lineas++;
  60.     if( lineas == 38 )
  61.       system ( "pause" ) , lineas = 0;
  62.     printf( cifra , prims[i] );
  63.   }
  64.   /*************************************************************/
  65.   free ( nums );
  66.   free ( prims );
  67.   return EXIT_SUCCESS;
  68. }

Hago hincapié en el nuevo for, en lugar de:

Código C++:
Ver original
  1. i2 = i + i ;
  2.     for( j = i * i ; j <= N ; j += i2 )

tengo que usar ahora:

Código C++:
Ver original
  1. for( j = z + z * i ; j <= N_medio ; j += i )

Todo es mirarlo un poco para entenderlo .

Y he aquí algunas salidas para alguno de los valores de N, en mi PC claro, en otros más nuevos y potentes se obtendrán mejores resultados:

Código C++:
Ver original
  1. ..........................................................
  2.         Hasta 100000000 hay 5761455 numeros primos
  3.                 El ultimo es: 99999989
  4.   Process returned 0 (0x0)   execution time : 1.877 s
  5. ..........................................................
  6.         Hasta 1000000000 hay 50847534 numeros primos
  7.                 El ultimo es: 999999937
  8.   Process returned 0 (0x0)   execution time : 21.485 s
  9. ..........................................................
  10.         Hasta 1500000000 hay 74726528 numeros primos
  11.                 El ultimo es: 1499999957
  12.   Process returned 0 (0x0)   execution time : 32.334 s
  13. ..........................................................
  14.         Hasta 2000000000 hay 98222287 numeros primos
  15.                 El ultimo es: 1999999973
  16.   Process returned 0 (0x0)   execution time : 42.225 s
  17. ..........................................................
  18.         Hasta 2500000000 hay 121443371 numeros primos
  19.                 El ultimo es: 2499999977
  20.   Process returned 0 (0x0)   execution time : 54.648 s
  21. ..........................................................
  22.         Hasta 2745000000 hay 132740843 numeros primos
  23.                 El ultimo es: 2744999969
  24.   Process returned 0 (0x0)   execution time : 59.215 s
  25. ..........................................................

Y quedo a la espera de nuevas perspectivas amigo CalgaryCorpus.


Última edición por leosansan; 11/07/2014 a las 11:01
  #11 (permalink)  
Antiguo 30/07/2014, 10:38
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 18 años, 5 meses
Puntos: 32
Respuesta: Buscando números primos, con la criba de Eratóstenes

No me acordaba que había participado aquí. Con lo que me gustan las frikadas numéricas. Aquí una vuelta de tuerca al algoritmo sin perder la esencia (ansi C):

Código C:
Ver original
  1. #include <stdio.h>
  2. #include <math.h>
  3. #include <stdlib.h>
  4.  
  5. int main(void) {
  6.    unsigned top = 1000000000;
  7.    unsigned rtop = sqrt(top);
  8.    unsigned char* nums = calloc( top, sizeof *nums);
  9.    unsigned primsTop = 2.2 * (top / log(top)); /* apaño para ahorrar memoria */
  10.    unsigned* prims = calloc( primsTop, sizeof *prims);
  11.    unsigned n; /* contador primos */
  12.    unsigned i, i2, ii;
  13.    unsigned j, k, l, x;
  14.  
  15.    k = x = nums[4] = nums[6] = nums[8] = 1;
  16.    i = l = n = prims[0] = 2;
  17.    prims[1] = 3;
  18.    l = 5;
  19.    i2 = i + i;
  20.    ii = i * i;
  21.  
  22.    /* calculo de la tabla */
  23.    for (i = 3; i <= rtop; i += 2, i2 += 4) {
  24.       /* El primer primo de la tabla es el primero con valor 0 */
  25.       if(nums[i]) continue;
  26.  
  27.       /* todo numero j tal que l <= j <= i*i-1 con nums[j] == 0 es primo */
  28.       ii = i*i;
  29.       for (j = l; j < ii; j += 2 )
  30.          if(nums[j] == 0)
  31.             prims[n++] = j;
  32.       l = ii+2;
  33.      
  34.       /* Dado k que va de x a n-1, se descartan los compuestos i*prims[k]    *
  35.        * donde se ve que i == prims[x]                                       *
  36.        * (Es una forma de evitar descartar números que ya están descartados) */
  37.       for (k = x; k < n; ++k) {
  38.          j = i*prims[k];
  39.          if (j >= top) break;
  40.          nums[j] = 1;
  41.       }
  42.      
  43.       /* a partir de aquí, se descartan los compuestos de la manera común */
  44.       if (j < top) j += i2;
  45.       for (; j < top; j += i2) {
  46.          if(!nums[j])
  47.             nums[j] = 1;
  48.       }
  49.       ++x;
  50.    }
  51.  
  52.    /* calcular resto de primos */
  53.    for (i = prims[n-1]+2; i < top; i += 2) {
  54.       if (nums[i] == 0) {
  55.          ++n;
  56.          /* prims[n++] = i; */
  57.       }
  58.    }
  59.    
  60.    printf("%d primos\n", n);
  61.    free(nums);
  62.    free(prims);
  63.  
  64.    return 0;
  65. }

Un saludo!
__________________
github.com/xgbuils | npm/xgbuils
  #12 (permalink)  
Antiguo 30/07/2014, 13:43
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por Pantaláimon Ver Mensaje
No me acordaba que había participado aquí. Con lo que me gustan las frikadas numéricas. Aquí una vuelta de tuerca al algoritmo sin perder la esencia (ansi C):
.................................................. ......
Un saludo!
No sé por qué extraño motivo no me compila declarando la variable "top" como unsigned, he de declararla como int.

La salida para los 100 primeros primos no es la esperada:

Código C++:
Ver original
  1. 24 primos
  2.   2  3  5  7 11 13 17 19 23 27 31 39 43 47  0  0  0  0  0  0  0  0  0  0
  3. Process returned 0 (0x0)   execution time : 0.040 s
  4. Press any key to continue.

"Creo" que son 25, pero eso no es más que un ajuste mínmo. Lo raro es que a partir del 47 son todos ceros

Como todos tus códigos requiere de un examen más minucioso a ver que sucede.

Por cierto, la aproximación que usas para limitar la memoria de los "prims" es demasiado "generosa". Como ejemplo para los 100 primeros números da un valor de 48, cuando en realidad son tan solo 25 tal como lo "clava" la relación que uso en mi código, y de CalgaryCorpus por supuesto.

Y ya para 100000000 ni te cuento, da 9375126 primos cuando en realidad son "5761455" con lo que el ajuste no ha de ser tan mínimo como creí en un principio.

Creo que tienes que "afinarlo" un poquitín.

Bueno lo miraré más detenidamente.

¡¡¡Saluditos!!!



Y por cierto, bienvenido otra vez.
  #13 (permalink)  
Antiguo 30/07/2014, 23:34
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 18 años, 5 meses
Puntos: 32
Respuesta: Buscando números primos, con la criba de Eratóstenes

Vaya! Quizá no testeé suficiente. Esta tarde cuando tenga un rato lo reviso.

La aproximación la saqué de aquí y le puse una constante multiplicativa para que nunca fuera menor que el número -al parecer erróneo- de primos que me daba para las pruebas que hice. Como es una aproximación asintótica, en teroía debería ajustarse un poco más cuando el número de primos a analizar es más grande.

Un saludo!
__________________
github.com/xgbuils | npm/xgbuils
  #14 (permalink)  
Antiguo 31/07/2014, 06:44
Avatar de leosansan  
Fecha de Ingreso: mayo-2012
Ubicación: GRAN CANARIA
Mensajes: 194
Antigüedad: 12 años, 6 meses
Puntos: 49
Respuesta: Buscando números primos, con la criba de Eratóstenes

Cita:
Iniciado por Pantaláimon Ver Mensaje
Vaya! Quizá no testeé suficiente. Esta tarde cuando tenga un rato lo reviso.

La aproximación la saqué de aquí y le puse una constante multiplicativa .................................................. ..
Lo imaginaba, lo de la aproximación. Fui más practico y busqué la relación o ciente entre el número y los primos que correspondían, por eso mi relación es tan ajustada.

Quedo, ansioso eso sí, a la espera de tus mejoras, "¡ fenómeno !".

¡¡¡Saluditos!!!

  #15 (permalink)  
Antiguo 31/07/2014, 08:44
 
Fecha de Ingreso: julio-2006
Ubicación: Barcelona
Mensajes: 244
Antigüedad: 18 años, 5 meses
Puntos: 32
Respuesta: Buscando números primos, con la criba de Eratóstenes

Vaya, era la variable i2, que debería tener en todo momento el valor de 2*i pero no lo tuvo por un despiste. El código ahora apenas mejora el tuyo en velocidad:

Código C:
Ver original
  1. #include <stdio.h>
  2. #include <math.h>
  3. #include <stdlib.h>
  4.  
  5. int main(void) {
  6.    unsigned top = 1000000000;
  7.    unsigned rtop = sqrt(top);
  8.    unsigned char* nums = calloc( top, sizeof *nums);
  9.    unsigned primsTop = 1.2 * (top / log(top)); /* apaño para ahorrar memoria */
  10.    unsigned* prims = calloc( primsTop, sizeof *prims);
  11.    unsigned n; /* contador primos */
  12.    unsigned i, i2, ii;
  13.    unsigned j, k, l, x;
  14.  
  15.    k = x = nums[4] = nums[6] = nums[8] = 1;
  16.    l = n = prims[0] = 2;
  17.    i = prims[1] = 3;
  18.    l = 5;
  19.    i2 = i + i;
  20.    ii = i * i;
  21.  
  22.    /* calculo de la tabla */
  23.    for (i = 3; i <= rtop; i = prims[x], i2 = 2*i) {
  24.  
  25.       /* todo numero j tal que l <= j <= i*i-1 con nums[j] == 0 es primo */
  26.      
  27.       ii = i*i;
  28.       /* printf("A) calcula primos de %d a %d\n", l, ii-1); */
  29.       for (j = l; j < ii; j += 2)
  30.          if(nums[j] == 0){
  31.             prims[n++] = j;
  32.             //printf("%d\n", prims[n-1]);
  33.          }
  34.       l = ii+2;
  35.      
  36.       /* Dado k que va de x a n-1, se descartan los compuestos i*prims[k]    *
  37.        * donde se ve que i == prims[x]                                       *
  38.        * (Es una forma de evitar descartar números que ya están descartados) */
  39.       /* printf("B) descartar primos de %d a %d\n", i*prims[x], i*prims[n-1]); */
  40.       for (k = x; k < n; ++k) {
  41.          j = i*prims[k];
  42.          if (j >= top) break;
  43.          nums[j] = 1;
  44.          /* printf("%d\n", j); */
  45.       }
  46.      
  47.       /* a partir de aquí, se descartan los compuestos de la manera común */
  48.  
  49.       if (j < top) j += i2;
  50.       /* printf("C) descartar primos de %d a %d\n", j, top-1); */
  51.       for (; j < top; j += i2) {
  52.          if(!nums[j]) {
  53.             nums[j] = 1;
  54.             /*printf("%d\n", j);*/
  55.          }
  56.       }
  57.       ++x;
  58.       //printf("%d\n", prims[x]);
  59.    }
  60.  
  61.    /* calcular resto de primos */
  62.    for (i = prims[n-1]+2; i < top; i += 2) {
  63.       if (nums[i] == 0) {
  64.          prims[n] = i;
  65.          ++n;
  66.       }
  67.    }
  68.    /*
  69.    for(i = 0; i < n; ++i) {
  70.       printf("%d, ", prims[i]);
  71.    }
  72.    */
  73.    printf("%d primos\n", n);
  74.    printf("%f\n", (double)n/primsTop);
  75.    free(nums);
  76.    free(prims);
  77.  
  78.    return 0;
  79. }

Edit: añadida nueva modificación que mejora unas décimas.

Un saludo!
__________________
github.com/xgbuils | npm/xgbuils

Última edición por Pantaláimon; 31/07/2014 a las 10:00

Etiquetas: buscando, primos
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 06:08.