Outils pour utilisateurs

Outils du site


itc:tps:tp3:exercice2

Création d'une liste de nombre premiers

On veut créer, en vue de consultation, un fichier qui contient les premiers nombres premiers dans l'ordre croissant. On en mettra un par ligne. On commence par la création du fichier dans lequel on va rentrer soi-même les tout premiers. Le fichier est créé dans le dossier courant, que l’on peut connaître avec la commande os.getcwd. S'il ne convient pas on peut en changer, voir le cours.

Ouverture d'un fichier en écriture

L'exemple ci-dessous permet de créer le fichier avec les premiers nombres premiers.

#Création du fichier et premier remplissage
fich = open('liste_premiers.txt', 'w') # création du fichier pour écriture
fich.write('2\n') # le \n marque le passage à la ligne
fich.write('3\n')
fich.write('5\n')
fich.write('7\n')
fich.close()

L'option 'w' dans la fonction open permet d'indiquer que l'ouverture ce fait en mode écriture – write.

Un fichier ouvert doit être refermé ! En effet, quand on exécute open, Python demande au système d'exploitation (OS) de lui donner accès au fichier. C'est particulièrement important en mode écriture car l'OS va verrouiller le fichier pour empêcher un autre processus d'écrire dedans. Si on ne referme pas le fichier avec close, l'OS ne sait pas qu'il peut déverrouiller le fichier.

Lecture du contenu du fichier

On a ainsi les quatre premiers nombres premiers et si on veut voir le contenu de ce fichier on entre

fich = open ('liste_premiers.txt', 'r')
content = fich.read()
fich.close()

fich est un objet spécial représentant le fichier. Il ne faut pas confondre fich avec le contenu de fich qui est le texte contenu dans le fichier. On accède à ce texte avec read. Une fois que l'on a stocké le contenu de fich dans content, on peut refermer le fichier avec close.

Trouver de nouveaux nombres premiers

Pour rajouter des nombres premiers on utilise l'algorithme du crible d'Ératosthène : un nombre est premier si et seulement si il n'admet aucun diviseur premier inférieur ou égal à sa racine carrée.

On rappelle que 1 n’est pas premier ; le premier nombre premier est 2 et c'est le seul nombre premier pair. Ainsi pour rajouter p nombres premiers on ouvre liste premiers, on extrait la liste des nombres premiers déjà connus, on prend successivement les impairs strictement supérieurs au dernier nombre premier connu et on teste leur primalité avec la propriété donnée ci-dessus et en cas de succès on rajoute le nouveau nombre premier à la liste en l'écrivant dans une nouvelle ligne à la fin du fichier. On met tout cela dans une fonction ajout de paramètre p.

Au choix, on peut essayer d'écrire tout cela tout seul, ou recopier et traduire le pseudo-code suivant, en prenant le temps de le comprendre, voire en le modifiant si c'est pertinent. On rajoute la mesure du temps de l'ajout à l’aide de la fonction time du module time.

importer module time

FONCTION ajout
ENTRÉE: p, int, nombre de premiers à ajouter
SORTIE: float, temps d'exécution en secondes
DÉBUT
    enregistrer l'heure de début
    soit content le contenu du fichier 'liste_premiers.txt'
    soit lines le tableau obtenu en découpant content à tous les sauts de ligne '\n'
    soit premiers le tableau contenu la conversion en int des items non vides de lines
    soit N le nombre d'items dans premiers
    soit last_p le dernier item de premiers
    soit try_p = last_p + 2 le premier nombre à tester
    TANT QUE le nombre d'items dans premiers < N + p, RÉPÉTER
        soit success = True, marqueur qui passera à False si try_p a un diviseur
        POUR CHAQUE prem DANS premiers FAIRE
            SI prem divise try_p ALORS
                success passe à False
                interrompre la boucle
            FIN
        FIN
        SI success est True ALORS
            ajouter try_p au bout de premiers
        FIN
        ajouter 2 à try_p
    FIN
    ouvrir le fichier 'liste_premiers.txt' en ajout
    POUR CHAQUE item DANS premiers à partir du rang, N FAIRE
        convertir cet entier en texte
        l'écrire dans le fichier en ajoutant '\n' à sa suite
    FIN
    fermer le fichier
    enregistrer l'heure de fin
    renvoyer la différence entre l'heure de fin et l'heure de début
FIN

Ce script a un défaut : supposez qu'on lance dans 2 consoles différentes, à peu près en même temps, la commande ajout(100000). Que va-t-il se passer ? Le premier appel lit le fichier, constate qu'on n'a que les premiers de 2 à 7, libère le fichier et commence à chercher. Le deuxième appel accède lui aussi au fichier et fait le même constat et se met à chercher. Quand il a terminé sa recherche, le premier appel ajoute les premiers 11, 13, … Le 2e appel attend que le premier appel ait terminé d'ajouter ses trouvailles puis ajoute les siennes, qui sont les mêmes… Pour éviter ce problème, on pourrait ouvrir le fichier en écriture dès le début et ne le fermer qu'à la fin de façon à verrouiller l'accès au fichier tout du long de la recherche.

Consultation de la liste

FONCTION affiche_liste():
DÉBUT
    soit content le contenu de 'liste_premiers.txt'
    soit lines le tableau obtenu en découpant L à tous les sauts de ligne '\n'
    afficher le nombre d'items dans lines et lines
FIN

Avec affiche_liste() on a la liste des premiers nombres premiers connus.

Mise en œuvre

Il est temps de s'amuser et d'enrichir notre fichier, en essayant d'aller raisonnablement loin.

On peut en rajouter 500, 1000, etc. et on pourra constater que suivant le nombre N de nombres premiers déjà calculés le temps d'exécution de ajout(p) augmente avec N , ce qui peut interroger le mathématicien, la mathématicienne, et aussi l’informaticien, l’informaticienne mais pas forcément pour les mêmes raisons.

On peut maintenant interroger notre fichier pour savoir si un nombre n est premier ou non (s'il est dans la liste), pour connaître, dans le cas où il serait premier, son rang dans la liste des premiers et inversement pour un rang p donné, retourner le p-ième nombre premier. L'intérêt étant que les calculs sont faits une fois pour toutes et donc l'accès à l'information est plus rapide.

Écrire une fonction p_ieme_premier(p) qui retourne le p-ième nombre premier. Il faudra prévoir le cas où p est trop grand pour la liste calculée soit, au choix, en répondant que le nombre est trop grand pour l'état actuel des données, soit en rajoutant les nombres premiers nécessaires (les deux versions sont intéressantes).

Écrire une fonction rang_premier(n) qui pour un entier n précise s'il est premier ou non – il faudra là encore gérer le dépassement comme ci-dessus – et, dans le cas où il est premier, donner son rang dans la liste des premiers déjà calculés.

Fréquence des nombres premiers

Ceci étant fait, on s'intéresse en mathématiques à la fréquence des nombres premiers, c'est-à-dire, si on note $\pi(n)$ le nombre de nombres premiers inférieurs ou égaux à $n$, on s'intéresse au quotient $\frac{\pi(n)}{n}$.

En utilisant la liste obtenue, on peut définir une fonction Python pi_premier(n) et considérer numériquement ou graphiquement la suite $\left(\frac{\pi(n)\,\ln(n)}{n}\right)_{n\geqslant 2}$ et on voit des choses que l’on interprète.

On sait qu'il y a de plus en plus de nombres premiers – l'ensemble des nombres premiers est infini – mais on voit qu'ils sont de plus en plus rares et on peut quantifier cette rareté progressive. C'est le célèbre théorème des nombres premiers que l'on découvre numériquement, visuellement, ici.

itc/tps/tp3/exercice2.txt · Dernière modification : de goupillwiki