Académique Documents
Professionnel Documents
Culture Documents
Dans le cadre de notre cours, on va utiliser la version 3 du langage (bien que la version 2 est
encore utilisée mais elle présente certaines différences syntaxiques) qui est téléchargeable via
ce lien :
Il suffit de suivre les instructions pour installer correctement le système sur votre ordinateur et
Python est prêt à l’utilisation.
Et une autre version disponible sur Apple store pour iOS intitulée Pythoni :
Mais ce n’est pas l’unique manière de faire. Python permet aussi d’écrire des programmes
complets, on parle du mode script. Il suffit de tapez CTRL+N pour éditer un nouveau script :
Il est interdit d’utiliser une variable non initialisée (sans valeur de départ) dans une expression :
On peut effectuer des affectations simultanées (en même temps) de plusieurs variables :
Pour tester si deux variables sont identiques (possédant le même identificateur), on utilise la
commande is :
• Valeur absolue :
• Conversion de types :
• Fonctions mathématiques :
Puissance :
Div et mod :
Minimum et maximum :
Arrondi :
Une fois saisi, on utilise CTRL+S pour sauvegarder le script, il faut ne pas oublier de lui
attribuer un nom avec l’extension .py (par exemple script1.py). Le programme ainsi enregistré
est prêt à l’exécution, il suffit de taper F5 et on voit apparaître le résultat sur l’IDLE (et pas au
niveau du script) :
2.1. Introduction
Python permet de manipuler deux catégories de types :
- Les types simples : les entiers, les réels, les booléens, les nombres complexes
- Les types structurés appelés aussi conteneurs qui seront traités dans des chapitres
ultérieurs.
Dans ce chapitre, on va exposer les principales commandes applicables sur chacun des types
numériques cités précédemment ce qui nous mènera à aborder la notion de modules à savoir les
deux modules math et cmath.
• Opérateurs arithmétiques
Addition +, soustraction -, multiplication *, division / et puissance ** :
Partie imaginaire
Conjugué
…etc.
• l’argument : phase
Belgacem Hajji ~ 10 ~
Chapitre 3 : Programmation Python
3.1. Introduction
Jusqu’à maintenant, on a utilisé Python en mode interpréteur, on tape une commande (une sorte
de requête) et on obtient le résultat correspondant. Cependant, Python permet comme les autres
langages de programmation d’éditer des scripts complets pouvant contenir des instructions
séquentielles, conditionnelles et des boucles.
On peut tout de même forcer la saisie d’un entier ou d’un réel directement en utilisant
respectivement les deux commandes int et float déjà rencontrées :
Parfois l’utilisateur se trompe sur la valeur introduite, l’instruction input déclenche une
erreur ou une exception, un message en rouge apparait indiquant la nature de l’erreur :
Belgacem Hajji ~ 11 ~
On peut afficher plusieurs messages sur une même ligne en choisissant le caractère de
séparation en utilisant la syntaxe suivante : print(mes1,mes2,…mesk,sep=caractere)
Pour choisir le caractère de fin de ligne, on utilise l’option end de la commande print (si
le caractère choisi est ‘’ alors il n’y aura pas de retour à la ligne) :
• L’affectation :
On affecte une valeur à une variable en utilisant l’opérateur =. La commande n’affiche
aucun résultat :
On n’a pas le droit d’utiliser des variables non initialisées dans une affectation :
Si condition alors
instruction
finsi
Belgacem Hajji ~ 12 ~
On remarque que Python utilise la notion d’indentation (espacement) au niveau du bloc
d’instruction ce qui lui permet de distinguer quelles sont les instructions à exécuter si la
condition est vraie. Python gère automatiquement l’ensemble de ces espacements, il
suffit de mettre : et de taper ENTREE pour que le curseur soit placé au bon endroit
d’écriture.
Si condition alors
Instruction1
Sinon
Instruction2
Finsi
• schéma imbriqué :
Si cond1 alors
ins1
sinon si cond2 alors
ins2
…..
Sinon insk
Finsi
Finsi
…
Finsi
Belgacem Hajji ~ 13 ~
• Application : Donner les instructions Python qui permettent d’afficher la solution de
l’équation ax+b=0 avec a et b deux réels introduits au clavier. Discuter tous les cas
possibles.
- TANTQUE ….FAIRE :
Tantque condition faire
Instruction(s)
fintantque
- REPETER ….JUSQU’A :
Répéter
Instruction(s)
Jusqu’à condition
Belgacem Hajji ~ 14 ~
Exemple 2 : le calcul de la somme
• boucle for :
- avec l’utilisation des intervalles :
Python offre un objet spécial appelé range qui représente un intervalle qui n’est
autre qu’une suite de valeurs discrètes (et pas réelles). La syntaxe générale de la
commande range est la suivante :
range(BI,BS,pas)
Avec :
➢ BI = borne inférieure par défaut égale à 0
➢ BS = borne supérieure jamais atteinte (on s’arrête toujours en BS-1)
➢ pas = Le pas qui est par défaut égal à 1 pouvant être un entier positif ou négatif :
Belgacem Hajji ~ 15 ~
La notion d’intervalle est utilisée pour déterminer la plage des valeurs que va prendre un
compteur dans une boucle pour. L’instruction Pour est matérialisée via le mot clé for de python
dont la syntaxe a la forme suivante :
boucles croissantes :
Supposons qu’on veut utiliser une boucle for pour effectuer différents
affichages :
➢ affichage des entiers compris entre 0 et 3 :
Belgacem Hajji ~ 16 ~
boucles décroissantes :
Le pas doit être une valeur négative avec une borne inférieure > à la borne
supérieure :
NB : on peut utiliser une boucle for pour itérer sur les éléments d’un conteneur (notion qui sera
vue dans les séances prochaines). La forme générale de la boucle est la suivante :
Exemple :
Belgacem Hajji ~ 17 ~
• break : permet de quitter la boucle quand une condition est vraie :
Exemple 1: Saisie d’une variable entière strictement positive sans gestion d’erreurs :
Belgacem Hajji ~ 18 ~
Exemple 2 : Saisie d’une variable entière strictement positive avec gestion d’erreurs :
Applications:
1) Ecrire un script qui affiche tous les entiers premiers compris entre deux entiers a et
b (a<b) saisis au clavier. Faites tous les contrôles nécessaires.
2) Ecrire un script qui résout dans C une équation du second degré à coefficients réels
de la forme ax²+bx+c=0. Discuter tous les cas envisageables.
Belgacem Hajji ~ 19 ~
Chapitre 4 : Les conteneurs à accès séquentiel
(Tuples, listes et chaines)
4.1. Introduction
Python permet de regrouper un ensemble de données de même type ou de types différents à
l’intérieur de structures de données spéciales appelées conteneurs. Il existe deux catégories de
conteneur :
- Les conteneurs séquentiels : On note les listes, les tuples et les chaines de caractères. A
l’intérieur de ces structures de données, les éléments sont rangés en utilisant des index
(commençant de 0).
- Les conteneurs à accès directs qui seront abordés dans le chapitre suivant.
Dans la suite, on va exposer les propriétés communes aux conteneurs séquentiels, puis on
abordera le côté spécifique de chacun de ces derniers.
• Une chaine : une séquence de caractères encadrés par des côtes ou des chevrons ‘’ ou
«»:
• Taille : len(C)
• Accès à un élément : C[index de l’élement] : Les index permis sont compris entre 0 et
len(C )-1 pour un parcours de gauche à droite et entre -1 et –len(C) pour un parcours
de droite à gauche
Belgacem Hajji ~ 20 ~
• Accès à une partie des éléments : C[i :j] : renvoie un conteneur constitué des éléments
se trouvant entre la position i et la position j-1.
NB :
- C[ :i] : renvoie la sous liste constituée des éléments d’indices compris entre 0 et i-1
- C[i :] : renvoie la sous liste constituée des éléments d’indices compris entre i et len(C)-
1.
- C[ ::] :renvoie la totalité de la liste
• Accès à une partie des éléments avec notion de pas : C[i :j :k] : renvoie un conteneur
stockant les éléments entre les deux positions i et j-1 en effectuant des sauts d’amplitude
k
• Test d’appartenance : x in C
Belgacem Hajji ~ 21 ~
• Répétition : C*n : l’opérateur * permet de créer un conteneur contenant n copies de C
Application :
- Créer un tuple t=(1,2,4,-6) et une chaine ch=’abc cd ! ;:’
- Appliquer toutes les commandes vues précédemment pour t et ch.
NB : Les conteneurs ne peuvent pas être tous modifiés après leurs créations, seules les listes
sont des structures de données modifiables ou mutables ou non immuables, tuples et chaines
sont non mutables ou immuables.
commande list :
avec les [] :
Belgacem Hajji ~ 22 ~
Application : retrouver les listes en compréhension présentées en haut en utilisant les boucles.
par conversion
d’un slice
Application : créer une liste contenant les entiers compris entre 0 et 5 puis modifier tous les
éléments impairs par des 0 en utilisant la notion de pas.
• ajout
d’un élément à la fin : on utilise la commande append
Belgacem Hajji ~ 23 ~
• suppression
d’un élément : trois possibilités : del, remove et pop
d’un slice
• copie :
copie normale : avec la commande copy :
Belgacem Hajji ~ 24 ~
• tri : avec la commande sort
par conversion
en compréhension
• modification :
Les tuples sont non modifiables, toute tentative de mise à jour déclenche une erreur :
Belgacem Hajji ~ 25 ~
• ajout d’un élément :
Belgacem Hajji ~ 26 ~
• coller plusieurs chaines : commande join : opération réciproque de split :
• Recherche d’une sous chaine dans une autre chaine : commande find :
Belgacem Hajji ~ 27 ~
Chapitre 5 : Les conteneurs à accès direct
(Dictionnaires & ensembles)
5.1. Introduction
Dans le chapitre précédent, on a traité les conteneurs séquentiels de Python. Pour accéder à un
élément d’un conteneur, on utilise son index. Les valeurs autorisées des index sont
obligatoirement séquentielles. Python permet d’utiliser une autre catégorie de conteneurs
maniant des index organisés d’une manière non séquentielle. Le conteneur n’est plus une
structure ordonnée, il s’agit bien d’un conteneur à accès direct.
• Dictionnaire vide :
• La commande dict (avec comme paramètre une liste de listes, chacun de la forme
[clé ,valeur]) :
• Les {} :
• En compréhension
Belgacem Hajji ~ 28 ~
• Directement (en utilisant la syntaxe NomDict[cle]=valeur) :
La taille d’un dictionnaire qui est le nombre d’entrées qu’il contient est accessible via la
commande len, pour afficher une valeur correspondante à une clé, on utilise la syntaxe
NomDict[clé], les deux opérateurs in et not in testent l’appartenance d’une clé à un
dictionnaire :
Python permet d’accéder aux informations stockées au niveau d’un dictionnaire de plusieurs
manières :
Belgacem Hajji ~ 29 ~
Une entrée peut être accédée par la commande get :
• créer une entrée vide si on fournit un index non encore utilisé et pas de valeur en
deuxième paramètre :
La mise à jour d’un dictionnaire passe par l’ajout, la modification et la suppression d’entrées :
Belgacem Hajji ~ 30 ~
• pop : la syntaxe est NomDict.pop(index) : supprime l’entrée dont la clé est index et la
retourne en résultat ; popitem : la syntaxe est NomDict.popitem() : supprime
aléatoirement une entrée et la retourne :
• copy : La syntaxe est d2=d1.copy() : copie un dictionnaire dans un autre après avoir
importer le module copy.
• ensemble vide :
• la commande set :
Belgacem Hajji ~ 31 ~
• les {}
• en compréhension
Un ensemble est une structure de données non ordonnée. La commande len donne la cardinalité
de l’ensemble. On peut tester l’appartenance d’un élément à un ensemble mais on ne peut pas
utiliser un indice :
La mise à jour d’un ensemble est possible vue que c’est un type mutable :
Belgacem Hajji ~ 32 ~
discard : La syntaxe est s.discard(elt) : supprime un élément s’il existe et ne déclenche
pas d’erreurs si elt ne fait pas part de s :
Belgacem Hajji ~ 33 ~
• un ensemble contient un autre ensemble
• L’union
• L’intersection
• La différence
• La différence symétrique
Belgacem Hajji ~ 34 ~
Chapitre 6 : Programmer des fonctions sous Python
6.1. Introduction
Python offre une panoplie de fonctions prédéfinies prêtes à l’utilisation. On distingue
les fonctions qui se chargent au moment du démarrage de l’interpréteur comme la
fonction abs() ou la fonction len() …(il suffit d’appeler ses fonctions en précisant le(s)
paramètre(s) de chacune), d’autres qui se chargent au moment de l’importation de
modules (cas de sqrt du module math ou de polar du module cmath) … Ce chapitre a
pour but d’initier à la programmation des fonctions en Python.
6.2. Définition
Une fonction est un bloc d’instructions qui possède un nom, qui reçoit un certain nombre
de paramètres et renvoie un résultat. L’usage des fonctions permet :
• d’éviter la répétition
• de séparer les données des résultats
• de rendre le code réutilisable à travers l’importation de fichiers de fonctions…
• et de décomposer les tâches complexes en tâches simples plus abordables.
On doit définir une fonction alors à chaque fois qu’un bloc d’instructions se trouve
répété à plusieurs reprises. Il s’agit d’une factorisation du code.
6.3. Syntaxe
Une fonction Python à la syntaxe suivante :
Belgacem Hajji ~ 35 ~
6.4. Première fonction Python
On vient de créer une première fonction sous Python (La saisie peut être effectuée au
niveau du shell ou à l’intérieur d’une nouveau fichier .py, la différence entre les deux
cas sera traitée ultérieurement, mais pour le moment on opte pour la première option).
La fonction TableMul5 ne possède pas de paramètres, on lui a confié la responsabilité
d’afficher la table de multiplication de 5, ce qui explique la documentation entre triple
côtes… Une fois déclarée, la fonction peut être appelée comme le montre l’exemple
suivant :
Une fonction est définie pour assurer la factorisation du code et la réutilisabilité. Dans
l’exemple précédent, en dehors de la valeur 5, la fonction n’accepte aucune autre valeur.
D’où la pertinence de la notion de paramétrage. On redéfinit alors TableMul en réservant
un paramètre n qui représentera la valeur de l’entier dont on cherche à afficher sa table
de multiplication :
La fonction TableMul autorise des appels avec des paramètres de natures différentes
(Besoin de contrôle d’eventuelles erreurs de saisie à travers la gestion des exceptions…)
:
Belgacem Hajji ~ 36 ~
6.5. Le NonType & les fonctions avec retour
On peut aller un peu plus loin avec les fonctions Python et développer une vraie fonction
qui retourne un résultat récupérable par la fonction appelante (on traitera plus tard la
fonction __main__ qui représente la notion de programme principale). Le mot clé return
permet d’arrêter l’exécution d’une fonction et de renvoyer un résultat. Tous les types
vus en Python peuvent être utilisés comme un retour. Quand la fonction ne programme
pas d’instructions de type return, Python retourne tout de même un objet vide ayant
comme type NoneType. C’est un type réservé par le langage pour exprimer le vide, tel
est le cas d’une liste ou d’un tuple vide.
On va partir d’une fonction qui affiche la somme de deux entiers passés en paramètres :
En vérifiant le type du résultat obtenu, on tombe sur le cas d’un objet vide, très logique
print ne fait pas le travail de return :
Rectifions la fonction som de sorte qu’elle soit une vraie fonction avec un return :
Le type du résultat est bel et bien un entier. Le mot clé return peut figurer dans plusieurs
endroits au sein d’une fonction. La fonction signe retourne une chaine exprimant le signe
d’un argument x :
Belgacem Hajji ~ 37 ~
Quand une fonction programme plus d’un paramètre en retour, le type tuple s’avère très
utile. La fonction somdifprod calcule la somme, la différence et le produit de deux
entiers a et b passés en paramètres, un objet de type tuple constitué des trois résultats est
alors retourné. Au moment de l’appel, il faut prendre en considération la nature du
résultat. Soit qu’on utilise un objet qui stockera tout le tuple puis pour accéder aux
résultats on utilise les indices respectifs suivant la taille de ce dernier, soit qu’on effectue
un appel basé sur trois variables :
La première version de la fonction permut est supposée échanger le contenu des deux
variables a et b :
Belgacem Hajji ~ 38 ~
Sauf que le contenu des deux variables reste inchangé preuve que les types non mutables
ne peuvent pas être modifiés. Dans cette deuxième version, on effectue un affichage du
contenu de a et b à l’intérieur de la fonction avant la fin de son exécution :
- Passage par variable : Les objets mutables sont modifiables par nature. La fonction
ajout ajoute à la fin d’une liste l un objet x.
Belgacem Hajji ~ 39 ~
Pour mieux assimiler la notion de mode de passage, on se base sur l’évolution de la
valeur des identifiants des paramètres avant l’appel, au cours et à la fin de l’exécution
d’une fonction :
Belgacem Hajji ~ 40 ~
6.7. Paramètres avec valeur par défaut - Paramètres positionnels
Une fonction peut être déclarée avec des paramètres ayant une valeur des valeurs de
départ. Si l’utilisateur omet l’un des paramètres au moment de l’appel, la valeur par
défaut sera prise en considération tel est le cas de la fonction construire_point qui affiche
le point (0,0) si l’utilisateur ne communique aucun paramètre.
Belgacem Hajji ~ 41 ~
6.10. Les fonctions imbriquées
Il est possible de déclarer une fonction g à l’intérieur d’une fonction f. Seule la fonction
f est capable d’appeler g. Cette dernière a un comportement local au sein de f. Python
offre deux commandes qui permettent de retourner une liste composée chacune des
noms des objets considérés comme locaux à une fonction et de leurs équivalent
globaux : locals() et globals() :
Suite à l’appel de la fonction min3, on obtient la liste des objets locaux et globaux
pour chacune des fonctions min2 et min3. La fonction min2 figure comme un objet
local à la fonction min3.
Belgacem Hajji ~ 42 ~
6.11. Les variables globales
Pour forcer une fonction à considérer une variable comme globale, on utilise le mot clé
global suivi du nom de la variable en question. La fonction inc1 utilise la variable x sans
la déclarer ni comme paramètre ni comme variable globale. L’interpréteur ne reconnait
pas x et déclenche une exception :
la deuxième version, inc2(), la variable x est déclarée globale d’une manière explicite.
La variable est initialisée à l’extérieur de la fonction et son contenu est modifiable par
les fonctions qui la déclarent comme avec le mot clé « global ».
Belgacem Hajji ~ 43 ~
6.12. Les fonctions lambda
En Python, une fonction lambda est une fonction anonyme (à laquelle on n’a pas donné
de nom), et qu’on peut appliquer “à la volée” dans une expression. La syntaxe est :
lambda paramètres : expression
Les fonctions lambda sont réservées à des situations relativement simples. Leur
définition doit tenir sur une seule ligne, et elles ne peuvent pas contenir d’instructions
composées (pas d’affectation, pas de boucle, etc.). Elles consistent donc essentiellement
en la définition d’une expression calculée en fonction des paramètres qui lui sont passés.
Pour prendre un exemple simpliste (et pas très utile), les deux définitions suivantes de
la fonction f sont équivalentes :
Les fonctions anonymes permettent d’appliquer deux commandes intéressantes sur les
listes :
• map : Applique une fonction sur tous les éléments d’une liste pour former une
nouvelle liste distincte de l’initiale. La syntaxe de map est la suivante : list(map(f,l))
• filter : crée une liste contenant les éléments qui ne vérifient pas un critère spécifié
par une fonction f d’une liste initiale. La syntaxe de filter est la suivante :
list(filter(f,l))
Supposons qu’on veut supprimer tous les multiples de 3 d’une liste d’entiers :
Belgacem Hajji ~ 44 ~
6.13. Gestion des exceptions
Quand une erreur se produit dans un script, elle provoque l’arrêt du programme et
l’affichage d’un message d’erreur. Pour éviter cette interruption brutale, on peut
appliquer un traitement spécifique. Plus généralement, on peut traiter une situation
exceptionnelle dont on ne sait pas forcément où et quand elle va survenir à l’intérieur
d’un bloc donné. Plutôt que de parler d’erreur, on emploiera donc le terme “exception”
et prévoir une réaction adaptée à une exception, c’est la “rattraper”.
Pour le traitement des exceptions, Python offre la clause try: ... else.La forme la plus
simple est la suivante, où le bloc2 est parcouru si une exception (quelle qu’elle soit) est
rencontrée dans le bloc1 (cette exception provoque l’arrêt du bloc1 et le passage
immédiat au bloc2) :
On peut également prévoir un traitement particulier pour telle ou telle exception. Dans
ce cas, on utilisera une construction un peu plus élaborée, du genre :
Belgacem Hajji ~ 45 ~
La gestion des exceptions favorise le contrôle des données. Au moment de la saisie,
l’utilisateur peut se tromper sur une valeur introduite. La fonction saisie par exemple
doit allouer la saisie de nouveau d’une nouvelle valeur en cas d’erreur en spécifiant la
nature du bug détecté avec un message explicatif :
Belgacem Hajji ~ 46 ~
Le regroupement de plusieurs modules dans un dossier donne naissance à un paquetage
(package en anglais).
On souhaite développer un module intitulé pile qui permet de créer une nouvelle
structure de donnée LIFO (Last In First Out). Cette structure repose sur deux
opérations :
• Empiler : ajouter un nouvel élément à la fin de la pile si cette dernière n’est pas
saturée
• Dépiler : supprimer toujours l’élément récemment ajouté si la pile n’est pas vide.
Sous Python, on ouvre un nouveau script avec CTRL+N, pour éditer notre script :
Belgacem Hajji ~ 47 ~
On enregistre ce script sous le nom de « pile.py ». Le module est prêt à l’utilisation :
Belgacem Hajji ~ 48 ~
L’appel du script précédent donne :
Belgacem Hajji ~ 49 ~
Chapitre 7 : La récursivité
7.1. Introduction
Pour minimiser la complexité d’un problème informatique, on opte pour sa décomposition en
sous problèmes de complexités moindres. Certains problèmes spéciaux sont vues comme étant
un ensemble de sous problèmes similaires au problème original mais qui opèrent sur des
données de tailles plus petites. Résoudre le grand problème revient alors à résoudre les sous
problèmes, qui s’avèrent parfois facile à faire. L’algorithme de résolution du problème initial
va s’appeler lui-même pour résoudre les sous problèmes semblables. On parle de la notion de
récursivité des algorithmes. Une illustration de cette dernière est le calcul de la factorielle d’un
entier positif ou nul n. On sait que n !=n*(n-1)*…*1, il suffit de remarquer que calculer n !
revient à multiplier n par (n-1) ! que le problème soit résolu. Dans la suite, on présente une
fonction python chargée de calculer la factorielle de n d’une manière récursive :
….
Un appel récursif induit lui-même d’autres appels récursifs, ce qui peut mener à un nombre
infini d’appels. Il faut arrêter la suite des appels quand on obtient le résultat. Dans notre cas, on
sait que 0 ! = 1, une fois n atteint la valeur 0, le processus d’appel doit s’arrêter :
Belgacem Hajji ~ 50 ~
7.2. Définition
Une fonction est dite récursive si elle s’appelle elle-même. Il faut prévoir toujours une condition
d’arrêt pour stopper les appels récursifs sinon on obtient un traitement similaire à une boucle
infinie.
Python utilise une pile d’exécution pour enregistrer les appels et leurs contextes (paramètres
utilisés, résultats rendus, variables locales déclarées par chacun des appels …), mais la taille de
cette dernière n’est pas illimitée. Un chevauchement des appels mène au déclenchement d’une
exception (débordement de la taille de la pile).
• Appel de fact(3) :
fact(3) :
Retourner 3*fact(2)
• Appel de fact(2) :
fact(3) : fact(2) :
Retourner 3*fact(2) Retourner 2*fact(1)
• Appel de fact(1) :
• Appel de fact(0) :
• fact(0) = 1
• fact(1) = 1*fact(0)=1 * 1 = 1
• fact(2) = 2*fact(1)=2 * 1 = 2
• fact(3) = 3*fact(2)=3 * 2 = 6
- On paramètre le problème ;
- On décompose le cas général de façon à réduire la taille des données ;
- On détermine la condition d’arrêt qui arrête les énoncés récursifs.
Dans le cas de la factorielle, la relation entre le cas simple et le cas général est la réduction des
données n !=n*(n-1) !, mais ce n’est pas l’unique champ d’application de la récursivité. Parfois
Belgacem Hajji ~ 51 ~
la fonction ne retourne aucune valeur, mais effectue des actions récursives, tel est le cas de
l’affichage d’une liste ou son inversion :
Belgacem Hajji ~ 52 ~
• Solution itérative :
• Solution récursive :
On procède à un jeu d’appels avec différentes valeurs de n, on remarque que quand les
valeurs sont petites, y a pas de différence notable entre les deux méthodes sur la base du
temps de calcul nécessaire, mais l’écart devient flagrant dès qu’on accroît un peu n :
Belgacem Hajji ~ 53 ~
Le système (Python dans notre cas), réalise les actions nécessaires pour obtenir le résultat sans
qu’on lui donne l’algorithme sous forme d’actions pour résoudre le problème. On peut utiliser
la récursivité si :
Hypothèses :
- n disques troués au centre qu’on peut placer sur 3 piliers : A, B et C respectivement le
pilier à gauche, le pilier du milieu et le pilier à droite.
- Au départ, tous les disques sont placés au niveau du pilier A triés d’une manière
décroissante (le plus petit disque est en haut, le plus grand en bas)
But :
- Déplacer les disques du pilier A au pilier C en gardant la même disposition du départ
Règles du jeu :
- On ne peut déplacer qu’un seul disque à la fois et il doit être non couvert par un autre
- Un disque ne doit jamais être placé sur un autre plus petit que lui
Solution :
• Cas de n=2 :
- On déplace le petit disque de A vers B
- On déplace le grand disque de A vers C
- On déplace le petit disque de B vers C
On a utilisé 3 déplacements pour résoudre le problème = 2² -1
• Cas général :
- Déplacer les n-1 premiers disques (D1,D2,…,Dn-1) du pilier gauche vers le pilier
intermédiaire
- Déplacer le grand disque Dn du pilier gauche au pilier droit
- Et déplacer les n-1 disques du pilier intermédiaire vers le pilier droit.
Belgacem Hajji ~ 54 ~
Formulons le problème mathématiquement :
Si n=0 alors on a 0 déplacement à effectuer
Si n>0 alors Xk= 2Xk-1+ 1 avec Xk = le nombre de déplacements à effectuer à l’étape
k.
La résolution de cette relation donne l’expression de Xn en fonction de n donne :
Xn=2n-1
On vient de définir un algorithme récursif qui est basé sur un simple affichage (instruction
numéro 2) et qui effectue un appel à lui-même avec un nombre de données inférieur (n-1
disques). On est sure qu’il va se terminer à un moment donné (quand n atteint la valeur 0)
et on effectuera un nombre limité de déplacement géré par la formule précédente.
La fonction python Hanoï est basée sur le principe décrit précédemment aura l’allure
suivante :
• Multiple : La fonction s’appelle elle-même plusieurs fois dans un même appel récursif
(calcul d’une combinaison)
Belgacem Hajji ~ 55 ~
1 𝑠𝑖 𝑛 = 𝑝 𝑜𝑢 𝑝 = 0
𝐶𝑛𝑝 = { 𝑝 𝑝−1
𝐶𝑛−1 + 𝐶𝑛−1 𝑠𝑖 𝑛 > 𝑝
• Croisée : Deux fonctions récursives chacune appelant l’autre (nombres pairs et impairs)
Belgacem Hajji ~ 56 ~
Chapitre 8 : Analyse de complexité des fonctions
8.1. Introduction
Jusqu’à maintenant, on n’a fait qu’écrire des algorithmes, des scripts, des fonctions avec
l’unique objectif d’avoir le bon résultat à partir des données fournies sans jamais s’intéresser à
l’aspect performance. Qui dit performance, dit espace mémoire utilisé et temps processeur, on
parle bien de complexité spatiale et de complexité temporelle. Dans la suite, on va se focaliser
sur l’aspect complexité temporelle. La durée de l’exécution d’une fonction python est facile à
obtenir avec le module time. Mais ce n’est pas ce qui est intéressant dans le cadre de notre
étude, parce qu’il suffit de changer de machine que le temps d’exécution change. Pensons alors
chercher à décrire le comportement asymptotique d’un algorithme quand la taille des données
augmente. Ceci est réalisable grâce à des fonctions qui décrivent l’ordre de grandeur du temps
d’exécution.
Belgacem Hajji ~ 57 ~
Boucles while while condition : On note m,c1 et c2 m*(c1+c2)
ins respectivement le
nombre de boucles
espéré, le cout de la
condition et le cout de
ins
• Script 1 :
• Script 2 :
Belgacem Hajji ~ 58 ~
Un bloc d’instructions On suppose que les O(max(f1(n),f2(n),…,fk(n))
simples de la forme : complexités respectives de
i1 i1, i2,…ik sont O(f1(n)),
i2 O(f2(n)),…O(fk(n))
…
ik
if condition : On suppose que les O(max(g(n),f1(n),f2(n))
i1 complexités respectives de la
else : condition, de i1 et de i2 sont
i2 données par : O(g(n)),
O(f1(n)) et O(f2(n))
Démonstration :
• Initialisation : i(0)=1
• On suppose que la boucle while s’exécute q fois, alors i(q)=2*2*…*2 q fois
• i(q) = 2q , on applique la fonction log des deux côtés, on obtient log(i)=q*log(2)
• le nombre de boucle q est estimé alors à log(i)/log(2)=log2(i)
• Or la valeur de i tend vers N (100 dans notre cas), on conclut alors que la complexité de
cette boucle est O(log2(N))
Belgacem Hajji ~ 59 ~
Si une boucle multiplicative se situe à l’intérieur d’une boucle for alors la complexité de la
double boucle est donnée par :
O(nlog(n))
On remarque le temps nécessaire à la résolution du problème avec n disques dépend des temps
de résolution des sous problèmes à n-1 disques, lui-même dépendant du temps de résolution
des problèmes à n-2 disques… on note la présence d’une récurrence dans le calcul du coût et
par la suite dans la détermination de la fonction O associée.
Posons T(n) le temps nécessaire à la résolution du problème initial, on aura besoin de T(n-1)
unités de temps pour les deux appels récursifs et d’une unité de temps pour le déplacement d’un
disque. On rappelle que si n=0, on n’a aucun déplacement à faire. Le temps total est formulé
par la relation de récurrence suivante :
𝑇(0) = 0
{ (1)
𝑇(𝑛) = 2𝑇(𝑛 − 1) + 1
C’est une équation de récurrence linéaire de la forme suivante :
Belgacem Hajji ~ 60 ~
𝑛
𝑛
1
𝑇(𝑛) = 2 (𝑇(0) + ∑ )
2𝑖
𝑖=1
𝑓(𝑖)
Or le terme ∑𝑛𝑖=1 𝑎𝑖 est la somme de n termes d’une suite géométrique de premier terme ½ et
de premier terme ½ d’où :
𝑛
1 (1 − 1/2𝑛 )
𝑇(𝑛) = 2 (0 +
2 1 − 1/2
La simplification de cette expression donne :
𝑇(𝑛) = 2𝑛 − 1
Quand n augmente, T(n) devient exponentiel, il s’agit bien d’un algorithme en O(exp(n))
• La suite Fibonacci :
(1+√5) (1−√5)
La résolution de P(x)=0 donne deux racines r1= et r2= :
2 2
1 + √5 𝑛 1 − √5 𝑛
𝑃(𝑥) = 𝑎( ) + 𝑏( )
2 2
La solution est exponentielle.
Belgacem Hajji ~ 61 ~
• Recherche du maximum d’une liste :
La récursivité permet de chercher le maximum d’une liste en la subdivisant en deux sous listes
de tailles égales et en retournant le maximum entre les maximums respectifs des deux sous
listes. Pour trouver une solution, on a divisé le problème en deux sous problèmes chacun
opérant sur la moitié des données, on a résolu chacun des sous problèmes et on a combiné les
solutions trouvées pour aboutir à la solution finale. On parle du paradigme diviser pour
régner :
Les problèmes récursifs basés sur ce paradigme ont des fonctions de coût de la forme suivante :
𝑂(1) 𝑠𝑖 𝑛 ≤ 𝑐
𝑇(𝑛) = { 𝑛
𝑎𝑇 ( ) + 𝑐𝑛𝑘 𝑠𝑖𝑛𝑜𝑛
𝑏
Avec :
a : le nombre de sous problèmes
n : taille du problème initial
c : constante sur la taille du problème qui rend sa résolution facile (en O(1))
b : constante intervenant dans la répartition des données du problème inital
k : constante intervenant dans le calcul du temps de division du problème initial en sous
problèmes et de la combinaison des solutions résultantes pour construire la solution du
problème initial.
• a=2
• b=2
• k=0
2>20 : 2>1 O(nLog2(2))=O(n) : La recherche est linéaire.
Belgacem Hajji ~ 62 ~
8.6. Classification des problèmes
Dans la littérature, les problèmes algorithmiques sont classés selon leurs complexités comme
indique le tableau suivant :
Belgacem Hajji ~ 63 ~
exponentiel par rapport à la ensemble de
taille des données données.
Pour être mieux être sensibilisé à l’écart entre ces différentes classes de complexité, on va
considérer un processeur pouvant exécuter 109 opérations par seconde et on va essayer de
calculer le temps nécessaire à l’exécution d’un script avec différents jeux de données de tailles
croissantes comme montre le tableau suivant :
Reprenons le script qui calcule la double somme vu précédemment. La fonction de cout qui en
découle est f(n)=3n²+2. Supposons que la valeur de n=200, on peut déterminer le poids de
chaque terme de cette somme dans le temps d’exécution :
• f(200)=3*200²+2 = 120000+2=120002
• la contribution du premier terme 3n² dans le temps de calcul est évaluée à :
120000/120002 = 99,8%
• par contre la contribution du deuxième terme 2 = 2/120002 = 0,2
On peut alors garder le terme qui contribue le plus qui n’est autre que 3n². La conclusion est
que le script a une complexité quadratique ce qui justifie la notation O (la notion d’ordre de
grandeur).
Belgacem Hajji ~ 64 ~
Chapitre 9 : Algorithme de recherche en Python
8.1. Problématique
On a besoin de modifier la valeur d’un objet x par une nouvelle valeur y dans un conteneur C.
Il faut tout d’abord procéder à la vérification de l’appartenance de x à C. Python permet de
vérifier si un objet x appartient à un conteneur C avec l’opérateur in : x in C ou l’opérateur not
in : x not in C. La nature de la réponse dans ce cas est booléenne (True ou False).
Parfois la réponse booléenne ne suffit pas, le programmeur a besoin de savoir où se situe x dans
C. Ceci est réalisable grâce à la commande index (C.index(x)). Le problème peut être étendu
pour déterminer tous les i tels que C[i]=x avec i =0..len(C). On rappelle que la commande
C.count(x) calcule le nombre d’occurrences. La recherche diffère selon la nature du conteneur :
Rech1 effectue un parcours séquentiel (du 1er au dernier élément) de C. Elle itère sur les
éléments de C et pas sur les indices. A chaque fois on vérifié si i=x, dans ce cas, on retourne
vrai et on quitte la fonction. Si tous les objets de C sont parcourus et on n’a jamais obtenu la
valeur vrai pour la condition i=x, alors on est sure que l’objet n’existe pas, dans ce cas on doit
retourner la valeur faux.
Rech1 peut être appelée avec tous les conteneurs qu’on a rencontrés :
Belgacem Hajji ~ 65 ~
Bien que cette recherche séquentielle n’est pas vraiment adaptée aux dictionnaires... (À
développer dans la dernière partie du cours).
Solution 2 :
Rech2 utilise une boucle tantque basée sur les indices et pas les objets. La détermination du
résultat de la recherche est basée sur la valeur de i avec laquelle on quitte la boucle tantque. Si
i atteint len(C) c’est qu’on a parcouru tout le conteneur sans tomber sur x.
Attention à ce genre d’appels, les ensembles ne sont pas des séquences, donc la notion d’indice
n’existe pas.
Au maximum, on effectuera len(C) comparaisons dans les deux cas. Si on pose n=len(C), on
peut dire que le nombre de comparaisons augmente d’une manière parallèle avec la taille du
conteneur. D’où la complexité des deux solutions est linéaire notée O(n).
Solution 3 :
Belgacem Hajji ~ 66 ~
Rech3 parcourt les indices de C qui sont compris entre 0 et len(C)-1. Si on ne trouve pas
l’élément x d’habitude on retourne un indice non existant (comme -1) ; hors Python utilise aussi
un système d’indexation négatif, la valeur None du type NoneType permet de résoudre ce
problème.
Application : Modifier Rech3 pour obtenir la liste des indices de l’élément x dans C ainsi que
le nombre de ses occurrences.
Solution 2 :
Quand le conteneur est trié on a la possibilité de procéder par dichotomie, le principe est le
suivant :
Belgacem Hajji ~ 67 ~
On va modifier la fonction pour afficher les partitions de recherche à chaque étape :
Posons n=len(C) ; supposons que la boucle while s’exécute p fois alors à chaque boucle on a p
comparaisons au pire des cas étant donné qu’à chaque boucle on effectue une comparaison :
Initialement : n éléments
Boucle n°1 : n/2 éléments
Boucle n°2 : n/4 éléments
….
Belgacem Hajji ~ 68 ~
La valeur de p est donnée par :
log(𝑛)
𝑝= = 𝑙𝑜𝑔2 (𝑛)
log(2)
Au lieu de mettre à jour les bornes de l’intervalle de recherche, on peut appeler la fonction de
recherche en lui passant directement le nouvel intervalle en question. Le calcul du cout de la
fonction Rech3 est un peu spécial, étant donne qu’à l’intérieur de la fonction Rech3, on a un
appel à elle-même :
T(n)=T(n/2)+5
Le temps d’exécution est donné par une relation de récurrence non linéaire ayant généralement
la forme suivante :
T(n)=aT(n/b)+cnk
Belgacem Hajji ~ 69 ~
• a=1
• b=2
• k=0 (Le terme n ne figure pas dans l’expression de T(n))
bk=20=1 = a : T(n)= O (n0 log2(n))=O (Log2(n)) : On conclut que la version
récursive a la même complexité que celle itérative.
La position d’une entrée dans un dictionnaire est calculée à partir de sa clé. Une fonction
spéciale est utilisée pour ce faire, appelée fonction de hachage. Dans la littérature, on croise pas
mal de fonctions de hachage, on va considérer une fonction qui se base sur les codes ascii des
caractères constituants d’une clé.
- ajout des différentes entrées de la forme (clé,valeur) sans dépasser la taille maximale
(20 dans notre cas) :
Supposons qu’on veut ajouter les trois entrées suivantes dans un dictionnaire d : (‘orange’,.9),
(‘dattes’,6.), (‘citron’,.7) ; on procède comme suit :
Belgacem Hajji ~ 70 ~
• Recherche dans un dictionnaire :
En disposant de la clé d’une entrée, on utilise la fonction de hachage pour déterminer la position
de la valeur correspondante dans le dictionnaire :
O(T(n))=1 : Les dictionnaires sont les conteneurs qui assurent la recherche la plus
rapide.
Belgacem Hajji ~ 71 ~
Chapitre 10 : Algorithmes de tri en Python
9.1. Problématique
Dans ce cours, on va se restreindre au tri des éléments d’une liste, mais rien n’empêche
l’application des fonctions de tri qui seront présentées dans la suite aux autres conteneurs
séquentiels. Nous allons supposer que :
• Les listes sont homogènes (contenant toutes des éléments de même nature, par exemple
des listes d’entiers uniquement ou de réels uniquement…)
• Les éléments de la liste peuvent être ordonnés en utilisant une relation d’ordre par
exemple l’opérateur de comparaison ≤.
Trier une liste c’est ordonner ses éléments par ordre croissant ou décroissant. La liste sera elle-
même une donnée et un résultat. Toutes les modifications opérées lors du processus du
traitement seront enregistrées sur la liste L et pas sur une copie d’elle-même.
Belgacem Hajji ~ 72 ~
• Implémentation :
Application :
• Analyse de la complexité :
Pour faciliter le calcul du temps d’exécution, nous allons considérer la version suivante
de triselect :
Le nombre de comparaison est donné par (n-i-1) pour l’élément à la position i d’où :
Belgacem Hajji ~ 73 ~
𝑛−2
𝑇(𝑛) = (𝑛 − 1) + (𝑛 − 2) + ⋯ + 2 + 1 = ∑(𝑛 − 𝑖 − 1)
𝑖=0
T(n) peut être vu comme la somme de n-1 termes d’une suite arithmétique de premier terme 1
et de raison 1 :
• Implémentation :
• Analyse de complexité :
Pour simplifier l’étude on va se baser sur la 2ème version de tribulles :
Algorithme Unités de temps
def tribulles(L): T(n)
n=len(L) 2
for i in range(n-1): n-2 boucles
for j in range(n-i-1): n-i-1 boucles
if L[j]>L[j+1]: 1
L[j],L[j+1]=L[j+1],L[j] 2
Belgacem Hajji ~ 74 ~
D’où :
𝑛−2
𝑛2 𝑛
𝑇(𝑛) = ∑(𝑛 − 𝑖 − 1) + 𝑐𝑠𝑡𝑒 = − + 𝑐𝑠𝑡𝑒
2 2
𝑖=0
• Implémentation :
• Analyse de complexité :
Belgacem Hajji ~ 75 ~
Le temps d’exécution est donné par :
𝑛−1
𝑛2 𝑛
𝑇(𝑛) = ∑ 𝑖 + 𝑐𝑠𝑡𝑒 = 1 + 2 + ⋯ + 𝑛 − 1 = + + 𝑐𝑠𝑡𝑒
2 2
𝑖=1
O(T(n))=O(n²)
• Implémentation :
Belgacem Hajji ~ 76 ~
• Analyse de complexité :
Belgacem Hajji ~ 77 ~
Le temps d’exécution du tri est donné par :
𝑛
𝑇(𝑛) = 2𝑇 ( ) + 𝑛 + 𝑐𝑠𝑡𝑒
2
On en déduit que : a=2, b=2 et k=1. Or bk=21 = a : O(T(n))=O(nkLogb(n)) = O(nlog2(n)). Le tri
par fusion est un algorithme quasi linéaire plus rapide que les tris abordés précédemment.
n n² nlogn
100 10000 665
1000 1000000 9966
100000 10000000000 1660964
• Implémentation :
- Fonction de partitionnement : On va choisir le 1er élément de la liste L comme pivot.
- La fonction du tri :
Belgacem Hajji ~ 78 ~
• Analyse de complexité :
D’où :
𝑛
𝑇(𝑛) = 2𝑇 ( ) + 2𝑛 + 𝑐𝑠𝑡𝑒
2
O(T(n))=O(nlog2(n)) : Le tri rapide est un tri quasi linéaire.
Belgacem Hajji ~ 79 ~
Chapitre 11 : Les fichiers en python
10.1. Introduction
On appellera fichier toute collection de données (textuelles ou binaires) enregistrée sur un
support physique (un disque dur, une clé usb, etc). Un fichier peut être accessible en lecture
seule, ou en lecture/écriture. Pour pouvoir être consulté et/ou modifié, il doit être ouvert
d’abord, puis fermé ensuite (mais Python se charge souvent de fermer le fichier lui-même quand
tout est terminé).
Python propose la classe file pour modéliser la notion de fichier, et pour faciliter les
interactions. Tout dépend beaucoup du système d’exploitation (Windows, Linux, Mac OSX,
etc) mais la classe file offre des méthodes qui permettent de masquer les différences. Dans tous
les cas, un fichier possède un nom et réside (ou est créé) dans un dossier. Pour accéder au
fichier, on a besoin de son nom et du chemin d’accès à son dossier. Par défaut, il s’agit du
“dossier courant”, c’est-à-dire celui où réside le script en cours d’exécution (ou alors le dernier
script utilisé, ou celui de l’application IDLE si on travaille en mode interactif).
Dans toute la suite de cette section, on suppose qu’on se place uniquement dans le “dossier
courant”. Les fichiers seront donc désignés par leur nom, simplement, sans indication d’un
chemin d’accès. Mais si on veut un contrôle précis sur les dossiers, on importera le module os
(pour “operating system”) qui fournit quelques fonctions utiles :
On ne confondra pas les objets de type file tels qu’ils sont créés et manipulés par Python (voir
plus loin) avec la concrétisation “physique” de ces fichiers sur le disque. En fait, les objets file
sont des abstractions permettant de désigner commodément ces fichiers “physiques”.
Belgacem Hajji ~ 80 ~
- ‘a’ : append : C’est un mode semblable au mode ‘w’ sauf qu’on n’écrase pas
l’ancien contenu du fichier, mais on ajoute à la fin du fichier de chaines de
caractères nouvelles.
Le type d’accèes :
- ‘t’ : le fichier est considéré comme une suite de caractères ascii : c’est le mode
texte
- ‘b’ : le fichier est considéré comme une suite d’octets : c’est le mode binaire.
• Ecriture :
Syntaxe : varFichier.write(chaine_de_caracteres)
La commande write enregistre la chaine dans le fichier et retourne la longueur de cette
dernière.
Il faut toujours penser fermer le fichier à la fin de son utilisation surtout s’il est ouvert
en mode ‘w’ ou ‘a’ pour que les modifications soient enregistrées.
La commande closed permet de tester si le fichier est fermé avec succès, son résultat est
booléen.
Une fois créée, on peut aller consulter le contenu de notre fichier dans le répertoire de
Python :
Belgacem Hajji ~ 81 ~
Le contenu du fichier est le suivant :
Les deux chaines sont écrites l’une à côté de l’autre sans retour à la ligne.
NB : on peut utiliser un conteneur pour enregistrer les lignes qu’on désire stocker dans
un fichier :
Belgacem Hajji ~ 82 ~
• Lecture :
Avant de pouvoir lire le contenu d’un fichier, on doit l’ouvrir en mode lecture texte avec
la commande open(nomFichier,’rt’) . La consultation du contenu peut s’effectuer de
trois manières :
- VarFichier.read() : Affiche le contenu intégral du fichier :
Belgacem Hajji ~ 83 ~
- VarFichier.readlines() : retourne une liste constituée des lignes du fichier :
• Ajout à la fin :
Syntaxe : VarFichier=open(NomFichier,’a’)
Belgacem Hajji ~ 84 ~
• Lecture d’un fichier binaire :
On utilise toujours le module pickle et plus précisément sa commande load avec la
syntaxe suivante : pickle.load(varFichier). Cette commande retourne le 1er objet du
fichier binaire. On peut l’utiliser pour lire autant d’objet qu’on le souhaite à condition
de ne pas dépasser la taille du fichier, sinon une exception EOFError sera levée :
Belgacem Hajji ~ 85 ~
Chapitre 12 : Les tableaux nupmy – simulation
numérique en Python
11.1. Introduction
Les tableaux sont des conteneurs spéciaux en Python, qui ne peuvent être utilisés que si le
module numpy est installé et chargé. On distingue les tableaux à une seule dimension et les
tableaux à deux dimensions appelés aussi matrices. Python autorise un certain nombre de
types pour les tableaux à savoir : les entiers, les booléens, les réels et les nombres
complexes. Les tableaux sont des conteneurs séquentiels (ordonnés) et modifiables
(mutables).
• Type des éléments : L’attribut dtype permet de renvoyer le type des éléments
• Tableaux spéciaux :
Vides
Belgacem Hajji ~ 86 ~
A partir d’un intervalle :
• Accès : Le même système d’indexation des listes est réservé pour les tableaux :
Belgacem Hajji ~ 87 ~
11.3. Les tableaux à deux dimensions
Chaque élément d’un tableau à deux dimensions est accessible à partir de deux indices, le
premier est l’indice sur les lignes et le deuxième sur les colonnes.
• Tableaux de 0 ou de 1 :
• Nombre d’éléments
• Dimension
• Accès :
Un élément
Une ligne
Une colonne
Belgacem Hajji ~ 88 ~
• Ajout d’une ligne
• Modification
Belgacem Hajji ~ 89 ~
• Somme, produit et moyenne des éléments :
Belgacem Hajji ~ 90 ~