Nombres premiers en C

Fermé
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 - Modifié par Pierrot-du-18 le 27/06/2013 à 17:50
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 - 8 juil. 2013 à 12:24
Bonjour,

J'ai décidé de créer un algorithme très subtile, bien sur, qui teste tous les nombres de 1 à 100000 et qui me les écrit dans un fichier texte.

Voici le principe de l'algorithme :

Il essaye pour le premier nombre : 2.
Il voit qu'il est premier, donc, il l'écrit dans un fichier texte.
De plus, il l'inscrit en position 0 dans un tableau de valeur.

Ensuite, il essaye pour trois : il fait le modulo de trois par tous les nombres contenus dans le tableau. Si tous les modulos sont différents de 0, alors, on inscrit trois en place 1 dans le tableau, et on l'inscrit dans un fichier texte.

Et ainsi de suite : en fait, soit x le nombre testé, il fait le modulo de x par tous les nombres premiers qu'il aura trouvés avant (et qui seront bien sur stockés dans le tableau).

Ah oui, autre chose, soit y dans {var = x % y}, il doit arrêter le processus pour le nombre en cours si le nombre y est supérieur à la racine carrée de x (pour éviter les opérations inutiles).

Voilà voilà, j'ai essayé plusieurs fois, mais cela n'a jamais marché... :-(

Voici mon dernier code source :
#include <stdio.h>
#include <stdlib.h>
#include <math.h>



int main ()
{
    int i, a, reste, tableau[100000];
 tableau[0] = 2;
 for (a = 3 ; a < 100001 ; a++)
 {
  int counter = 0;
  do
  {
   counter++;
  }
  while (tableau[counter] != 0);
  for (i = counter ; i < sqrt(a) ; i++)
               {
   int iPlusUn = i + 1;
   int var = tableau[i];
   reste = a % var;
   int nombreMoinsUn = a - 1;
   if (reste != 0 && i == nombreMoinsUn)
   {
    tableau[iPlusUn] = a;

    FILE* fichier = NULL;
    fichier = fopen("nombrespremiers.txt", "a");
    if (fichier != NULL)
                               {
                              fprintf(fichier, "%d\n", a);
                              fclose(fichier);
                               }
         }
               }
 }   
}



Si vous avez une idée, n'hésitez surtout pas!
Merci d'avance! :D

5 réponses

fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
27 juin 2013 à 23:38
Bonjour,

Merci d'éviter les double post !
Je réponds sur celui-là car il doit être plus à jour.

En fait, tu ne crées pas un algorithme puisqu'il existe déjà : crible d'Eratosthène ;-).

Pour plus de clarté, tu devrais créer une fonction "estPremier() qui te retourne 1 s'il est premier, 0 sinon.

De même, une fonction écriture(). D'ailleurs, plutôt qu'ouvrir le fichier à chaque fois. Tu devrais plutôt calculer tous les premiers. Et plus tard écrire le tableau dans ton fichier. Sinon, ton tableau n'a aucun intérêt...

Je te conseille également de revoir ton indentation.
Ta variable var ne sert pas à grand chose et nuit à la lisibilité. Mets simplement : reste=a%tableau[i].

C'est quoi ce nombreMoinsUn ? J'en vois pas l'intérêt... Pareil pour iPlus1. Autant mettre i+1 directement dans les variables si tu en as besoin.

Pourquoi reparcourir tout le tableau en calculant counter ?

while (tableau[counter] != 0);
int var = tableau[i];
reste = a % var;

Pour sortir de la boucle while, on a tableau[counteur]=0. Pour la première itération : var=tableau[i] (i=counter). Donc var vaut 0. Donc tu calcules à modulo 0 ;-).

Je te conseille déjà de tout revoir, de corriger et de reposter.
Bon courage.
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
28 juin 2013 à 12:29
Merci de ta réponse.
Avant de faire ce que tu m'as dit, je vais d'abord le reposter avec des commentaires, car cela pourra déjà répondre à certaines de tes questions :)

Sinon, je suis un peu dégouté car je ne connaissait vraiment pas cet Eratosthène...
J'aurais pu être l'inventeur de cet algorithme! (enfin non, car j'imagine que quelqu'un d'autre l'aurait trouvé avant moi ^^).

Bon, passons au sérieux :

#include <stdio.h>
#include <stdlib.h>
#include <math.h> // Je l'inclue pour le sqrt.



int main ()
{
 int i, a, reste, tableau[100000]; // J'initialise.
 tableau[0] = 2; */  Je stocke "2" dans la première ligne du tableau
 (c'est le premier nombre premier). /*
 for (a = 3 ; a < 100001 ; a++) // Je ne commence pas avec 2, car il est déjà dans le tableau

*/ Je compte le nombre de lignes pleines, car si il essaye avec trop de lignes, il va finir par diviser par 0 : comme ça, soit n le nombre de lignes pleines, je fait n modulos. /*
 {
  int counter = 0;
  do
  {
   counter++;
  }
  while (tableau[counter] != 0); // la variable counter sera donc égale au nombre de lignes non vides.

  for (i = counter ; i < sqrt(a) ; i++) // i = counter pour ne pas faire trop de modulos, je m'arrête à la racine carrée du nombre testé pour éviter les opérations inutiles.
               {
   int iPlusUn = i + 1; // Je vais supprimer, j'ai confondu avec counter + 1
   int var = tableau[i]; // Sinon code::blocks se bloque.
   reste = a % var; // Je fais le modulo que j'enregistre dans reste
   int nombreMoinsUn = a - 1; // Oui, car il ne faut pas que le programme fasse le modulo de x par x, car il sera forcement nul.
   if (reste != 0 && i == nombreMoinsUn)
   {
    tableau[iPlusUn] = a; // Je vais remplacer iPlusun par counterPlusUn, car il faut qu'il enregistre le nombre premier dans la ligne suivante du tableau, pas dans la ligne courante.

    FILE* fichier = NULL;
    fichier = fopen("nombrespremiers.txt", "a");
    if (fichier != NULL)
                               {
                              fprintf(fichier, "%d\n", a);  /* Si, je l'inscrit, mais c'est temporaire, c'est juste pour voir si le programme marche sans qu'il se finisse complétement, car même en C, l'algorithme prend un certain temps (je l'ai fait en BATCH, je te file le code source si tu veux). */
                              fclose(fichier);
                               }
                       }
               }
       }   
}
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
28 juin 2013 à 13:07
quelqu'un d'autre l'aurait trouvé avant moi
Beh oui, Eratosthène a trouvé avant :D.

Sinon pour info, tu peux encore améliorer le crible en utilisant l'optimisation d'Atkin.

c'est juste pour voir si le programme marche sans qu'il se finisse complétement,
Ok, mais pourquoi refermer le fichier à chaque fois. Tu l'ouvres une bonne fois puis tu le fermes en fin de programme. Ou sinon, tu affiches à l'écran et tu rediriges vers stdout vers un fichier.

// la variable counter sera donc égale au nombre de lignes non vides. Oui, j'avais compris le but. Mais, c'est inutile. Lorsque t'étais un nombre dans le tableau, tu mets à jour le compteur et basta. Tu te passes ainsi de la boucle while.

int var = tableau[i]; // Sinon code::blocks se bloque.
Aucune raison qu'il se bloque. S'il se bloque c'est qu'il y a une erreur ailleurs.

Je te laisse corriger le tout alors :-). Et n'oublie pas de mettre un return 0; à la fin pour dire que tout s'est bien déroulé.
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
28 juin 2013 à 13:40
Merci de ton aide, je vais reprendre tout ça et je le reposterai quand je l'aurai fini (ou amélioré ^^)
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
2 juil. 2013 à 14:57
Voilà ce que j'ai fait (j'ai tout recommencé en suivant tes conseils), ça ne marche toujours pas....

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int TestPremier (long double nombreTeste)
{
    long double reste, a, deux;
    deux = 2;
    reste =0;
    reste = fmodl(nombreTeste, deux);
    if (reste == 0)
    {
        return 0;
    }
    for (a = 3 ; a <= sqrt(nombreTeste) ; a += 2)
    {
        reste = fmodl(nombreTeste, a);
        if (reste == 0)
        {
            return 0;
        }
    }
    return 1;
}

int main()
{
    int nombrePremier;
    long double nombreMax, i;
    printf("Ce programme va vous calculer et vous %cnum%crer tous les nombres \npremiers de 2 %c N.\n", 130, 130, 133);
    printf("\nVeuillez choisir une valeur pour N : ");
    scanf("%Lg", &nombreMax);
    printf("\nVotre valeur N est %cgale %c %Lg\n",130, 133, nombreMax);
    getchar();
    FILE* fichier = NULL;
    fichier = fopen("nombres_premiers_trouves.txt", "a");
    fprintf(fichier, "2\n");
    for (i = 3 ; i <= nombreMax ; i += 2)
    {
        nombrePremier = TestPremier(i);
        if (nombrePremier)
        {
            fprintf(fichier, "%Lg\n", i);
        }
    }
    fclose(fichier);
    return 0;
}
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié par [Dal] le 3/07/2013 à 14:51
Salut Pierrot,

tu disais :

Voici le principe de l'algorithme :

Il essaye pour le premier nombre : 2.
Il voit qu'il est premier, donc, il l'écrit dans un fichier texte.
De plus, il l'inscrit en position 0 dans un tableau de valeur.

Ensuite, il essaye pour trois : il fait le modulo de trois par tous les nombres contenus dans le tableau. Si tous les modulos sont différents de 0, alors, on inscrit trois en place 1 dans le tableau, et on l'inscrit dans un fichier texte.

Et ainsi de suite : en fait, soit x le nombre testé, il fait le modulo de x par tous les nombres premiers qu'il aura trouvés avant (et qui seront bien sur stockés dans le tableau).


Tu dis que ton idée est de créer un tableau. Je ne vois nulle part dans ton code que tu crées un tel tableau.

Pour implémenter "ton idée", il faut donc que tu crées un tableau d'entiers pouvant accueillir les nombres premiers au fur et à mesure que tu les découvres.


Dal
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
3 juil. 2013 à 15:00
Ah, oui, excuse moi, j'ai oublié de préciser que je voulais savoir coder cet algorithme avant de passer à un plus compliqué. Pourrais tu me dire ce qu'il ne va pas dans ce codesource?
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié par [Dal] le 3/07/2013 à 15:28
Je ne parle pas d'un autre algorithme, je parle de celui que tu indiques vouloir implémenter, et j'ai cité exactement ce que tu as écris et simplement constaté que tu ne stockes rien dans un tableau.

Ce qui ne va pas, c'est que tu ne crées pas un tableau, et donc tu ne mets rien dedans, et donc tu ne compares rien, et tu ne peux donc pas appliquer l'algorithme dont tu décris le principe.

1.

Tu dois créer un tableau d'entiers avec une capacité suffisante pour mettre tous les nombres premiers de 2 à N. Au pire, tu alloues une taille de N pour être tranquille.

Bien sûr il n'y aura pas N nombres premiers et ce n'est pas optimisé.

2.

Alors, pour déterminer la taille pertinente du tableau à allouer, tu pourrais aussi utiliser certains théorèmes mathématiques. On peut lire sur ce site (premier résultat Google, vérifier si c'est fiable) :

http://villemin.gerard.free.fr/Wwwgvmm/Premier/densite.htm

que :

"La quantité de premiers inférieurs à n est environ n / ln (n)."

Ne me demande pas de démontrer le théorème :-)

Sauf erreur, il parle de logarithme népérien, et la fonction C pour cela c'est log.

Mais je ne suis pas vraiment matheux, alors je t'invite à vérifier tout cela... je suppose que tu es plus au point que moi sur ces questions... pour moi, ces amusements remontent à un bail :-)

Il y a aussi çà : https://fr.wikipedia.org/wiki/Th%C3%A9or%C3%A8me_des_nombres_premiers où on parle d'autres méthodes de calcul


Le 2., c'est du luxe (et si tu le fais, vérifie que tu ne débordes pas sa capacité). Fais déjà le 1., et passe le tableau en paramètre à ta fonction TestPremier en plus du nombre testé.


Dal

Edit : ajout lien Wikipedia
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
3 juil. 2013 à 15:31
Merci, c'est très gentil de ta part, mais il y a du avoir un mal entendu :-)

En fait, le code que j'ai posté ne correspond pas à ma question de départ, mais à une sorte d'algorithme intermédiaire grâce auquel je n'aurai pas besoin d'utiliser de tableaux.
Une fois que j'aurais fini cet algorithme (il essaye de diviser par deux le nombre testé, et ensuite, par trois, par cinq, par sept etc. jusqu'à que le modulo soit égal à zéro : s'il ne l'est pas, il inscrit le nombre testé dans mon fichier), j'essayerai bien évidemment celui que j'avais prévu au départ :-)


Je prend cependant en compte tout les renseignements que tu m'as donné, et les réutiliserai pour le "vrai" algorithme.

Donc, pourrais tu analyser l'algorithme que j'ai posté (pas celui de départ) ? :D
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
3 juil. 2013 à 17:42
OK, compris :-)

alors, voilà :

- je ne sais pas pour le tiens, mais mon compilateur (MinGW) n'aime pas %Lg, alors j'ai remplacé "long double nombreMax, i;" par "long int nombreMax, i;" et utilisé %ld dans scanf et fprintf
- pareil pour le prototype de TestPremier, qui devient "int TestPremier (long int nombreTeste)"
- j'ai purgé stdin après ton scanf, pour que le getchar() ne soit pas affecté par ce qui y reste
- j'ai changé le mode d'ouverture en "w" au lieu de "a", sinon cela écrit à la suite d'un fichier éventuellement existant, au lieu de cela, désormais cela l'écrase.

au final, cela parait fonctionner de mon côté avec le code suivant :

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int TestPremier (long int nombreTeste)
{
    long double reste, a, deux;
    deux = 2;
    reste =0;
    reste = fmodl(nombreTeste, deux);
    if (reste == 0)
    {
        return 0;
    }
    for (a = 3 ; a <= sqrt(nombreTeste) ; a += 2)
    {
        reste = fmodl(nombreTeste, a);
        if (reste == 0)
        {
            return 0;
        }
    }
    return 1;
}

int main()
{
    int nombrePremier;
    long int nombreMax, i;
    char c;
    printf("Ce programme va vous calculer et vous %cnum%crer tous les nombres \npremiers de 2 %c N.\n", 130, 130, 133);
    printf("\nVeuillez choisir une valeur pour N : ");
    scanf("%ld", &nombreMax);
    while ((c = getchar()) != '\n' && c != EOF)
                /* discard */ ;
    printf("\nVotre valeur N est egale a %ld\n",nombreMax);
    getchar();
    FILE* fichier = NULL;
    fichier = fopen("nombres_premiers_trouves.txt", "w");
    fprintf(fichier, "2\n");
    for (i = 3 ; i <= nombreMax ; i += 2)
    {
        nombrePremier = TestPremier(i);
        if (nombrePremier)
        {
            fprintf(fichier, "%ld\n", i);
        }
    }
    fclose(fichier);
    return 0;
}

Dal
0

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

Posez votre question
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
4 juil. 2013 à 21:49
J'ai essayé ça, mais... Problème lors de la compilation :

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define TAILLE_MAX 1000000000

void EcrireTableau (unsigned long long nombreAEcrire);
int TestPremier (unsigned long long nombreTeste);

int main()
{
    unsigned long long nombreMax, i;
    unsigned long long* nombrePremier;
    *nombrePremier = 0;
    int *tableau = malloc(TAILLE_MAX * sizeof(unsigned long long));
    if(tableau == NULL)
    {
        printf("L'allocation n'a pu être réalisée\n");
        system("pause");
        exit(5);
    }
    printf("Ce programme va vous calculer et vous %cnum%crer tous les nombres \npremiers de 2 %c N.\n", 130, 130, 133);
    printf("\nVeuillez choisir une valeur pour N : ");
    scanf("%llu", &nombreMax);
    printf("\nVotre valeur N est egale a %llu\n",nombreMax);
    FILE* fichier = NULL;
    fichier = fopen("nombres_premiers_trouves.txt", "w");
    fprintf(fichier, "2\n");
    for (i = 3ULL ; i <= nombreMax ; i += 2)
    {
        if (TestPremier(i))
        {
            EcrireTableau(i);
        }
    }
    unsigned long long z;
    for (z = 0 ; z <= nombrePremier ; z++)
    {
        fprintf(fichier, "%llu\n", tableau[z]);
    }
    fclose(fichier);
    return 0;
}

void EcrireTableau (unsigned long long nombreAEcrire)
{
    *nombrePremier++;
    tableau[*nombrePremier] = nombreAEcrire;
}
int TestPremier (unsigned long long nombreTeste)
{
    unsigned long long a;
    int reste;
    reste = 0;
    reste = nombreTeste - (nombreTeste / 2);
    if (reste == 0)
    {
        return 0;
    }
    for (a = 3ULL ; a <= sqrt(nombreTeste) ; a += 2)
    {
        reste = nombreTeste - (nombreTeste / a);
        if (reste == 0)
        {
            return 0;
        }
    }
    return 1;
}

0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
4 juil. 2013 à 23:29
et les messages d'erreur ou avertissements ne te mettent pas sur la voie ?

Sinon :

- allouer 1000000000 unsigned long long n'est pas nécessaire (à supposer que tu aies cette quantité de mémoire disponible). Alloue seulement nombreMax, cela sera largement suffisant (ou une évaluation du nombre de nombres premiers basée sur les théorèmes mentionnés plus haut)

- si tu alloues de la mémoire avec malloc, il faut la libérer après usage avec free

- mon compilateur paramétré sur C89 se plaint encore de tes types (cette fois "unsigned long long", car c'est un type C99), alors je lui préfèrerait "unsigned long int", qui est largement suffisant (et a une capacité supérieure à 1000000000 sur ma machine)

- unsigned long long* nombrePremier; n'est pas un bon nom, car, en fait, tu te sert de cette variable pour décompter le nombre de nombres premiers trouvés. Alors j'appellerai cette variable nb_np_trouves. De plus je ne vois pas l'utilité d'en faire en pointeur (d'autant que tu n'alloues pas la mémoire occupée par le type pointé). Alors autant en faire directement un entier, pour moi "unsigned long int nb_np_trouves;"

- tes prototypes pour EcrireTableau et TestPremier ne sont pas bons, car tu as besoin dans ces fonctions d'accéder : à nb_np_trouves et à tableau. Donc les prototypes devraient être :

void EcrireTableau(unsigned long int nombreAEcrire, unsigned long int * nb_np_trouves,
        unsigned long int *tableau);
int TestPremier(unsigned long int nombreTeste, unsigned long int nb_np_trouves,
        unsigned long int *tableau);

dans le cas de EcrireTableau, tu as besoin d'incrémenter nb_np_trouves, c'est pourquoi il faut passer un pointeur.

- ton tableau ne sert à rien, puisque tu ne l'utilises pas dans TestPremier, alors ... utilise-le :-)

- attention aux constructions comme "*nombrePremier++;". Outre le fait que je rebaptiserai "nombrePremier" en "nb_np_trouves" pour des raisons sémantiques, il faudrait faire :

(*nb_np_trouves)++; 

pour s'assurer que tu incrémentes le contenu pointé par le pointeur, une fois déréférencé, et que ce n'est pas le pointeur que tu incrémentes.

De plus, il faut incrémenter après avoir fait :

tableau[*n] = nombreAEcrire;

et non avant, sinon, tu démarres au mauvais endroit.

Avec ces indications, tu devrais pouvoir rétablir un code fonctionnel.


Dal
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
5 juil. 2013 à 08:35
Alors....
De un, merci encore une fois du temps que tu m'as consacré. De deux, je ne posterai pas encore un autre code pour l'instant car je n'ai pas le temps pour l'instant, et de trois, je voulais juste savoir pourquoi tu ne veux pas que j'utilise du C99... C'est dommage, car je pourrais travailler sur des nombres vraiment très très grands... :(
Et puis en plus, l'usage des commentaires sur une ligne est exclusif au C99, alors qu'on
l'utilise tout le temps, je ne vois donc pas pourquoi je ne pourrais pas faire :

int main ()
{
     unsigned long long a = 15000000000000000;
     printf("a a pour valeur %ull", a);
     return 0;
}


Merci d'avance!
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
5 juil. 2013 à 10:06
Tu utilises du C99 si tu le veux, je ne suis personne pour t'interdire de le faire :-)

En ce qui me concerne, lorsque je crée un nouveau projet, je n'ajoute pas aux options de compilation de gcc "-std=c99". Alors mon code est en C89 par défaut.

Je me permets juste de te signaler que ton code n'est pas portable sur un compilateur C89. C'est un choix. Ce qui veux dire aussi que les personnes du forum qui n'utilisent pas C99 par choix, ou simplement parce qu'ils ne règlent pas les options sur C99, si leur compilateur le supporte, ne pourront pas compiler ton code.

En ce qui concerne les commentaires, il n'y en a pas dans ton code ;-) Mais, là aussi, c'est une question d'orthodoxie par rapport aux standards.


Dal
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
5 juil. 2013 à 10:18
Dans ton cas utiliser C99 n'était, de plus, pas vraiment utile, puisque tu te limitais à un nombre maximal de 1000000000, inférieur au maximum de unsigned long int, du moins pour mon MinGW, sur un processeur 32 bits sous Windows, le maximum est :

#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("ULONG_MAX = %lu", ULONG_MAX);
    return 0;
}

donne :

ULONG_MAX = 4294967295

soit plus de 4 fois plus ce que ton programme voulait de toutes façons utiliser.


Dal
0
Pierrot-du-18 Messages postés 133 Date d'inscription vendredi 28 décembre 2012 Statut Membre Dernière intervention 8 mai 2014 4
5 juil. 2013 à 14:42
Oui, pour le tableau, d'accord, mais le but de mon programme est de tester des nombres jusqu'à plus de 10 000 000 000... Et là, ça ne marche pas... As tu une proposition de solution pour traiter des nombres supérieurs à 10 milliards en C89 ?
0