C / C++ / C++.NET : Le crible d'Eratosthène classique (2024)

C / C++ / C++.NET : Le crible d'Eratosthène classique (1)

Télécharger le projet

Commenter

Description

La probabilité de découvrir aujourd'hui une amélioration significative de l'algorithme classique du Crible d'Eratosthène est très mince car rares sont les programmeurs ou développeurs qui ne se sont jamais intéressés de près ou de loin à ce procédé.

Malgré tout, je me décide de présenter ici un développement propre (inédit ?) dont les derniers codes divisent les temps de calcul carrément par 3 ou plus !


Le crible d'Ératosthène classique

Par classique, on entend (souvent) un code ressemblant à EratoA ci-dessous, et qui comporte les particularités suivantes:
a) Le crible est complet, sans suppression préliminaire (p.ex. nombres pairs).
b) L'élimination des candidats s'arrête lorsque p*p<=mx.
c) L'élimination des candidats se limite au multiples de nombres premiers if (isPrime[p]) ...

bool *EratoA(uint mx) { bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=p+p; q<=mx; q+=p) isPrime[q]=false; return isPrime;}

Une amélioration, qui devrait être "classique" aussi, mais souvent oubliée, est de commencer l'élimination avec le carré du nombre premier considéré: for (uint q=p*p; ....
Il est vrai que le gain obtenu n'est que de quelques pourcents:

bool *EratoB(uint mx) { bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=p*p; q<=mx; q+=p) isPrime[q]=false; return isPrime;}

Ce code très sobre est par exemple proposé dans primesieve pour déterminer les "petit*" nombres premiers, qui sont ensuite utilisés pour déterminer le crible segmenté.
Malgré la simplicité ces quelques lignes, on rencontre souvent des codes qui remplissent des pages !


Essais personnels

Selon mon "intuition", le prochain code EratoC, qui, sous une forme "équivalente" à EratoB, utilise la multiplication p*q, devrait être moins performant.
Mais les mesures montrent qu'ils sont tout à fait comparables.

bool *EratoC(uint mx) { bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=p; q<=mx/p; ++q) isPrime[p*q]=false; return isPrime;}

En travaillant dans d'autres articles sur les nombres premiers (cycles et multiplications), il m'est venu l'idée de visualiser les multiples de p pour lesquels l'élimination isPrime[p*q]=false est vraiment nécessaire.
Par exemple, pour p=5, j'ai obtenu p*q=25,35,55,65,85,etc. En regardant de plus près, on peut constater que p*q=5*5,5*7,5*11,5*13,5*17,... , donc les produits de p=5 par les nombres premiers >= 5.
Cela ne nous avance pas à grand chose, car ces nombres premiers, on est justement entrain de les chercher !
Mais pour éviter d'éliminer tous les multiples de p à partir de p*p, on peut se limiter aux produits p*q pour lesquels q n'est pas un multiple d'un nombre premier inférieur à p, c'est-à-dire pour ceux qui restent candidats (isPrime[q]=true).
De cette manière, on s'approche de l'idée du Crible d'Euler qui n'élimine un candidat qu'une seule fois.

bool *EratoFaux(uint mx) { // Faux bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=p; q<=mx/p; ++q) if (isPrime[q]) isPrime[p*q]=false; // Faux return isPrime; // Faux}

Malheureusem*nt, ce code donne des résultats faux !!!. En effet, par exemple pour p=2, après avoir éliminé 4, on n'éliminera plus 8=2*4 !


Amélioration personnelle (inédite ?)

Après avoir laissé "dormir" ce projet pendant quelques temps, je me suis demandé si l'erreur ci-dessus ne provenait pas simplement de l'ordre dans lequel les candidats sont éliminés.
Et, en effet, en inversant la boucle intérieure, la méthode devient correcte ("Ouf !!!"):

bool *EratoD(uint mx) { bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=mx/p; q>=p; --q) if (isPrime[q]) isPrime[p*q]=false; return isPrime;}

Par rapport à la version classique EratoB, je m'attendais pour EratoD à une diminution du temps d'exécution de quelques %, ou peut-être même de quelques dizaines de %.
Je suis donc extrêmement surpris que ce gain est nettement plus élevé (voir Mesures à la fin de ce texte).

En résumé, on obtient un algorithme 3 à 4 fois plus rapide en remplaçant la boucle intérieure:
for (uint q=p*p; q<=mx; q+=p) isPrime[q]=false;
par:
for (uint q=mx/p; q>=p; --q) if (isPrime[q]) isPrime[p*q]=false;

La tentative d'éviter la multiplication dans la boucle intérieure n'améliore pas (ou peu) le temps de calcul.
La soustraction pq-=p doit en effet être faite pour tous les multiples de p, alors que le produit p*q, incontestablement plus lourd, n'est effectué que pour les candidats q (multiples de p) non éliminés.

bool *EratoE(uint mx) { bool *isPrime=(bool*)malloc(mx+1); memset(isPrime,true,mx+1); isPrime[0]=false; isPrime[1]=false; for (uint p=2; p*p<=mx; ++p) if (isPrime[p]) for (uint q=mx/p,pq=p*q; q>=p; --q,pq-=p) if (isPrime[q]) isPrime[pq]=false; return isPrime;}

Suppression préliminaire des nombres pairs

Pour essayer de rester "simple", je me suis limité jusqu'ici au crible d'Ératosthène classique (complet).
Pour diviser par deux la mémoire utilisée, ne considérons maintenant que les nombres pairs à l'aide de la relation suivante entre l'index i du crible et le nombre correspondant p:

// correspondance: i=p/2; p=2 si i=0 et p=2*i+1 si i>0;// i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... // p: 2 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 ... bool *EratoOdd(uint mx) { bool *isPrime=(bool*)malloc((mx+1)/2); memset(isPrime,true,(mx+1)/2); for (uint i=1,p=3; p*p<=mx; ++i,p+=2) if (isPrime[i]) { uint a=mx/p; // a must be odd ! for (uint j=((a&1)?a:a-1)/2; j>=i; --j) if (isPrime[j]) isPrime[i+p*j]=false; } return isPrime;}

Le temps de calcul par rapport à EratoD est divisé par environ 1.65 !


Conclusion

Maintenant que j'ai le nouveau code sous les yeux (EratoD), l'amélioration apportée me semble tellement "simple" et "évidente", que j'ai de la peine de croire qu'elle n'ait pas été "découverte" et publiée jusqu'à ce jour.
Merci d'avance de me faire parvenir les coordonnées de développements semblables ou complémentaires.

Une prochaine étape sera d'optimiser la mémoire en n'utilisant qu'un bit par élément du crible, ainsi que d'éliminer d'avance non seulement les nombres pairs, mais aussi les multiples de 3, 5, 7, ... .
Il me reste aussi à adapter mes articles actuels et futurs concernant les nombres premiers, dont une liste, complétée de mesures des temps d'exécution, peut être obtenue ici: Nombres premiers.


Mesures

Les fichiers sieve.cpp et EratoOdd.cpp vous permettent d'essayer EratoA à Erato 4 et EratoOdd,
dont voici ce que me donne mon ordi (i5 CPU 650 3.2 GHz):

Output de Erato.cppEratoA: (classique)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=94 msm=100000000, n=5761455, t=1326 msm=1000000000, n=50847534, t=16411 msEratoB: (classique +)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=94 msm=100000000, n=5761455, t=1248 msm=1000000000, n=50847534, t=15085 msEratoC: (classique avec multiplication)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=78 msm=100000000, n=5761455, t=1232 msm=1000000000, n=50847534, t=15071 msEratoD: (personnel avec multiplication)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=31 msm=100000000, n=5761455, t=421 msm=1000000000, n=50847534, t=4680 msEratoE: (personnel sans multiplication)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=31 msm=100000000, n=5761455, t=437 msm=1000000000, n=50847534, t=4627 msOutput de EratoOdd.cppEratoOdd: (Sans nombres pairs)m=10, n=4, t=0 msm=100, n=25, t=0 msm=1000, n=168, t=0 msm=10000, n=1229, t=0 msm=100000, n=9592, t=0 msm=1000000, n=78498, t=0 msm=10000000, n=664579, t=15 msm=100000000, n=5761455, t=265 msm=1000000000, n=50847534, t=2808 ms

Télécharger le projet

Codes Sources

  • Racine
  • Zip
  • ..
  • voir Télécharger

    Erato.cpp
  • voir Télécharger

    EratoOdd.cpp

A voir également

  • Le crible d'Eratosthène classique
  • Quel est le système le plus classique de numérotation des versions ? - Conseils pratiques -Delphi
  • Belote classique - Forum PHP
  • Crible d'eratosthene - Forum C
  • Numérotation - Forum Delphi / Pascal
  • Crible quadratique - Forum Visual Basic

S'inscrire maintenant

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.

C / C++ / C++.NET  : Le crible d'Eratosthène classique (2024)

FAQs

Quel est le but du crible d'Ératosthène ? ›

Méthode permettant de déterminer une liste des nombres premiers. Elle consiste à écrire la suite des nombres entiers 1, 2, 3, 4, 5… et à effacer tous les multiples de 2 autres que 2, puis tous les multiples de 3 autres que 3, puis tous les multiples de 5, etc.

Qui a inventé le crible d'Ératosthène ? ›

Mathématiques. Article connexe : Crible d'Ératosthène. Mathématicien, Ératosthène a établi le crible d'Ératosthène, méthode qui permet de déterminer (par exclusion des nombres composés) tous les nombres premiers jusqu'à une borne donnée à l'avance.

Quelle est la méthode d'Ératosthène ? ›

Essentiellement, pour mesurer la circonférence terrestre, Ératosthène a utilisé deux choses banales à son époque : un bâton planté verticalement à Alexandrie et dont l'angle avec les rayons solaires est égal à l'angle au centre de la terre entre Alexandrie et Syène, comme le modèle géométrique le montre.

Quelles sont les hypothèses du modèle d'Ératosthène ? ›

Cela revient à supposer que le Soleil est situé suffisament loin, ce qui en l'espèce est une approximation tout à fait raisonnable. Enfin la dernière hypothèse faite par Eratosthène, c'est que Syène et Alexandrie sont situées sur le même méridien.

Comment s'appelle Syène aujourd'hui ? ›

À Syène – aujourd'hui Assouan, dans la haute vallée du Nil en Égypte – le jour du solstice d'été à midi, les rayons du Soleil éclairent le fond des puits.

Comment Ératosthène est mort ? ›

Et il invente un procédé mathématique pour trouver rapidement des nombres premiers (le crible d'Ératosthène). Ératosthène est mort en 194 avant J. -C., il s'est laissé mourir de faim car il était aveugle. Le nom d'Ératosthène a été donné à un astéroïde.

Quel est le chiffre parfait ? ›

Plus formellement, un nombre parfait n est un entier tel que σ(n) = 2n où σ(n) est la somme des diviseurs positifs de n. Ainsi 6 est un nombre parfait car ses diviseurs entiers sont 1, 2, 3 et 6, et il vérifie bien 2 × 6 = 12 = 1 + 2 + 3 + 6, ou encore 6 = 1 + 2 + 3.

Pourquoi Ératosthène considère que les rayons du soleil sont parallèles entre eux ? ›

Les rayons du Soleil sont supposés parallèles en raison de l'éloignement de l'astre (cette hypothèse est majeure, puisqu'un Soleil de petite taille et peu éloigné d'une Terre plate produirait aussi des rayons inclinés à Alexandrie : le raisonnement d'Ératosthène est donc "chargé de théorie", celle d'une Terre sphérique ...

Comment Ératosthène est devenu aveugle ? ›

Selon la légende, il serait devenu aveugle en vieillissant. Ne pouvant plus étudier les étoiles, il se serait laissé mourir de faim et de fatigue. Ératosthène.

Comment calculer le rayon de la terre avec la méthode d'Ératosthène ? ›

Calcul du rayon : Utilisez la formule du cercle [ C = 2\pi r ]pour calculer le rayon r de la Terre, où C est la circonférence estimée. Conversion en kilomètres : Le rayon obtenu sera en unités proportionnelles à la distance mesurée.

Quel est le gnomon d'Ératosthène ? ›

Ératosthène considérait comme parallèles les rayons lumineux du Soleil en tout point de la terre. En comparant l'ombre et la hauteur du gnomon, Ératosthène déduisit que l'angle entre les rayons solaires et la verticale était de 1/50 d'angle plein, soit 7,2 degrés (360°/50).

Top Articles
Latest Posts
Article information

Author: The Hon. Margery Christiansen

Last Updated:

Views: 6199

Rating: 5 / 5 (70 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: The Hon. Margery Christiansen

Birthday: 2000-07-07

Address: 5050 Breitenberg Knoll, New Robert, MI 45409

Phone: +2556892639372

Job: Investor Mining Engineer

Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding

Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.