Optimisation d’un crible d’Erathostene en C

Résolu
Grifix -  
 PierrotLeFou -

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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     

    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. Grifix
       

      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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > Grifix
       

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

      0
    3. Grifix > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
       

      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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > Grifix
       

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

      0
    5. Grifix > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
       
      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. PierrotLeFou
     

    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
  3. PierrotLeFou
     

    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. Grifix
       

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

      Merci!

      0
  4. Grifix
     

    Que nenni mon algo fonctionne!!!

    Voir ici :

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

    Rentrer val= 80000000 maxi

    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. Grifix
     

    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
  7. vaanbasch Messages postés 794 Date d'inscription   Statut Membre Dernière intervention   666
     

    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. Grifix
       

      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
  8. PierrotLeFou
     

    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