begin process at 2012 05 27 15:15:35
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Maths & Algorithmes

 > CRIBLE D'ERATOSTHÈNE OPTIMISÉ

CRIBLE D'ERATOSTHÈNE OPTIMISÉ


 Information sur la source

Note :
Aucune note
Catégorie :Maths & Algorithmes Classé sous :crible, Eratosthène, nombres premiers, optimisation Niveau :Débutant Date de création :26/06/2011 Date de mise à jour :26/06/2011 17:57:06 Vu / téléchargé :2 835 / 51

Auteur : pgl10

Ecrire un message privé
Site perso
Commentaire sur cette source (9)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
Voici un calcul des nombres premiers compris entre 1 une limite donnée. Ce calcul utilise une version optimisée de l'algorithme tradionnel d'Eratosthène. Sur mon ordinateur, déjà un peu ancien, je calcule les 50847534 nombres premiers compris entre 1 et 1000000000 en 19.5 secondes. Ce logiciel est pévu pour aller à la limite des "unsigned int" du PC : (2^32)-1 Cependant pour des raisons de programmation expliquées la limite utilisable de l'intervalle de calcul est : 4294709602. Une documentation supplémentaire est incluse dans l'envoi. Et le source est largement commenté. L'optimisation porte sur la taille mémoire utilisée et sur le temps de calcul : les nombres pairs sont traités à part et les multiples de 3 ne sont pas repris ensuite. Il me semble que les nombreux autres programmes disponibles sur Internet qui utilisent l'algorithme d'Eratosthène n'ont pas une efficacité semblable. Sur cette base, il serait possible de développer une variante qui dépasserait la capacité limite des "unsigned int".

Source

  • /* Crible d'Eratosthène par pgl10 */
  • #include <stdio.h> // pour printf
  • #include <stdlib.h> // pour system
  • #include <string.h> // pour memset
  • #include <time.h> // pour clock
  • #include <math.h> // pour sqrt
  • typedef unsigned long ulong;
  • #define ulmax 4294967295 // (2^32)-1 : valeur maximum des unsigned long
  • ulong nmax; // borne supérieure de l'intervalle [1,nmax] de calcul utilisé
  • char* crible; // crible[1+(nmax-1)/16] : une suite de (1+nmax)/2 bits utiles
  • // quand le calcul sera fini les nombres pairs ne seront pas marqués
  • // et le (1+i)/2-ième bit de crible mis à 1 quand i est premier et 0 si non.
  • // crible[0] pour les impairs de 1 à 15, crible[1] pour les impairs de 17 à 31, etc.
  • void pause() {
  • printf("\n");
  • system("pause");
  • }
  • void comp(ulong i) { // mettre à 0 le (1+i)/2-ième bit du crible ( i impair et composé )
  • ulong q, r;
  • char c[9]={'\xFF', '\xFE', '\xFD', '\xFB', '\xF7', '\xEF', '\xDF', '\xBF', '\x7F'};
  • // if(i%2==0) return; // inutile : tous les nombres pairs ne sont pas marqués
  • q=i>>4; // q=i/16=(x-1)/8 avec x=(1+i)/2 ou x=1+i/2 pour i impair
  • r=((1+i)>>1)-(q<<3); // r=(1+i)/2-8*q=x-8*q avec x=(1+i)/2 ( r de 1 à 8 )
  • crible[q]=crible[q]&c[r]; // on met à 0 le bit (1+i)/2 du crible quand i est impair
  • }
  • bool prem(ulong i) { // retourner true si le nombre i est premier et false si non
  • ulong q, r;
  • if(i<2 || i>nmax) return false;
  • if(i==2) return true;
  • if(i%2==0) return false;
  • q=i>>4; // q=0 si i de 1 à 15, q=1 si i de 17 à 31, etc.
  • r=((1+i)>>1)-(q<<3);
  • if(crible[q]>>(r-1)&1) return true; // si le bit (1+i)/2 de crible est à 1
  • return false;
  • }
  • void Eratosthene(ulong n) { // crible d'Eratosthène
  • ulong i, j, m, q, r, s, t;
  • char c[9]={'\xFF', '\xFE', '\xFD', '\xFB', '\xF7', '\xEF', '\xDF', '\xBF', '\x7F'};
  • memset(crible, '\xFF', 1+(n-1)/16); // initialisation de crible[]
  • comp(1); // 1 n'est pas premier
  • for(j=9; j<=n; j=j+6) { // les multiples de 2 ne sont pas marqués
  • q=j>>4; // pour marquer les multiples impairs de 3
  • r=((1+j)>>1)-(q<<3); // on évite d'appeler comp(j) pour gagner
  • crible[q]=crible[q]&c[r]; // un peu de temps
  • }
  • m=1+(ulong)sqrt((double)n);
  • s=4;
  • for(i=5; i<m; i=i+s) { // on marque les multiples des autres
  • if(prem(i)) { // nombres premiers connus en évitant
  • if(((i+2)*i)%3) t=4; else t=2; // les multiples de 2 et 3
  • for(j=i*i; j<=n ; j=j+t*i ) {
  • q=j>>4; // ici j est toujours impair
  • r=((1+j)>>1)-(q<<3);
  • if(crible[q]>>(r-1)&1) crible[q]=crible[q]&c[r];
  • t=6-t;
  • // if(j>ulmax-t*i) { // pour éviter un dépassement de capacité des ulong
  • // printf("\nLa limite de l'intervalle de calcul est trop grande\n");
  • // pause(); // pour gagner du temps on peut éviter ce contrôle,
  • // exit(1); // mais il faut veiller à utiliser nmax < 4294709603
  • // }
  • }
  • }
  • s=6-s;
  • }
  • }
  • int main (int argc, char *argv[]) {
  • ulong i, np=0, p1=0, pn;
  • char limite[21];
  • if(argc!=2) {
  • printf("\nPour calculer les nombres premiers de 1 \205 nmax\n");
  • printf("\nlancez ce programme en faisant : crible nmax\n");
  • pause();
  • return 1;
  • }
  • nmax=atol(argv[1]);
  • sprintf(limite,"%lu",nmax); // on doit y retrouver argv[1]
  • printf("\nCalcul des nombres premiers entre 1 et %lu\n", nmax);
  • crible=(char *)malloc((1+(nmax-1)/16)*sizeof(char));
  • if(strcmp(argv[1],limite)!=0 || crible==NULL) {
  • printf("\nLa limite de l'intervalle de calcul est trop grande\n");
  • pause();
  • return 1;
  • }
  • clock_t start = clock();
  • Eratosthene(nmax);
  • printf("\nTemps elapse du calcul : %f s\n", (double)(clock()-start)/CLOCKS_PER_SEC);
  • for(i=1; i<=nmax; i=i+1) if(prem(i)){np++; if(p1==0)p1=i; pn=i;} // calcul de np, p1 et pn
  • printf("\nNombre de nombres premiers calcul\202s : %lu\n", np);
  • printf("\nLe premier nombre premier : %lu\n",p1);
  • printf("\nLe %lu-i\212me nombre premier : %lu\n", np, pn);
  • free(crible);
  • pause();
  • return 0;
  • }
/*  Crible d'Eratosthène par pgl10  */

#include <stdio.h>   // pour printf
#include <stdlib.h>  // pour system
#include <string.h>  // pour memset
#include <time.h>    // pour clock
#include <math.h>    // pour sqrt

typedef unsigned long ulong;

#define ulmax 4294967295  // (2^32)-1 : valeur maximum des unsigned long 

ulong nmax;    // borne supérieure de l'intervalle [1,nmax] de calcul utilisé
char* crible;  // crible[1+(nmax-1)/16] : une suite de (1+nmax)/2 bits utiles
//   quand le calcul sera fini les nombres pairs ne seront pas marqués
//   et le (1+i)/2-ième bit de crible mis à 1 quand i est premier et 0 si non.
//   crible[0] pour les impairs de 1 à 15, crible[1] pour les impairs de 17 à 31, etc.

void pause() {
    printf("\n");
    system("pause");    
}
void comp(ulong i) { // mettre à 0 le (1+i)/2-ième bit du crible ( i impair et composé )
    ulong q, r;
    char c[9]={'\xFF', '\xFE', '\xFD', '\xFB', '\xF7', '\xEF', '\xDF', '\xBF', '\x7F'};
//  if(i%2==0) return;         // inutile : tous les nombres pairs ne sont pas marqués
    q=i>>4;                    // q=i/16=(x-1)/8 avec x=(1+i)/2 ou x=1+i/2 pour i impair
    r=((1+i)>>1)-(q<<3);       // r=(1+i)/2-8*q=x-8*q avec x=(1+i)/2  ( r de 1 à 8 )
    crible[q]=crible[q]&c[r];  // on met à 0 le bit (1+i)/2 du crible quand i est impair
}
bool prem(ulong i) { // retourner true si le nombre i est premier et false si non
    ulong q, r;
    if(i<2 || i>nmax) return false;
    if(i==2) return true;
    if(i%2==0) return false;
    q=i>>4;                    // q=0 si i de 1 à 15, q=1 si i de 17 à 31, etc.
    r=((1+i)>>1)-(q<<3);
    if(crible[q]>>(r-1)&1) return true;  // si le bit (1+i)/2 de crible est à 1
    return false;
}
void Eratosthene(ulong n) {       // crible d'Eratosthène
    ulong i, j, m, q, r, s, t;
    char c[9]={'\xFF', '\xFE', '\xFD', '\xFB', '\xF7', '\xEF', '\xDF', '\xBF', '\x7F'};
    memset(crible, '\xFF', 1+(n-1)/16);      // initialisation de crible[]
    comp(1);                                 // 1 n'est pas premier
    for(j=9; j<=n; j=j+6) {                  // les multiples de 2 ne sont pas marqués
        q=j>>4;                              // pour marquer les multiples impairs de 3
        r=((1+j)>>1)-(q<<3);                 // on évite d'appeler comp(j) pour gagner
        crible[q]=crible[q]&c[r];            // un peu de temps             
    }
    m=1+(ulong)sqrt((double)n);
    s=4;
    for(i=5; i<m; i=i+s) {                   // on marque les multiples des autres 
        if(prem(i)) {                        // nombres premiers connus en évitant
            if(((i+2)*i)%3) t=4; else t=2;   // les multiples de 2 et 3
            for(j=i*i; j<=n ; j=j+t*i ) { 
                q=j>>4;                      // ici j est toujours impair
                r=((1+j)>>1)-(q<<3);
                if(crible[q]>>(r-1)&1) crible[q]=crible[q]&c[r];
                t=6-t;
//              if(j>ulmax-t*i) {  // pour éviter un dépassement de capacité des ulong
//                  printf("\nLa limite de l'intervalle de calcul est trop grande\n"); 
//                  pause();       // pour gagner du temps on peut éviter ce contrôle, 
//                  exit(1);       // mais il faut veiller à utiliser nmax < 4294709603
//              }
            }
        }
        s=6-s;
    }
}
int main (int argc, char *argv[]) { 
    ulong i, np=0, p1=0, pn;
    char limite[21];
    if(argc!=2) {
        printf("\nPour calculer les nombres premiers de 1 \205 nmax\n");
        printf("\nlancez ce programme en faisant : crible nmax\n");
        pause();
        return 1;
    }
    nmax=atol(argv[1]);
    sprintf(limite,"%lu",nmax);      // on doit y retrouver argv[1]
    printf("\nCalcul des nombres premiers entre 1 et %lu\n", nmax);
    crible=(char *)malloc((1+(nmax-1)/16)*sizeof(char));
    if(strcmp(argv[1],limite)!=0 || crible==NULL) {
        printf("\nLa limite de l'intervalle de calcul est trop grande\n");
        pause();
        return 1;
    }
    clock_t start = clock();
    Eratosthene(nmax);
    printf("\nTemps elapse du calcul : %f s\n", (double)(clock()-start)/CLOCKS_PER_SEC);
    for(i=1; i<=nmax; i=i+1) if(prem(i)){np++; if(p1==0)p1=i; pn=i;} // calcul de np, p1 et pn
    printf("\nNombre de nombres premiers calcul\202s : %lu\n", np);
    printf("\nLe premier nombre premier : %lu\n",p1);
    printf("\nLe %lu-i\212me nombre premier : %lu\n", np, pn);
    free(crible);
    pause();
    return 0;
}

 Conclusion

Je suis preneur de tous les commentaires et améliorations pouvant permettre d'effectuer ce calcul plus rapidement, tout en conservant l'algorithme d'Eratosthène.

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Historique

26 juin 2011 17:57:07 :
Othographe

 Sources du même auteur

Source avec Zip Source avec une capture UNE LISTE HÉTÉROGÈNE DOUBLEMENT CHAINÉE
Source avec Zip Source avec une capture POUR AFFICHER LES CARACTÈRES ACCENTUÉS SOUS WINDOWS EN MODE ...
Source avec Zip Source avec une capture CONVHTML : UN UTILITAIRE DE CONVERSION POUR FICHIERS HTML
Source avec Zip Source avec une capture AFFIMOFF : UNE VISIONNEUSE 3D AVEC PARAMÉTRISATION ET TEXTUR...
Source avec Zip Source avec une capture UN INTERPRÉTEUR POUR RATIONNELS DE GRANDES TAILLES

 Sources de la même categorie

Source avec Zip UN EXAMPLE D'APPLICATION EN CUDA DE L'ALGORITHME DE SCAN POU... par oguzaras
Source avec Zip Source avec une capture CHIFFREMENT DE VIGENERE par lajouad
Source avec Zip Source avec une capture ANALYSE SYNTAXIQUE par lajouad
Source avec Zip Source avec une capture STRUCTURE D'UNE MATRICE PAR LES LISTE LINÉAIRE (NON CONTUGUS... par benzarabel
Source avec Zip Source avec une capture DESSINER UNE ARBRE BINAIRE( MODE CONSOLE): par benzarabel

 Sources en rapport avec celle ci

Source avec Zip Source avec une capture EULER AURAIT 303 ANS par pgl10
Source avec Zip Source avec une capture FORMULES POUR NOMBRES PREMIERS par pgl10
Source avec Zip Source avec une capture OPTIMISATION DE CALCULS (WIN64) par BruNews
Source avec Zip Source avec une capture ALGORITHMES D'OPTIMISATION NON LINÉAIRE: DESCENTE DE GRADIEN... par Pistol_Pete
Source avec Zip ALGORITHME GÉNÉTIQUE RCPSP par pclover

Commentaires et avis

Commentaire de pgl10 le 27/06/2011 14:21:57

Il y a un moyen simple de faire le calcul plus rapidement. Dans le programme ici présenté on met au départ un indicateur à 1 pour tous les nombres entiers impairs et ensuite on marque à 0 tous les nombres composés impairs. Que se passerait-il si on ne marquait pas les multiples de 3 ? Le nombre 45, par exemple, ne serait plus marqué en tant que composé comme multiple de 3 mais il le serait en tant que multiple de 5. Au final seuls les nombres en 3^n resteraient avec un indicateur à 1 Mais si on en tient compte au début de prem() en ajoutant : if(i==3) return true; if(i%3==0) return false; après les 2 lignes semblables pour 2 on rétablit un calcul correct. Il y a seulement un ralentissement faible de prem() mais on évite le marquage de nombreux entiers, ce qui gagne du temps. Ceci permet de passer de 43.071 sec à 41.161 sec pour 100000000.bat et de 95.050 sec à 90.765 sec pour maximum.bat Et puis on peut continuer. Si on évite de marquer les multiples de 3, 5, 7, 11 et 13 en commençant à 17 on obtient 36.247 sec pour 100000000.bat et 80.823 sec pour maximum.bat Alors, voilà ! Est-ce que l'on peut encore parler d'algorithme d'Eratosthène ? Est-ce acceptable ? C'est à chacun de décider si on peut faire cette modification.

Commentaire de pgl10 le 27/06/2011 21:23:21

Il y a bien un autre gain de temps facile. Dans la boucle : for(i=5; i<m; i=i+s) on peut remplacer le : if(prem(i)) par : q=i>>4; r=... if(...) comme dans prem(i). Cela évite l'appel à la fonction prem() et les 3 if du début de prem() qui sont inutiles à cet endroit. Mais le gain est petit et presque insignifiant, parce que la boucle externe en i est moins fréquente que la boucle interne en j.

Commentaire de Rescassol le 04/07/2011 07:07:26

Voilà qui me semble plus rapide (en C++), avec une table qui s'auto-remplit:

const long NMAX = 1000000L;

vector <long> prem, sigma;

bool premier(long n)
{
for (long k = 0; prem[k] * prem[k] <= n; k++) if (n % prem[k] == 0) return false;

return true;
}


void FaireTable(void)
{
prem.push_back(2);
for (long n = 2; n < NMAX; n++)
   if (premier(n))
      prem.push_back(n);
}

Commentaire de Rescassol le 04/07/2011 07:17:04

Rectification (si on pouvait modifier son propre message, ce serait bien) :

const long NMAX = 1000000L;

vector <long> prem;


bool premier(long n)
{
for (long k = 0; prem[k] * prem[k] <= n; ++k) if (n % prem[k] == 0) return false;

return true;
}


void FaireTable(void)
{
prem.push_back(2);
for (long n = 3; n < NMAX; n += 2)
    if (premier(n))
       prem.push_back(n);
}

Commentaire de LeFauve42 le 04/07/2011 12:56:29

La seule optimisation vraiment efficace du cribble consiste a sauter les premieres iterations et a initialiser le tableau de maniere efficace.
Pourquoi ne pas deplier la boucle jusqu'a ce que les valeurs s'alignent sur un multiple de 8, et remplir tout ca avec des _int64 ?
La derniere fois que j'ai joue avec ca (sur un 68030 a 25 MHz :o) ), 7 etait le nombre magique (au dela duquel on ne gagnait plus grand chose a precalculer le tableau de crible).
Il doit aussi y avoir des modes d'adressage en assembleur permettant d'accelerer sans commune mesure le traitement par rapport a ce qui peut se faire en C (par exemple, sur les 68030, on peut recuperer en une instructuion machine le premier bit a 0 en memoire a partir d'une adresse donnee. Je serais etonne que les intels modernes n'aient pas cette possibilite)

Commentaire de pgl10 le 14/07/2011 19:58:46

Remerciements tardifs à Rescassol et LeFauve42 ( J'étais en déplacement sans accès à Internet ) Avec une variante utlilisant les __int64 j'ai pu effectivement calculer les 1300005926 nombres premiers entre 1 et 30 milliards. Au-delà il faut une autre gestion de la mémoire pour les indicateurs : nombre premier ou nombre composé.

Commentaire de pgl10 le 18/07/2011 09:11:02

Cette variante utlilisant les __int64 est en ligne sur mon site web : http://pgl10.chez.com avec source, exécutable, exemples et documentation.

Commentaire de Saros le 14/11/2011 01:32:11

Ça peut être intéressant de savoir jusqu'à quand il faut déplier la boucle en fonction de la borne !
LeFauve42 le chiffre de 7 que tu as avancé c'était pour calculer les nombres premiers jusqu'à combien ?

Commentaire de LeFauve42 le 14/11/2011 10:52:26

Saros, tout ca remonte à plus de 20 ans, alors mes souvenirs sont peut-etre un peu rouillés :o)

Le truc était qu'avec la méthode utilisée (le 68030 avait 2 instructions bien pratiques pour le cribble qui en gros pouvaient :
- Fixer le bit "n" à partir d'une adresse (n étant sur 32 bits, pas juste <8 (ou a 64))
- Trouver le premier bit à un (ou a zero, je ne me rappelle plus très bien) à partir d'une adresse donnée) ce qui était lent c'était les premières itérations.

Les 7 premières en particulier (à moins que ce ne soit jusqu'à celle du chiffre 7).

Donc, on les précalcule, et on initialise le tableau avec cette valeur.
Pour aller encore plus vite, au lieu de remplir la partie minimale de la boucle à chaque iteration, j'avais pris plusieurs fois cette partie de n octets jusqu'à obtenir un multiple de 4 afin d'utiliser uniquement des MOVE sur 32 bits, bien plus rapides que les MOVE sur 8 bits.

Ici il faudra aligner sur un multiple de 8 pour faire un move sur 64 bits.

Je ne suis pas sur que ce soit très clair, mais si tu essaies ca devrait etre plus facile à comprendre :o)

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Optimisation... :) [ par CodeNeo ] Hello tout le monde !!Question ! Est ce que quelqu'un saurait comment raccourcir le code suivant ?bool __fastcall TForm1::InitBouton ( void ) { Spe [C++] Optimisation de pile [ par guiguikun ] optimisation [ par ifren ] Comment est ce que je peux optimiser le temps d'execution de mon application, quel sont les differents type de pistesmerci soyant heureux faisant fort optimisation affichage opengl [ par xian240482 ] J'ai reussis a affiché un fichier .ASE , mais c'est lent! En cherchant un peu partout, j'ai trouvé plusieur voie :Les gll optimisation de code [ par yakalelo ] Salut J' aimerais optimiser un programme en langage C pour le faire tourner plus vite. Il est constitue principalement de conditions if-else. est ce optimisation d un code asm [ par sajid_morad ] salut tout le monde j aimerai bien savoir comment optimiser un code en assembleur ( le temps d execution des instruction le plus optimal ), et esqu il systeme non lineaire+optimisation d'une fonction non lineaire [ par correcte ] Bonjour,Je cherche un programme ecrit en c++ qui permet de resoudre un systeme d'equations non lineaire.Je cherche egalement un programme qui fait le optimisation [ par arf63 ] Salut j aimerai savoir s il y a moyen d optimiser ca avec un switch case je le maitrise moyenement si quelqu un pourait m aider if (iMat[iPosy][iPosx] pb d'optimisation [ par pipow1 ] Bonjour &#224; tous Je recherche la m&#233;thode la plus rapide pour copier un tableau 3D dans un tableau 1D, en &#233;vitant bien sur de passer par u optimisation de la memoire en c++ [ par ebooserge ] salut a tous,voila j'ai une question un peu bete mais je me lance quand meme.lorsqu'on declare une variable a l'interieur d'une fonction qui appartien


Nos sponsors


Sondage...

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,593 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales