Académique Documents
Professionnel Documents
Culture Documents
Alain Satabin
Lycée Monge
Charleville-Mézières
1 Préliminaires indispensables 4
1.1 De quoi est-il question ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1.1 Dénition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Variables et opérateurs 6
2.1 Utilisation des variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3.3 Transtypage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3 Structures de contrôle 10
3.1 Traitement conditionnel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.1.3 Imbrication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2.3 Imbrication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4 Les tableaux 15
4.1 Deux exemples pour se faire une idée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.2 La théorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
5 Procédures et Fonctions 20
5.1 La sous-traitance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.1.1 Pourquoi ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.1.5 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.2.1 La procédure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.2.2 La fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6 La récursivité 30
6.1 La magie de l'auto-référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
A.2 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
A.4 Aectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
A.6 Saisie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
A.7 Achage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
B.2 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
D.1.1 Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
D.2.1 Déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
E.2 Fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Les instructions composant un algorithme sont susamment simples pour être compréhensibles par l'exécu-
tant (homme ou machine). On les appelle alors des instructions élémentaires.
Logique et non intelligente signie que l'exécutant (homme ou machine) doit appliquer strictement les
instructions les unes après les autres, sans se poser la question de la justesse du résultat, ni de la façon dont
il est obtenu. Si l'algorithme est bien fait, le résultat est correct dans tous les cas de gure !
Le concepteur d'un algorithme (donc vous !) doit dominer l'action globale à réaliser et doit en même temps
être capable de la décomposer en une suite d'actes élémentaires compréhensibles par l'exécutant qui, lui, est
un profane dans le domaine. Cela n'est pas toujours une opération aisée !
Exemple 4 : Un itinéraire.
Dès l'école primaire vous avez fréquemment manipulé la notion d'algorithme. Lorsque, par exemple, à partir
des tables d'addition des nombres à 1 chire vous avez appris à additionner deux nombres quelconques . . .
et ensuite en apprenant à faire des multiplications.
En géométrie vous avez également rencontré bon nombre d'algorithmes : par exemple en apprenant le pro-
tocole de construction d'une médiatrice d'un segment avec une règle et un compas.
Article 2 : Décomposez votre résolution du problème en actions de plus en plus simples . . . jusqu'à atteindre
un niveau élémentaire que l'exécutant comprendra
Article 6 : Testez votre algorithme à la main sur des cas particuliers simples
4
1. les variables sont à un algorithme ce que les personnages sont à un roman ou un lm
2. l'exécutant possède un vocabulaire limité et est obéissant, mais bête !
3. cela n'a aucune utilité pour le bon fonctionnement de l'algorithme, mais le rend plus lisible et plus compréhensible
4. le concepteur doit être capable de simuler le comportement d'un exécutant bête et discipliné !
5. rien de plus horrible et incertain qu'un algorithme bidouillé et rustiné de toutes parts !
En 1936, le mathématicien anglais Alan Turing publie un document sur la résolution des problèmes
calculables par une machine virtuelle simple : la machine de Turing. Considérée comme le premier ordinateur
conceptualisé, sa machine est universelle et peut résoudre tout problème se traduisant en algorithme à partir
d'un nombre très réduit d'instructions élémentaires.
C'est avec le mathématicien américano-hongrois John von Neumann qu'il réalisera et fera fonctionner le
21 juin 1948 le premier ordinateur : le Manchester Mark I. Pour la circonstance il crée le premier langage
informatique, le short code, doté d'une cinquantaine d'instructions.
Une machine en général et un ordinateur en particulier sont des objets idiots qui se contentent de faire à la
lettre ce que vous lui dictez de faire. Les mythes d'ordinateurs intelligents (Matrix, 2001 Odyssée de l'espace,
R2D2, etc.) restent pour l'instant du domaine de la science-ction !
8 9
Un ordinateur n'est qu'un ensemble de circuits logiques traitant des informations binaires . Une façon
de s'adresser à lui pour lui faire remplir une tâche est de lui parler dans son langage primaire : le langage
machine 10
.
C'est pour cette raison que se sont développés des logiciels appelés langages de programmation 11 qui sont en
quelque sorte des interprètes traduisant des instructions claires en langage machine.
Lorsqu'il est traduit dans un langage de programmation, l'algorithme s'appelle un programme et son concep-
teur le programmeur ou développeur s'il s'agit de projets plus sophistiqués.
Le but de ce cours est d'apprendre à écrire des algorithmes, c'est à dire à décomposer une action compliquée
en instructions simples. Cet exercice est a priori totalement indépendant d'un langage de programmation et
reste un travail de papier . Nous écrirons nos algorithmes dans un pseudo-langage et, pour les tester sur
une machine, nous les traduirons dans un langage de programmation.
Cette mise en ÷uvre d'un algorithme sur une machine s'appelle l' implémentation.
12
Nous choisirons le Python 3 . La syntaxe liée à ce langage gure en annexe de ce document et sera vue
au fur et à mesure du déroulement du cours d'algorithmique.
Pour cela il utilise ce qu'on appelle une variable, comparable à une boîte dans laquelle on peut déposer ou y
lire une valeur. An de s'y retrouver, cette boîte est étiquetée par un nom de variable.
Les noms de variables ne doivent pas comporter d'espace et ne peuvent pas commencer par
un chire. D'une façon générale, on se limitera aux caractères alphanumériques non accentués et au tiret
de soulignement.
La bonne lisibilité d'un algorithme étant fondamentale, il est important de choisir des noms de variable
reétant le rôle joué par la variable en question.
Par exemple voici quelques noms de variable valides : N ; Date ; Resultat_01 ; NbreValeurs ; Nom_Famille ...
Le fait d'attribuer un contenu à une variable s'appelle l' aectation. Dans l'écriture d'un algorithme, elle est
symbolisée par ← selon la syntaxe :
N omV ariable ← V aleur
signiant que la quantité située à droite de la èche est évaluée, puis stockée dans la variable nommée
NomVariable, remplaçant ainsi son ancien contenu.
Lors de la mise au point d'un algorithme ou de la recherche d'erreurs, il est extrêmement important de savoir
le tester à la main . Cela consiste à simuler la machine en suivant l'algorithme pas à pas et en tenant à
jour un tableau de contenu des variables. On parle alors du tracé pas à pas de l'algorithme.
Voici dans le tableau de droite un exemple de tracé de la suite d'instructions écrite à gauche :
instructions A B C D
Début début n.a. n.a. n.a. n.a.
A←2
←2
A 2 n.a. n.a. n.a.
B←5
←5
B 2 5 n.a. n.a.
C ← 12
C ← 12 2 5 12 n.a.
D←A+B
D ← A + B 2 5 12 7
C←A+C
C ← A + C 2 5 14 7
B←B+C
B ← B + C 2 19 14 7
A←CA
A ← C A 12 19 14 7
D←B×DC
Fin D ← B × D C 12 19 14 119
n 12 19 14 119
On distingue pour pour commencer 4 types de variables : entier ; réel ; chaîne de caractères ; booléen.
Les deux premiers types servent à manipuler les nombres entiers ou "à virgule" (Z ou R)
Les chaînes de caractères sont conventionnellement écrites entre guillemets dans un algorithme.
Une variable de type booléen ne peut accueillir que l'une des deux valeurs Vrai ou Faux. Par exemple, si dans
un algorithme la variable Test est déclarée booléenne, on pourra trouver des instructions du genre Test ← Vrai
ou encore Test ← (Annee > 1582) 1 .
Les aectations opérées par l'algorithme doivent évidemment être cohérentes avec les types déclarés ! Par
exemple, si les variables A et B sont déclarées entières, les instructions A ← 3.7 ou A ← B/2 sont des erreurs.
De même, si A une variable de type entière et B de type réelle, l'aectation A ← B engendre une erreur, alors
que B←A est une instruction correcte.
Il convient de faire la diérence entre "3.14" (chaîne de caractères) et 3.14 (nombre réel) ou entre 3.0 (nombre
réel) et 3 (nombre entier)
Certaines fonctions, comme la partie entière ou l'arrondi à 0 décimale, ont la faculté de transformer un
nombre réel en nombre entier et pourront s'avérer fort utiles.
D'autres opérations de transtypage permettent de transformer une chaîne de caractères en nombre et ré-
ciproquement. Lors du transtypage d'une chaine en nombre, il est nécessaire que le contenu de la chaine
soit cohérent avec ce qu'on veut en faire ! Par exemple le transtypage de la chaine "3.14" en nombre entier
engendre une erreur.
Avant leur utilisation, les variables doivent toujours être initialisées, soit par une saisie (voir plus
loin), soit par l'aection d'une valeur xée.
La lecture d'une donnée saisie au clavier par l'utilisateur et son stockage dans une variable s'écrit :
NomVariable ← saisir()
En arrivant sur cette instruction, le programme se met en attente d'une entrée. L'utilisateur tape quelque
chose au clavier et le valide, provoquant le stockage de la valeur tapée dans la variable NomVariable et la
reprise de l'algorithme à l'instruction suivante.
...
A←2
B←5
Acher("A vaut ",A," , B vaut ",B," et leur somme vaut ",A+B)
...
provoquera la sortie à l'écran de : A vaut 2 , B vaut 5 et leur somme vaut 7
On peut provoquer une saisie avec l'achage d'un message grâce à l'instruction :
Algorithme : Ache_Double
Rôle : calculer le double d'un entier donné
Entrées : Un nombre entier A
Sortie : achage de 2×A
Variables : entier : A
Début
A ← saisir("Donnez-moi un nombre entier : ")
Acher("le double de votre nombre vaut ",2×A)
Fin
des commentaires an d'être facilement lisible et compréhensible. On signalera un commentaire par le
symbole #. Tout ce qui suit ce symbole sur la même ligne est ignoré par la machine.
L'addition, la soustraction et la multiplication restent valides. L'élévation en puissance n'est cohérente que si
la puissance est entière et positive (sinon le résultat n'est plus nécessairement entier). Quant à la division, elle
doit s'adapter au cas entier et nous disposons de deux opérateurs : div et mod qui donnent respectivement
le quotient et le reste de la division euclidienne.
Une seule opération est possible sur les chaînes : la concaténation, qui consiste à les mettre bout à bout et
que nous symboliserons par +.
Par exemple après la séquence d'instructions (A et B étant des variables de type chaîne) :
...
A ← "Salut"
B ← A+"Tu vas bien ?"
...
la variable B contiendra : "SalutTu vas bien ?".
Nous retrouvons ici les opérateurs classiques (qui vont être) vus en logique : ET, OU et NON.
Nous nous limiterons à quelques manipulations correspondant à des fonctions existant dans pratiquement
tous les langages de programmation :
2.3.3 Transtypage
L'instruction 123 + 456 renvoie 579 alors que "123"+"456" renvoie la chaîne "123456".
Il nous sera souvent utile de pouvoir convertir une chaîne en un nombre, ou réciproquement :
Si (condition) Alors
action si condition vraie . . .
Fin Si
La condition est une phrase logique ou une variable booléenne. Dans le cas où la condition n'est pas réalisée,
l'action est ignorée et on passe directement à l'instruction suivant le Fin Si.
Par exemple, la séquence suivante ache le plus grand nombre pair inférieur ou égal au nombre entier saisi :
...
A ← saisir("Tapez un entier positif : ")
B←A
Si ((A mod 2) = 1) Alors
acher("votre nombre est impair")
B←A1
Fin Si
acher("Le plus grand entier pair inférieur ou égal à votre nombre est : " , B)
...
Et, pour information, son implémentation en Python 3 est :
syntaxe python 3
...
A = int(input( "Tapez un entier positif : "))
B = A
if ((A % 2) == 1) :
print("votre nombre est impair")
B = A 1
# fin Si
print("Le plus grand entier pair inférieur ou égal à votre nombre est : " , B)
...
Le principe reste identique au traitement simple, mais on est dirigé vers une autre suite d'instructions lorsque
la condition n'est pas réalisée. La structure est la suivante :
Si (condition) Alors
action si condition vraie . . .
Sinon
action si condition fausse . . .
Fin Si
Notons que cette fois une des deux actions, et une seule, sera exécutée.
...
acher("Donnez-moi deux nombres réels : ")
A ← saisir("A = ")
B ← saisir("B = ")
Si (A=B) Alors
acher("Les deux nombres saisis sont égaux")
Sinon
acher("Les deux nombres saisis sont diérents")
Fin Si
...
Et son implémentation en Python 3 :
syntaxe python 3
...
print("Donnez-moi deux nombres réels : ")
A = float(input("A = "))
B = float(input("B = "))
if (A == B) :
print("Les deux nombres saisis sont égaux")
else :
print("Les deux nombres saisis sont différents")
# Fin Si
...
3.1.3 Imbrication
Les structures peuvent évidemment être imbriquées les unes dans les autres an de créer des sous-cas. Par
exemple la séquence suivante saisit l'âge de l'utilisateur (variable entière positive Age ) et suivant les cas
détermine s'il est en âge de voter ou de conduire (variables Voter booléenne et Conduire de type chaîne).
...
Age ← saisir("Quel est votre âge ? ")
Si (Age > 18) Alors
# il peut voter et conduire seul s'il a le permis
Voter ← Vrai
Conduire ← "possible seul"
Sinon
# il est mineur donc ne peut pas voter
Voter ← Faux
Si (Age > 16) Alors
# peut éventuellement être en conduite accompagnée
Conduire ← "possible accompagné"
Sinon
# moins de 16 ans, il ne peut pas conduire
Conduire ← "impossible"
Fin Si
Fin Si
...
On notera que l'indentation est fondamentale à la compréhension des imbrications !
Lorsque l'instruction "Tant Que" est rencontrée, la condition est évaluée. Si elle est réalisée, on exécute
l'action spéciée et lorsque le "Fin Tant Que" est rencontré on revient au "Tant Que" et on recommence. Si
elle n'est pas réalisée, on ignore l'action et on passe directement à l'instruction suivant le "Fin Tant Que"
(sortie de boucle).
...
n ← 65
Tant que (n 6 90)
acher(carASCII(n))
n ← n+1
Fin Tant Que
...
et l'implémentation correspondante en Python 3 :
syntaxe python 3
...
n = 65
while (n <= 90) :
print(chr(n))
n = n+1
# Fin Tant Que
...
On notera l'importance de l'initialisation des variables utilisées dans la condition : si n était initialisé par 95,
les instructions situées dans la boucle ne seraient jamais exécutées. Par ailleurs, les variables utilisées dans
la condition doivent évoluer dans le contenu de la boucle . . . faute de quoi on ne pourrait plus en sortir une
fois rentré ! (enlevez le n←n+1 dans l'exemple précédent et vous verrez).
Et une mauvaise aectation peut avoir le même eet. Le seul moyen de ne pas tomber dans le piège des
boucles innies (ou jamais parcourues !) est de tester son algorithme à la main sur des cas simples . . . et là
les failles apparaissent. Essayez sur le morceau d'algorithme suivant :
...
n←5
Tant que n 6= 10
acher(n)
n ← n+2
Fin Tant Que
...
Vous remarquerez dans ce cas que le problème peut disparaitre de diérentes façons :
par exemple en remplaçant n ← n + 2 par n ← n + 1
ou bien en remplaçant n ← 5 par n ← 4
ou encore en remplaçant la condition n 6= 10 par n < 10
Il s'agit d'une boucle "comptée" dans laquelle le programme s'occupe de tout : initialisation de la variable
de comptage, son évolution et la condition d'arrêt. Sa structure est la suivante :
Lorsque l'instruction "Pour" est rencontrée la première fois, le protocole suivant s'enclenche :
VariableComptage ← ValeurDebut
Tant que (VariableComptage 6 ValeurFin)
action . . .
VariableComptage ← VariableComptage + 1
Fin Tant que
instruction suivante
Quatre points extrêmement importants sont à noter :
...
Pour n allant de 65 à 90
acher(carASCII(n))
n suivant
...
ce qui donnerait une fois implémenté :
syntaxe Python 3
...
for n in range(65,91) :
print(ch(n))
# n suivant
...
Là encore, les structures peuvent être imbriquées les unes dans les autres. Par exemple, la séquence suivante
ache les tables de multiplications de 2 à 9 :
...
Pour i allant de 2 à 9
acher("Table des ",i," :")
Pour j allant de 1 à 10
acher(i , " fois " , j , ' = ' , i×j)
j suivant
i suivant
...
ce qui, une fois implémenté, donnera :
syntaxe python 3
...
for i in range(2,10) :
print("Table des ",i," : ")
for j in range(1,11) :
print(i , " fois " , j , ' = ' , i*j)
# j suivant
# i suivant
...
Un relevé de notes de 5 devoirs réalisés dans une classe de 31 étudiants est un tableau à deux dimensions :
4.2 La théorie
4.2.1 Des variables à tiroirs . . .
Un tableau est comparable à un ensemble de boîtes dans lesquelles on va stocker des informations.
Il est repéré par un nom qui obéit aux même règles que les noms de variable ( Nom_Jour dans l'exemple 1
et Notes dans l'exemple 2).
Il possède aussi un type qui dénit le type des données qui y vont être stockées (chaine de caractères dans
l'exemple 1 et entier dans l'exemple 2).
Un tableau à une dimension est comparable à une suite de cases, chacune repérée par un indice
(voir exemple 1)
Un tableau à deux dimensions est comparable à un meuble à tiroirs, chacun repéré par un
indice de rangée (ou ligne) et un indice de colonne (voir exemple 2).
Chaque "case" ou "tiroir" s'appelle une cellule et les indices commencent à 0. Pour repérer une cellule précise
dans un tableau, on place son indice entre crochets derrière le nom du tableau.
Un tableau à 2 dimensions est en fait un tableau à 1 dimension (les lignes) dont les cellules sont des tableaux
à 1 dimension (les colonnes).
Dans l'exemple 2, on peut considérer le relevé de notes comme un tableau à 1 dimension et 31 cellules (les
étudiants), chaque cellule étant un tableau à 1 dimension et 5 cellules (les notes). Notes est un tableau à
deux dimensions et N otes[3] est un tableau à 1 dimension et 5 cellules : les notes du quatrième étudiant.
La taille d'un tableau est son nombre de cellules quand il s'agit d'un tableau à 1 dimension, et son nombre
de lignes et de colonnes pour un tableau à 2 dimensions.
Lors de la déclaration du tableau, la taille sera précisée si elle est prévisible et gée.
La fonction long() qui a été dénie sur les chaines de caractères sera aussi utilisable pour les tableaux :
long(Nom_Jour) renvoie 7
long(Notes) renvoie 31
long(Notes[3]) renvoie 5
Dans l'exemple 1, les 7 lignes d'initialisation pouvaient être remplacées par une seule :
Nom_Jour ← ["lundi","mardi","mercredi","jeudi","vendredi","samedi","dimanche"]
De même pour un tableau à 2 dimensions. Si vous voulez par exemple que le tableau matrice contienne :
3 1 6 0
5 2 4 2
matrice ← [[3,1,6,0],[5,2,4,2]]
4.2.4 Extension d'un tableau
La taille d'un tableau n'est pas toujours prévisible dès le début d'un algorithme. On peut dans ce cas déclarer
un tableau qui sera initialisé vide . . . c'est à dire avec 0 cellule, et au gré des besoins on ajoute une nouvelle
cellule au tableau en y stockant une valeur. La taille du tableau évolue en conséquence.
On conviendra que la procédure étendre(N omT ableau,V aleur) ajoute une cellule au tableau N omT ableau
avec valeur comme contenu.
Par exemple, si le tableau d'entiers machin est aecté par [2,5,4], l'instruction ci-dessous augmentera sa taille
de 1 et il contiendra ensuite [2,5,4,8].
...
étendre(machin,8)
...
ce que je vous engage à tester en Python 3 avec les instructions suivantes :
éditeur python 3
>>> machin=[2,5,4]
>>> len(machin)
3
>>> machin.append(8)
>>> len(machin)
4
>>> machin
[2,5,4,8]
L'algorithme qui suit a pour but de tester le générateur aléatoire. Il simule 600 lancers de dé et ache, pour
chaque face, le nombre de fois où elle a été obtenue en agrémentant cela d'un diagramme bâton horizontal.
ALGORITHME : Lancers de dé
ROLE : Simuler 600 lancers de dés et acher sous forme d'histogramme les résultats pour chaque face
ENTREES :
SORTIE : L'eectif d'obtention de chaque face et une présentation en diagramme bâton horizontal
VARIABLES i , j , lancer : entiers
eectif [0 .. 5] : tableau d'entiers
DEBUT
eectif ← [0,0,0,0,0,0] # initialisation du tableau d'eectifs à 0
# ===== simulation des lancers
pour i allant de 0 à 599 # 600 lancers : boucle comptée de 600 tours
lancer ← PartieEntiere(6*alea()) # nombre entier aléatoire de 0 à 5
eectif[lancer] ← eectif[lancer]+1 # comptabiliser le résultat du lancer
i suivant
# ===== achage des résultats
acher("Face <tab> Eectif <tab> Diagramme") # titres
pour i allant de 0 à 5 # i varie de 0 à 5 ; face n(i+1)
acher(i+1,<tab>,eectif[i],<tab>,<rester sur la ligne>) # numéro de face et son eectif
pour j allant de 0 à eectif[i] 1 # acher autant . . .
acher("=",<rester sur la ligne>) # . . . de signes "=" que . . .
j suivant # . . . l'eectif de la face
acher() # va à la ligne
i suivant
FIN
# ALGORITHME : Lancers de dé
# ROLE : Simuler 600 lancers de dés et
afficher sous forme d'histogramme les résultats pour chaque face
# ENTREES : -
# SORTIE : L'effectif d'obtention de chaque face et une présentation en diagramme bâton horizontal
# VARIABLES i , j , lancer : entiers
# effectif [0 .. 5] : tableau d'entiers
# importation des modules utiles
from math import *
from random import *
# DEBUT
effectif = [0,0,0,0,0,0] # initialisation du tableau d'effectifs à 0
# ===== simulation des lancers
for i in range(0,600) : # 600 lancers : boucle comptée de 600 tours
lancer = floor(6*random()) # nombre entier aléatoire de 0 à 5
effectif[lancer] = effectif[lancer]+1 # comptabiliser le résultat du lancer
# i suivant
# ===== affichage des résultats
print("Face","Effectif","Diagramme",sep="\t") # titres
for i in range(0,6) :
# i varie de 0 à 5; face n(i+1)
print(i+1,effectif[i],"",sep="\t",end="") # numéro de face et son effectif
for j in range(0,effectif[i]) : # afficher autant ...
print("=",end="") # ... de signes "=" que ...
# j suivant # ... l'effectif de la face
print() # va à la ligne
# i suivant
# FIN
L'algorithme qui suit saisit un certain nombre de notes et les stocke dans un tableau. La n de la saisie est
détectée par l'entrée d'une valeur supérieure strictement à 20 ou strictement négative.
Une fois la série terminée, l'algorithme détermine (et ache) le nombre de notes, les notes extrêmes et la
moyenne arrondie à 1 décimale.
Je vous laisse le soin d'analyser le contenu de cet algorithme et de l'implémenter en Python 3 en le sauve-
gardant sous le nom Cours_4_3_2.py.
Le triangle de Pascal est un tableau de nombres permettant d'écrire le développement de (a + b)n . Il sera
approfondi en cours de dénombrement et seule sa construction nous intéresse ici.
En voici les premières lignes (complétées par des 0 pour en faire un tableau rectangulaire) :
1 0 0 0 0 0 0
1 1 0 0 0 0 0
1 2 1 0 0 0 0
1 3 3 1 0 0 0
1 4 6 4 1 0 0
1 5 10 10 5 1 0
1 6 15 20 15 6 1
L'algorithme suivant saisit le nombre de lignes désiré et les achent. Faites-en un tracé pas à pas pour la
valeur n = 4 an de bien comprendre son fonctionnement. Il est également conseillé de l'implémenter en
Python 3 et de le sauvegarder sous le nom Cours_4_3_3.py.
5.1 La sous-traitance
5.1.1 Pourquoi ?
Le concept de langage procédural, consistant à dénir des sous-algorithmes qui peuvent être appelés de façon
indépendante, présente trois avantages non négligeables :
Le premier point est la clé de l'analyse dite descendante d'un problème. La tâche globale est subdivisée
en sous-problèmes conés à des équipes diérentes qui peuvent à leur tour, si elles en éprouvent le besoin,
les subdiviser à nouveau. Cette structure emboîtée en poupées russes doit évidemment s'assurer que
l'assemblage nal des briques conçues par les diérentes équipes et sous-équipes conduit de façon cohé-
rente au résultat attendu. Chaque sous-algorithme doit donc obéir à un cahier des charges précis édicté par
l'algorithme qui fait appel à lui.
sous-algorithme sous_probleme_1_a()
( . . .)
n sous-algorithme
sous-algorithme sous_probleme_1_b()
( . . .)
n sous-algorithme
sous-algorithme sous_probleme_1()
appeler sous_probleme_1_a()
appeler sous_probleme_1_b()
n sous-algorithme
sous-algorithme sous_probleme_2()
( . . .)
n sous-algorithme
algorithme probleme_general
( . . .)
DEBUT
( . . .)
appeler sous_probleme_1()
appeler sous_probleme_2()
( . . .)
FIN
U22 - Algorithmique - Chapitre n°5 Page 20/40
5.1.3 Éviter la répétition
Le deuxième point est dicté par un souci d'optimisation et permet d'éviter la répétition de séquences d'ins-
tructions identiques ou similaires. Il n'est pas rare, dans la résolution d'un problème, d'avoir à faire appel
plusieurs fois à une même tâche. Dans ce cas, un sous-algorithme exécutant cette tâche sera déni et appelé
plusieurs fois. D'un point de vue structure algorithmique, cela ressemble à cela :
sous-algorithme tache_a_repeter()
( . . .)
n sous-algorithme
algorithme probleme_general
( . . .)
DEBUT
( . . .)
appeler tache_a_repeter()
( . . .)
appeler tache_a_repeter()
( . . .)
appeler tache_a_repeter()
( . . .)
FIN
Il est clair que les sous-algorithmes doivent être dénis avant qu'un appel ne leur soit destiné. Par exemple
dans le premier encadré, déclarer sous_probleme_1 avant de déclarer sous_probleme_1_a engendrerait une
erreur. Comme dans un roman, il faut que les personnages soient présentés avant de les faire intervenir.
Par ailleurs, lors de l'appel d'un sous-algorithme, tout se passe comme si les instructions du sous-algorithme
étaient insérées à l'endroit de l'appel. Il est clair qu'au retour de l'appel, la machine reprend son histoire à
l'instruction qui suit l'appel.
1 sous-algorithme truc()
( . . .)
n sous-algorithme 2
3 sous-algorithme machin()
( . . .)
n sous-algorithme 4
5 algorithme probleme_general
( . . .)
DEBUT
( . . .)
appeler machin() 6 7
( . . .)
appeler truc() 8 9
( . . .)
appeler machin() 10 11
( . . .)
FIN 12
Les noms donnés aux sous-algorithmes répondent aux mêmes exigences que les noms de variables : caractères
alphanumériques non accentués et trait de soulignement, le premier caractère étant obligatoirement une lettre.
Le nom doit se terminer par un couple de parenthèses (éventuellement vide) dont nous verrons l'utilité plus
loin.
Voici un exemple de procédure qui envoie à l'écran un message d'erreur séparé par des lignes vides lorsqu'elle
est invoquée :
PROCÉDURE erreur()
passer une ligne
acher("Erreur fatale rencontrée")
passer une ligne
FIN PROCÉDURE
ALGORITHME principal
( . . .)
DEBUT
( . . .)
Si (condition_1) Alors
erreur()
Sinon
Si (condition_2) Alors
(. . .traitement du problème . . .)
Sinon
erreur()
Fin Si
Fin si
(. . .)
FIN
Cet algorithme appellera la procédure et achera le message d'erreur lorsque condition_1 sera remplie et,
quand elle ne l'est pas, si condition_2 ne l'est pas non plus.
5.2.2 La fonction
La fonction est un sous-algorithme qui renvoie une valeur. La dernière instruction d'une fonction sera le
renvoi de la valeur en question.
Contrairement à la procédure, elle n'est pas en elle-même une instruction mais doit intervenir, comme une
variable, dans une instruction de l'algorithme qui l'invoque.
Prenons par exemple un algorithme qui a régulièrement besoin d'une valeur de π à 5 décimales. Plutôt que
de taper à chaque fois cette valeur, on peut créer une fonction dont le nom est plus court à écrire :
Un autre intérêt dans cette façon de procéder est que si par la suite on a besoin de résultats plus précis, il
sut d'augmenter le nombre de décimales à un seul endroit : dans le corps de la fonction.
Il est clair que les deux exemples donnés ci-dessus sont quelque peu gés et il serait bon de leur donner un
peu de souplesse.
Prenons l'exemple de la procédure achant un message d'erreur. Elle pourrait être modiée de la façon
suivante :
PROCÉDURE erreur(message)
ARGUMENT message : chaîne de caractères
RÔLE acher un message d'erreur
passer une ligne
acher("Erreur fatale rencontrée : " , message)
passer une ligne
FIN PROCÉDURE
ALGORITHME principal
( . . .)
DEBUT
( . . .)
Si (condition_1) Alors
erreur("division par zéro")
Sinon
Si (condition_2) Alors
(. . .traitement du problème . . .)
Sinon
erreur("racine d'un nombre négatif")
Fin Si
Fin si
(. . .)
FIN
Au moment de l'appel, la valeur mise entre parenthèses est transférée dans la variable message servant
d'argument à la procédure.
Lorsque la condition_1 est remplie, on va voir s'acher Erreur fatale rencontrée : division par zéro,
et lorsque aucune condition n'est remplie, on verra Erreur fatale rencontrée : racine d'un nombre négatif.
De même, l'exemple de fonction donné précédemment serait également plus souple si le nombre de décimales
de l'arrondi n'était pas prédéterminé :
Un sous-algorithme étant en lui même un algorithme, sa rédaction répond aux même règles et doit comporter
les commentaires et déclarations usuels (rôle, variables locales, . . .). Il faut y ajouter les types des arguments
passés et, dans le cas d'une fonction, le type de la valeur retournée (qui peut aussi être un tableau).
Les arguments passés gurent entre parenthèses dans la déclaration du sous-algorithme. Ils peuvent être
de types diérents et sont représentés par des noms de variable qui seront utilisé dans le corps du sous-
algorithme. Dans le cas de plusieurs arguments, les variables sont séparées par des virgules et l'ordre dans
lequel ils sont déclarés est extrêmement important.
Par exemple l'appel bidule(3,5,Vrai,"bonjour") est correct, de même que bidule(5,3,Vrai,"bonjour") . . . qui
donnera probablement un résultat diérent du précédent (dans le premier cas n ← 3 et p ← 5 alors que dans
le second cas n←5 et p ← 3).
Par contre les appels bidule(3.2,5,Vrai,"bonjour") , bidule(3,5,"Vrai","bonjour") , bidule("3",5,Vrai,"bonjour")
ou bidule(3,5,Vrai,7) ne sont pas cohérents et engendrent une erreur.
On peut également transmettre une valeur à un sous-algorithme grâce à une variable dont le type est cohérent
avec l'argument correspondant.
Je vous laisse analyser et vérier les achages de la procédure et du programme principal suivant :
ALGORITHME principal
ROLE tester la procédure repetition
ENTREES aucune
SORTIES achage de diérents appels par la procédure
VARIABLES Nbre : entier
Interjection : chaine de caractères
DEBUT
repetition(4,"Ha") # ache à l'écran : Ha Ha Ha Ha
Nbre ← 3
repetition(Nbre,"Oui") # ache à l'écran : Oui Oui Oui
Interjection ← "Ho"
repetition(Nbre+2, Interjection) # ache à l'écran : Ho Ho Ho Ho Ho
repetition(Nbre-2, "Interjection") # ache à l'écran : Interjection
FIN
puis l'implémenter en Python 3 :
# ALGORITHME principal
# ROLE tester la procédure repetition
# ENTREES aucune
# SORTIES affichage de différents appels par la procédure
# VARIABLES Nbre : entier
# Interjection : chaine de caractères
# DEBUT
repetition(4,"Ha") # affiche à l'écran : Ha Ha Ha Ha
Nbre = 3
repetition(Nbre,"Oui") # affiche à l'écran : Oui Oui Oui
Interjection = "Ho"
repetition(Nbre+2, Interjection) # affiche à l'écran : Ho Ho Ho Ho Ho
repetition(Nbre-2, "Interjection") # affiche à l'écran : Interjection
# FIN
Le factoriel d'un entier n, noté n!, est le produit de tous les entiers de 1 à n. Par exemple 3! = 1 × 2 × 3 = 6
et 6! = 720. Voici un exemple d'écriture de cette fonction :
FONCTION factoriel(n)
ARGUMENT n : entier
TYPE RENVOYE entier
ROLE renvoie le produit de tous les entiers de 1 à n
VARIABLES i , resultat : entiers
resultat ← 1
Pour i allant de 1 à n
resultat ← resultat × i
i suivant
renvoyer resultat
FIN FONCTION
Notons que ce n'est pas la variable resultat qui est retournée, mais son contenu (une valeur numérique). Je
vous laisse le soin de vérier les achages de ce programme test utilisant la fonction factoriel :
ALGORITHME principal
ROLE tester la fonction factoriel
ENTREES aucune
SORTIES achage de diérents retours de la fonction
VARIABLES Nbre1 , Nbre2 : entiers
DEBUT
acher(factoriel(4)) # ache à l'écran : 24
Nbre1 ← 5
Nbre2 ← 2× factoriel(Nbre1 2) 1 # Nbre2 est aecté par la valeur 11
acher(factoriel(Nbre1)) # ache à l'écran : 120
acher(factoriel((3 × Nbre2) mod Nbre1)) # ache à l'écran : 6
acher(factoriel(Nbre2) div factoriel(Nbre1)) # ache à l'écran : 332640
FIN
puis de tester le tout en Python 3 :
def factoriel(n):
# TYPE fonction
# ARGUMENTS n : entier
# TYPE RENVOYE entier
# ROLE renvoie le produit de tous les entiers de 1 à n
# VARIABLES i , resultat : entiers
resultat = 1
for i in range(1,n+1):
resultat = resultat * i
# i suivant
return resultat
# FIN FONCTION
# ALGORITHME principal
# ROLE tester la fonction factoriel
# ENTREES aucune
# SORTIES affichage de différents retours de la fonction
# VARIABLES Nbre1 , Nbre2 : entiers
# DEBUT
print(factoriel(4)) # affiche à l'écran : 24
Nbre1 = 5
Nbre2 = 2*factoriel(Nbre1 2) 1 # Nbre2 est affecté par la valeur 11
print(factoriel(Nbre1)) # affiche à l'écran : 120
print(factoriel((3*Nbre2) % Nbre1)) # affiche à l'écran : 6
print(factoriel(Nbre2) // factoriel(Nbre1)) # affiche à l'écran : 332640
# FIN
Comme elles ne communiquent pas ensemble durant la mise au point des sous-projets, il est tout à fait
possible que plusieurs équipes diérentes utilisent au sein de leur sous-algorithme des variables portant le
même nom (par exemple un compteur i). Il est clair que ces variables homonymes ne doivent pas interférer
une fois les sous-algorithmes aboutés dans le projet nal.
De même, il est tout à fait possible que le chef de projet manipule dans son algorithme principal des variables
contenant des données générales importantes pour le bon fonctionnement de l'ensemble. Il serait mal venu
que les sous-algorithmes modient les contenus de ces variables . . . même si, par le plus grand des hasards,
ils manipulent des variables de même nom.
Ce qui suit n'est pas simple, c'est même assez subtil . . . mais cela joue un rôle fondamental en programmation
et demande à être bien compris !
Les variables déclarées et aectées dans l'algorithme principal sont des variables globales.
Les variables déclarées et aectées dans le sous-algorithme sont des variables locales à ce sous-algorithme.
Les arguments passés au sous-algorithme sont des variables locales et leur aectation se fait automatiquement
lors de l'appel du sous-algorithme.
ALGORITHME principal
ROLE ...
VARIABLES Nb : entier
message : chaine de caractères
DEBUT
Nb ← 3
message ← "truc"
alphonse(Nb,message)
FIN
les variables Nb et message sont des variables globales et les variables n , texte et i sont des variables locales.
A l'appel de la procédure, n et texte sont initialisées par aectation des valeurs respectives 3 et "truc" et i
est ensuite initialisé par la valeur 7.
A chaque étape du déroulement d'un algorithme, les variables globales sont visibles et leurs contenus
peuvent être utilisés par les sous-algorithmes appelés alors que les variables locales ne sont visibles que
dans le sous-algorithme où elles sont dénies. Mais bien qu'il puisse voir une variable qui lui est globale,
un sous-algorithme ne peut pas en modier la valeur.
Vériez attentivement le bien-fondé des commentaires . . . et l'achage obtenu dans l'exemple suivant :
PROCEDURE Esclave(n)
ARGUMENTS n : entier # n est une variable locale aectée lors de l'appel
VARIABLE i : entier # i est une variable locale qui doit être aectée dans la procédure
i←7 # aectation de la variable locale i
acher("i=",i ," n=",n) # ache des contenus locaux
n←i+n # aectation d'une variable locale par des valeurs locales
i ← Nb + n # aectation d'une variable locale en utilisant la variable globale Nb
acher("i=",i ," n=",n) # ache des contenus locaux
acher("Nb=",Nb) # ache un contenu global
FIN PROCEDURE # n d'existence des variables i et n
ALGORITHME Maitre
VARIABLES Nb : entier
DEBUT
Nb ← 3 # la variable globale Nb contient 3
Esclave(Nb) # appel de la procédure avec transfert du contenu de Nb dans n
acher("Nb=",Nb) # la variable Nb n'a pas été aectée par la procédure
Nb ← Nb + 5 # la variable globale Nb vaut maintenant 8
Esclave(Nb+4) # appel de procédure avec transfert de 12 dans l'argument n
acher("Nb=",Nb) # la variable Nb n'a pas été aectée par la procédure
FIN
Achage obtenu : i=7 n=3 / i=13 n=10 / Nb=3 / Nb=3 / i=7 n=12 / i=27 n=19 / Nb=8 / Nb=8
PROCEDURE Esclave(n)
ARGUMENTS n : entier # n est une variable locale aectée lors de l'appel
VARIABLE i : entier # i est une variable locale qui doit être aectée dans la procédure
i ← n + Nb # pas de problème : on peut aecter une locale en utilisant une globale
Nb ← n + i # ERREUR : la procédure n'est pas autorisée à modier une variable globale
FIN PROCEDURE # les variables i et n n'existent plus à partir de là
ALGORITHME Maitre
VARIABLES Nb : entier
DEBUT
Nb ← 3 # la variable globale Nb contient 3
Esclave(Nb) # appel de la procédure avec transfert du contenu de Nb dans n
Nb ← 3 × n # ERREUR : variable locale n invisible d'ici
ache(i) # ERREUR : variable locale i invisible d'ici
FIN
Cela étant vu, que se passe-t-il lorsque des variables globales et locales portent le même nom ?
Lorsqu'un sous-algorithme rencontre une variable locale portant le même nom qu'une variable globale, il met
le contenu de la variable globale de côté an de pouvoir le restituer en sortant.
PROCEDURE Esclave(x)
ARGUMENTS x : entier # x est une variable locale aectée lors de l'appel
VARIABLE y : entier # y est une variable locale qui doit être aectée dans la procédure
y←2 # aectation de la variable locale y
x ← x+10 # modication de la variable locale x
acher("dans la procédure : x=",x ," y=",y)
FIN PROCEDURE # restitue les valeurs initiales des variables globales x et y
ALGORITHME Maitre
VARIABLES x , y : entiers # ces variables sont globales
DEBUT
x←3 # la variable globale x contient 3
y←5 # la variable globale y contient 5
acher("dans l'algorithme principal avant l'appel de procédure : x=",x," y=",y)
Esclave(x) # appel de la procédure avec transfert du contenu de x(global) dans x(local)
acher("en premier retour de procédure : x=",x," y=",y)
x ← x+y # ce sont ici les variables globales qui sont en jeu
Esclave(x+1) # appel de procédure avec transfert de 9 dans l'argument x(local)
ache ("en second retour de procédure : x=",x," y=",y)
FIN
et va provoquer l'achage suivant (à vérier) :
Pour le fun je vous laisse le soin de vérier que l'appel en cascade suivant :
FONCTION Esclave()
VARIABLE x : entier # x est une variable locale qui doit être aectée dans la procédure
x←2 # aectation de la variable locale x
renvoyer x # retourne le contenu de la variable locale x
FIN FONCTION # restitue la valeur de x initiale à celui qui a appelé
PROCEDURE Disciple()
VARIABLE x : entier # x est une variable locale qui doit être aectée dans la procédure
x←3 # aectation de la variable locale x
acher(x) # acher le contenu de la variable locale x (globale pour Esclave)
acher(Esclave()) # ache ce que renvoie la fonction Esclave
acher(x) # acher le contenu de la variable locale x
FIN PROCEDURE # restitue la valeur de x initiale à celui qui a appelé
ALGORITHME Maitre
VARIABLES x : entiers # cette variable est globale
DEBUT
x←1 # aectation de la variable globale x
acher(x) # acher le contenu de la variable globale x
acher(Esclave()) # ache ce que renvoie la fonction Esclave
acher(x) # acher le contenu de la variable globale x
Disciple # appelle la procédure disciple
acher(x) # ache le contenu de la variable globale x
FIN
ache (sur plusieurs lignes) : 1 / 2 / 1 / 3 / 2 / 3 / 1
Au premier abord, cette dénition semble totalement stupide puisque le mot à dénir est utilisé dans sa
propre dénition. Cela nous procure le sentiment de vouloir soulever la chaise sur laquelle nous sommes
assis !
Le but est de dénir, au regard de l'organigramme, si dans l'entreprise une personne A est un supérieur d'un
individu B. La dénition donnée ci-dessus correspond à la fonction booléenne suivante :
FONCTION supérieur(A,B)
VARIABLE LOCALE test : booléen
si B est au sommet de l'organigramme alors
test ← faux # point 1
sinon
si A est le chef de service de B alors
test ← vrai # point 2
sinon
test ← supérieur(A,chef de service de B) # point 3
n si
n si
retourner test # point 4
FIN FONCTION
Astride
Boris Chakira
Traçons deux appels de la fonction superieur en repérant les individus par leur initiale.
Au travers de cette exemple, on comprend la philosophie de la chose : la fonction s'appelle elle-même avec
des paramètres à chaque fois diérents, jusqu'à ce qu'une condition d'arrêt permette de calculer un vrai
résultat et stoppe cette mise en abîme.
Il est évident que si vous expliquez à quelqu'un que pour additionner 7 et 5, il sut d'additionner 7 et 5 . . .
vous n'apportez aucun élément pour aboutir au résultat ! L'appel récursif tourne ici en boucle innie.
Pour qu'une dénition récursive se termine en un temps ni, il faut que chaque nouvel appel se fasse en
transmettant des paramètres diérents, diminuant la diculté du problème, pour cheminer vers un cas où le
résultat sera calculé sans avoir recours à un nouvel appel.
Nous avons vu dans le premier exemple qu'un même nom de variable va être utilisé moult fois. Il est donc
nécessaire, lors d'un nouvel appel, de laisser au vestiaire les contenus des variables juste avant l'appel an
de les récupérer lors du retour de l'appel. Ce problème a déjà été évoqué et détaillé à la n du chapitre
précédent.
Pour réaliser ce prodige sans s'emmêler les pinceaux, on utilise une zone mémoire de stockage, appelé pile
LIFO (Last In First Out), qui fonctionne comme un empilement d'assiettes : la dernière posée sur la pile
sera la première à resservir quand on dépilera.
Le fonctionnement de cette pile est détaillé dans l'exemple qui suit (la dernière valeur entrée est à gauche).
Dans le chapitre précédent, nous avons déjà déni cette fonction. Nous allons le refaire, cette fois de façon
récursive :
FONCTION Factoriel(n)
Argument n : entier
Type retourné : entier
Rôle : calculer le produit des entiers de 1 à n
si (n=1) alors
retourner 1
sinon
retourner Factoriel(n1) × n
n si
FIN FONCTION
Étonnant de simplicité, n'est-il pas ? Traçons précisément l'appel F actoriel(4) (qui renvoie 24) :
met 4 sans n
va dans le "sinon" du 1er appel et doit appeler F actoriel(3)
met sur la pile l'ancienne valeur de n (la pile contient [4]) et met 3 dans n
va dans le "sinon" du 2e appel et doit appeler F actoriel(2)
met sur la pile l'ancienne valeur de n (la pile contient [3 ; 4]) et met 2 dans n
va dans le "sinon" du 3e appel et doit appeler F actoriel(1)
met sur la pile l'ancienne valeur de n (la pile contient [2 ; 3 ; 4]) et met 1 dans n
la condition est remplie au 4e appel : renvoie la valeur 1
quitte le 4e appel et dépile la valeur de n : n vaut 2 (la pile contient [3 ; 4])
revient dans le calcul du "sinon" du 3e appel et renvoie la valeur 1 × 2, c'est à dire 2
quitte le 3e appel et dépile la valeur de n : n vaut 3 (la pile contient [4])
revient dans le calcul du "sinon" du 2e appel et renvoie la valeur 2 × 3, c'est à dire 6
quitte le 2e appel et dépile la valeur de n : n vaut 4 (la pile est vide)
revient dans le calcul du "sinon" du 1er appel et renvoie la valeur 6 × 4, c'est à dire 24
Il est donc indispensable de tracer l'algorithme précisément "à la main" pour des valeurs raisonnables des
paramètres an de s'assurer que le processus se termine et "remonte" après un nombre ni d'opérations.
FONCTION Pascal(n , p)
Arguments n , p : entiers
Type retourné entier
si ((p=0) OU (n=p)) alors # on est en première colonne ou sur la diagonale
retourner 1
sinon # se calcule avec la ligne précédente
retourner Pascal(n-1 , p) + Pascal(n-1 , p-1)
n si
Fin FONCTION
Une première remarque est de constater que chaque appel invoque deux fois la fonction. Analysons sous
forme arborescente l'appel P ascal(4, 2) (qui doit retourner 6) :
Pascal(4,2)
Pascal(3,2) Pascal(3,1)
1 1 1 1
Cet arbre permet de constater que les appels récursifs se terminent bien, que le résultat nal vaut bien 6,
mais aussi que la fonction P ascal(2, 1) a été calculée plusieurs fois.
Cela représente donc une redondance et une perte de temps qui devient importante lorsque nous transmettons
des valeurs plus élevées. Ici, l'utilisation de la récursivité ne s'avère pas très judicieuse !
L'histoire de ce divertissement se perd dans la nuit des temps et il est répertorié sous diérentes appellations :
1 2
tours de Hanoï, de Brahma ou de Lucas .
Un plateau de jeu comporte 3 tiges verticales et sur l'une d'entre elles sont empilés des disques de tailles
décroissantes, les deux autres étant vides (tapez "tour de Hanoï" dans un moteur de recherche d'images). Le
but est de transférer la pile de disque sur un autre pic en respectant les deux règles suivantes : on ne bouge
qu'un disque à la fois et on ne peut pas poser un disque sur un autre plus petit que lui.
Par exemple, pour transférer une pile de 3 disques du pic no 1 sur le pic no 2, on procède de la façon suivante :
Cela nous a demandé 7 manipulations. On démontre que pour transférer une pile de n disques, le nombre
minimal de manipulations est 2n − 1. Pour la petite histoire, l'anecdote rapporte que des moines d'un temple
1. Dieu créateur dans la religion Hindouiste
2. du nom du mathématicien Édouard LUCAS du XIXe siècle
L'algorithme de transport des disques se règle aisément de façon récursive. Prenons l'exemple du transfert
de 7 disques du pic 1 au pic 2. Le disque 7 étant le plus grand, n'importe quel autre peut être posé dessus. Il
sut donc de transporter les 6 autres disques du pic 1 au pic 3 en ignorant le disque 7, puis le disque 7 du
pic 1 au pic 2, puis ramener les 6 autres disques du pic 3 au pic 2 où la pile va se reconstituer sur le disque
7. Ainsi, nous voyons que si nous sommes capable de transférer une pile de 6 disques, alors nous savons le
faire pour 7. C'est le principe même de la récursivité.
Une dernière remarque avant de passer à l'algorithme : les pics étant numérotés de 1 à 3, leur somme vaut
6. Cela implique que si je veux transférer du pic a vers le pic b, le pic intermédiaire (le troisième) a pour
numéro (6 − a − b).
PROCEDURE Hanoi(n,a,b)
Arguments n , a , b : entiers
Rôle transférer n disques du pic a vers le pic b
Sortie acher la manipulation à opérer
Variable locale inter : entier
si (n=1) alors # bouger 1 disque (celui du dessus)
acher("porter un disque du pic ",a," au pic ",b)
sinon # bouger une pile de n disques
inter ← 6 a b # numéro du troisième pic
Hanoi(n 1,a,inter) # envoyer n − 1 disques vers le pic intermédiaire
Hanoi(1,a,b) # bouger le plus grand disque
Hanoi(n 1,inter,b) # ramener n − 1 disques du pic intermédiaire
n si
Fin PROCEDURE
On constate que la procédure s'appelle trois fois, mais en transmettant un nombre de disques plus petit, et
qu'elle se résout lorsque ce nombre de disque vaut 1.
def Hanoi(n,a,b):
# Type: procédure
# Arguments: n , a , b : entiers
# Rôle: transférer n disques du pic a vers le pic b
# Sortie: afficher la manipulation à opérer
# Variable locale: inter : entier
if (n==1) : # bouger 1 disque (celui du dessus)
print("porter un disque du pic ",a," au pic ",b)
else : # bouger une pile de n disques
inter = 6 a b # numéro du troisième pic
Hanoi(n 1,a,inter) # envoyer n − 1 disques vers le pic intermédiaire
Hanoi(1,a,b) # bouger le plus grand disque
Hanoi(n 1,inter,b) # ramener n − 1 disques du pic intermédiaire
fin si
# Fin PROCEDURE
Testez-là avec un appel à partir du programme principal . . . mais attention : pour transférer 10 disques, il y
a 1023 manipulations !
A.1 Interface
En lançant l'application IDLE (Python GUI) une fenêtre d'exécution Python Shell s'ouvre. Vous pouvez y
tester des lignes de commande et c'est là que les résultats de vos programmes s'acheront.
En chargeant un programme (menu Fichier) ou en créant un nouveau document, une fenêtre d'édition appa-
Ces derniers doivent être sauvegardés
raît. C'est dans celle-ci que vous pourrez taper vos programmes.
avec l'extension ".py" pour être reconnus par l'éditeur et proter de la coloration syntaxique.
A.2 Commentaires
Les commentaires doivent être précédés du symbole #. La n de ligne à partir de ce symbole est alors ignorée.
A.3 Structure générale
La structure d'un programme écrit en Python doit ressembler à ceci :
# ALGORITHME ...
# ROLE ...
# ENTREES ...
# SORTIES ...
# VARIABLES noms
: types
# DEBUT
...
instructions # commentaire
# ===== titre
instructions # commentaire
...
# FIN
A.4 Affectation
L'aectation de NomVariable se fait grâce au symbole = suivant le schéma : NomVariable = Valeur
A.5 Chaînes de caractères
Doit être placée entre ' (et si une apostrophe gure dans la chaîne on la notera \') ou encore entre ". Un
retour à la ligne peut être provoqué en insérant \n dans la chaîne.
A.6 Saisie
input(message ) ache le contenu de message, attend une saisie au clavier validée par entrée et renvoie la
saisie sous forme d'une chaîne de caractères.
Par exemple sep='*' les séparera par une étoile , sep= (chaîne vide) les accolera, sep='\t' insèrera une
tabulation entre chaque et sep='\n' les écrira en passant à la ligne à chaque fois.
De la même façon, en ajoutant en dernier argument end=chaîne vous pouvez décider comment l'achage
doit se comporter à la n de l'instruction print et, par exemple, l'empêcher d'aller à la ligne pour exécuter
l'achage suivant (en mettant la chaîne vide ou d'un espace).
B.2 Fonctions
Dans les exemples, la variable X est de type chaîne et est aectée par 'BonjouR'. Les fonctions annotées de
1 2
(*) ou de (**) nécessite le chargement d'un module spécial en début de programme.
Attention :
Si vous voulez que votre variable i de comptage aille de 5 à 10, il faudra mettre : for i in range(5,11).
L'instruction T = [2, 7, 0] crée un tableau T à 1 dimension de taille 3 donc les contenus des cellules
sont initialisés respectivement à 2, 7 et 0.
Pour créer un tableau T à 1 dimension de taille n dont tous les contenus sont initialisés à x, les instructions
suivantes sont équivalentes :
Par exemple l'instruction T = [[2, 3], [5, 6], [8, 9]] crée le tableau T à deux dimensions, 3 lignes et 2
colonnes, possédant donc 6 cellules. Le contenu de la cellule qui se trouve à la ligne d'indice 2 et à la colonne
d'indice 0 valant 8.
Pour initialiser un tableau T de n lignes et p colonnes en mettant la valeur x dans toutes ses cellules, on
écrira l'instruction suivante : T = [[x for j in range(0, p)] for i in range(0, n)]
En eet, si vous voulez lui ajouter une ligne, il faudra que l'ajout transmette un tableau à 1 dimension dont
la taille correspond au nombre de colonnes du tableau. Par exemple si le tableau T comporte p colonnes et
qu'on veut lui ajouter une ligne de "x", on écrira :
T.append([x] ∗ p)
Et si vous voulez lui ajouter une colonne, il faut cette fois ajouter une cellule par ligne et il faudra utiliser
une boucle. Par exemple si T contient n lignes et qu'on veut lui ajouter une colonne de "x", on écrira :
E.1 Procédure
def NomProcédure (liste d'arguments éventuels séparés par des virgules ) :
# type : procédure
# arguments : noms et types
# rôle : (. . .)
# variables : noms et types
suite d'instructions
# Fin de procédure
E.2 Fonction
def NomFonction (liste d'arguments éventuels séparés par des virgules ) :
# type : fonction
# arguments : noms et types
# rôle : (. . .)
# valeur renvoyée : type
# variables : noms et types
suite d'instructions
return NomDeVariable ou ValeurCalculée
# Fin de fonction