Optimisation d’un crible d’Erathostene en C

Résolu

Bonjour,

J’aimerais optimiser mon code s'il vous plaît.

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h>

int main() {
    int val;
    printf("\n (Programme Grifix  programme de recherche des Nombres premiers optimisé avec amelioration gestion memoire sur des bits) \n Insérez la valeur sup de vos nombres : "); 
    scanf("%d", &val);
    unsigned char *Tab = (unsigned char *) malloc((val + 7) / 8); // On alloue le nombre de bits nécessaires
    if (Tab == NULL)
    {
        printf("Erreur d'allocation de mémoire\n");
    }
    int X = 3;
    int compteur = 1;
    int nombre_de_premiers = 0;
    time_t debut = time(NULL);
    while (compteur > 0)
    {
        for (int n =0 ; X * (X + 2 * n) < val ; n++)
        {
            if (!(Tab[X / 8] & (1 << (X % 8))))
            {
                Tab[X * (X +2 * n) / 8] |= (1 << (X * (X + 2 * n) % 8));
                compteur = n;
            }
            else
            {
                // printf("\n saut pour X= %d", X);
                goto suite;
            }
        }
suite:
        X = X + 2;
        compteur--;
    }
    time_t fin = time(NULL);
    printf ("\nTemps de recherche : %d secondes \n ", fin - debut);
    for (int i =3; i < val; i = i + 2)
    {
        if ((Tab[i / 8] & (1 << (i % 8))) ==0)
        {
            nombre_de_premiers ++;
            // printf ("   %d", i);
        }
    }
    printf("On a trouvé %d nombres premiers", nombre_de_premiers);
    free(Tab); // Libération de la mémoire allouée
    return 0;
}

Avez-vous une idée ? Merci !

Android / Chrome 129.0.0.0

7 réponses

  1. Contributeur

    bonjour,

    je me demande pourquoi tu as mis ce IF à l'intérieur du FOR:

                                 for (int n =0 ;  X*(X+2*n)< val ; n++)
                                   {
                                     if (!(Tab[X / 8] & (1 << (X % 8))))
    0
    1. Pour éviter les redondances de calcul pour les multiples sur des multiples

      Multiples calculés sur les carrés de 9 15 21 25 27 etc etc

      Cdlt

      0
    2. Contributeur
      @Grifix

      Le IF est utile, mais pourquoi ne pas le mettre à l'extérieur de la boucle FOR?

      0
    3. @yg_be

      J’ai vraiment l’habitude de commencer les trucs à l’envers pour terminer sur des résultats à peu près droit. Essayes ton truc je crois pas que ça marche. Le meilleur prog a été donné par mr Vaanbasch. 

      L’idée est la même mais le déroulement est concis et cohérent.

      On comprend mieux le programme si on traite le sujet avec un Tableau de booleen.

      Je suis pas programmeur et le sujet des NP est très intéressant mais peut être il est encore plus fascinant si on l’aborde d’une autre manière que les maths ou l’informatique. 

      Mais ce n’est pas le lieu pour en débattre!

      Il serait plus judicieux de poser ta question aux spécialistes du forum mais je vois qu’ils ont fait pareil. Je peux te donner mon programme mais il serait bancal bien que fonctionnant correctement. 

      Cdlt

      0
    4. Contributeur
      @Grifix

      Si tu lis le code suggéré par Vaanbasch, le IF est hors du FOR.

      0
    5. @yg_be
      for (X = 3; X * X <= val; X += 2) {
              // Si X est un nombre premier (non marqué)
              if (!(Tab[X / 8] & (1 << (X % 8)))) {
                  // Marquer les multiples impairs de X à partir de X*X
                  for (int n = X * X; n < val; n += 2 * X) {
                      Tab[n / 8] |= (1 << (n % 8));
                  }

      T’es sûr !!!!!  

      0
  2. Tu pourrais essayer de remplacer les divisions par 8 par un décalage de 3 (x >> 3) et les modulo 8 par un masque (x & 7)

    Le truc pour sauver de la mémoire est bon, mais ton algo est loin d'être le meilleur.

    Tu en trouveras de meilleurs en cherchant sur Internet.

    0
    1. J'ai pris la peine de tester ton code et il ne fonctionne définitivement pas.
      Pour 100, j'obtiens plus d'une valeur suivant les tests. Ça varie de 26 à 29.
      Il est évident que tu accèdes à une zone de mémoire non initialisée.
      Avant de chercher à optimiser, il faut commencer par faire fonctionner le code.
      Ton approche avec le GOTO est plutôt mauvaise.
      Tu n'as besoin que de deux boucles FOR imbriquées avec un IF pour savoir si le nombre suivant est premier.

      0
      1. Oui pour les 2 boucles for avec un if Mr Vaanbasch m’a montré le chemin.

        Merci!

        0
    2. Que nenni mon algo fonctionne!!!

      Voir ici :

      https://www.mycompiler.io/view/4hYfMMdgMIf

      Rentrer val= 80000000 maxi

      0
      1. Même programme en JScript
        https://reduction-image.com/eugenol/Premiers_allocation_memoireJS.html
        Rechercher pour 2500000000
        Temps de recherche : 75893 millisecondes
        On a trouvé 121443370 nombres premiers

        0
        1. Bonjour, il y a plusieurs "erreurs".

          Attention au GoTo. Tu peux utiliser un simple "if".

          Je te propose ce que tu as demandé.

          #include <stdio.h>
          #include <stdlib.h>
          #include <time.h>
          
          int main() {
              int val;
              printf("\n(Programme Grifix - recherche des nombres premiers optimisé)\nInsérez la valeur sup de vos nombres : ");
              scanf("%d", &val);
          
              // Allocation de mémoire pour le tableau de bits
              unsigned char *Tab = (unsigned char *)calloc((val + 7) / 8, sizeof(unsigned char));
              if (Tab == NULL) {
                  printf("Erreur d'allocation de mémoire\n");
                  return 1;
              }
          
              int X = 3;
              int nombre_de_premiers = 1; // On compte '2' comme premier
              time_t debut = time(NULL);
          
              // Parcours des nombres impairs pour marquer les non-premiers
              for (X = 3; X * X <= val; X += 2) {
                  // Si X est un nombre premier (non marqué)
                  if (!(Tab[X / 8] & (1 << (X % 8)))) {
                      // Marquer les multiples impairs de X à partir de X*X
                      for (int n = X * X; n < val; n += 2 * X) {
                          Tab[n / 8] |= (1 << (n % 8));
                      }
                  }
              }
          
              // Calcul du nombre de premiers
              for (int i = 3; i < val; i += 2) {
                  if ((Tab[i / 8] & (1 << (i % 8))) == 0) {
                      nombre_de_premiers++;
                  }
              }
          
              time_t fin = time(NULL);
              printf("\nTemps de recherche : %ld secondes\n", fin - debut);
              printf("On a trouvé %d nombres premiers\n", nombre_de_premiers);
          
              free(Tab); // Libération de la mémoire allouée
              return 0;
          }

           N'hésite pas à revenir si tu veux des explications.


          0
          1. Merci à vous.

            Très bonne rectification en effet.

            Le 2 était une négligence mais bon au temps pour moi

            J’ai gagné 10 secondes sur une entrée de 2000000000

            Je ne suis pas informaticien et connaît mal le C .

            Merci encore Mr Vaanbasch.

            F Tamisier

            0
        2. Puisqu'on tolère les solutions, voici la mienne.
          Non, je ne suis pas "monsieur" PierrotLeFou. :)
           

          #include <stdio.h>
          #include <stdlib.h>
          #include <string.h>
          #include <math.h>
          #include <time.h>
          #define uchar unsigned char
          // J'utilise size_t au lieu de uint64
          int main(void) {
              time_t begin = time(NULL);
              uchar mask[8] = {1, 2, 4, 8, 16, 32, 64, 128};
              for(int i = 0; i < 8; i++) mask[i] = ~mask[i];     // J'inverse pour favoriser la boucle intérieure.
              size_t max;
              printf("Entrez le maximum ");
              scanf("%lld", &max);
              size_t size = (max+8) >> 3;     // Le maximum est inclus.
              uchar *tab = malloc(size);
              if(tab == NULL) {
                  perror("malloc");
                  fprintf(stderr, "Requested: %lld\n", size);
                  exit(EXIT_FAILURE);
              }
              memset(tab, 0xff, size);     // Des 1 partout.
              size_t r = sqrt(max);     // Si max est plus grand que 2^53, il se peut que r soit incorrect.
              for(size_t n = 3; n <= r; n += 2) {
                  if(tab[n>>3] & ~mask[n&7]) {
                      for(size_t i = n*n; i <= max; i += n<<1) tab[i>>3] &= mask[i&7];
                  }
              }
              for(size_t n = max+1; n < size*8; n++) tab[n>>3] &= mask[n&7];     // J'efface la fin du  dernier octet.
              uchar many[256];     // Pour compter le nombre de bits dans chaque octets.
              uchar msk = 2 + 8 + 32 + 128;     // Je garde les bits en positions impaires seulement.
              for(int i = 0; i < 256; i++) {
                  uchar b = i & msk;
                  uchar c = 0;
                  for(int s = 0; s < 4; s++) {
                      b >>= 1;
                      c += b &1;
                      b >>= 1;
                  }
                  many[i] = c;
              }
              size_t count = 0;     // Le 1 n'est pas effacé, il comptera pour le 2.
              for(int n = 0; n < size; n++) {
                  count += many[tab[n]];     // On divise environ par 4 le nombre d'opérations.
              }
              printf("%ld millisecondes\n", time(NULL) - begin);
              printf("%lld\n", count);
              free(tab);
              return EXIT_SUCCESS;
          }
          0