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

A voir également:

7 réponses

yg_be Messages postés 23541 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 584
 

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

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

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

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

0
Grifix > yg_be Messages postés 23541 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
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
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
Grifix
 

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

Merci!

0
Grifix
 

Que nenni mon algo fonctionne!!!

Voir ici :

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

Rentrer val= 80000000 maxi

0

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

Posez votre question
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
vaanbasch Messages postés 770 Date d'inscription   Statut Membre Dernière intervention   662
 

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