Ce programme en C17 utilise la division d’essai pour tester les nombres et énumérer les nombres premiers de 1 à une borne supérieure n, incluse. Un nombre premier est un entier supérieur ou égal à 2 : une borne de 0 ou de 1 ne produit donc aucun nombre premier. Saisissez un seul entier décimal compris entre 0 et INT_MAX.
Code
| |
Compilation et exemple d’exécution
Enregistrez le programme dans primes.c. Dans un environnement disposant de GCC, exécutez gcc -std=c17 -Wall -Wextra -O2 primes.c -o primes, puis ./primes. La saisie de 30 affiche un en-tête suivi de cette ligne :
| |
La borne 2 produit 2 ; les bornes 0 et 1 produisent une ligne vide. L’entrée est une ligne de texte limitée à 127 octets, sans compter le saut de ligne. Les caractères d’espacement en début et en fin de ligne, ainsi qu’un + initial, sont autorisés. Les valeurs négatives, les lignes vides, les textes qui ne représentent pas un entier, les valeurs hors plage, les lignes trop longues et les échecs de lecture provoquent une erreur et l’arrêt du programme. Seule la première ligne est traitée ; les suivantes ne sont pas utilisées.
Fonctionnement du test et de la lecture
isPrimerenvoie faux pour les valeurs inférieures ou égales à 1, et vrai pour 2 et 3. Pour les autres multiples de 2 ou de 3, elle renvoie faux. Elle teste ensuiteieti + 2pouri = 5, 11, 17, .... Les diviseurs candidats sont de la forme6k - 1ou6k + 1: ce ne sont pas des multiples de 6, et tous les nombres de ces formes ne sont pas premiers.- Tout entier composé possède un diviseur premier inférieur ou égal à sa racine carrée. Il suffit donc de poursuivre tant que
i <= num / i. Pour les entiers positifs, cette condition couvre le même intervalle quei * i <= num, tout en évitant le débordement de la multiplication enint. Aucun calcul de racine carrée en virgule flottante n’est nécessaire. printPrimesn’affiche qu’une ligne vide si la borne est inférieure à 2. Sinon, elle affiche 2, puis teste les candidats impairs. Sii > n - 2, elle quitte la boucle avant l’addition, ce qui évite un débordement lors du dernieri += 2, même avec une borne égale àINT_MAX.readLimitlit avecfgets, vérifie le pointeur de fin de conversion etERANGEdestrtol, puis contrôle que la valeur est comprise entre 0 etINT_MAXavant de la convertir enint. L’argument deisspaceest converti enunsigned char. Ignorer la valeur de retour descanf("%d", &n)risque de conduire à l’utilisation d’unnnon initialisé après un échec de lecture. On ne peut pas non plus supposer qu’un résultat hors de la plage deintsera traité comme un simple échec de conversion.
Domaine d’application
INT_MAX dépend de l’implémentation. Pour un int de 32 bits, sa valeur est 2,147,483,647, mais C n’impose pas cette valeur partout. Pouvoir représenter une borne ne signifie pas que l’énumération de tous les nombres premiers jusqu’à cette borne sera rapide.
Cette implémentation sert à apprendre la division d’essai. Le test d’un nombre effectue au plus un nombre de divisions proportionnel à sa racine carrée ; pour énumérer tous les candidats, une borne supérieure grossière est O(n sqrt(n)) divisions. Une division compte ici pour une opération : ce n’est ni une complexité en bits ni un temps mesuré. Pour énumérer les nombres premiers sur un grand intervalle, envisagez le crible d’Ératosthène. Ce programme n’est pas un générateur de nombres premiers cryptographiques. Pour tester de grands entiers, consultez l’article sur Miller–Rabin.
Les règles des fonctions d’entrée et de l’arithmétique entière de C figurent aux sections 6.5, 7.4, 7.21.6.2, 7.21.7.2 et 7.22.1.4 du projet de comité C11 N1570 du WG14.
