Vous êtes sur la page 1sur 181

1 Algorithmique et structures de données

CHAPITRE 1

NOTIONS FONDAMENTALES SUR L’ ALGORITHMEQUE

1.1 Le concept d’algorithme

La principale raison pour laquelle nous écrivons des programmes informatiques (logiciels) est
de chercher à utiliser les ordinateurs pour résoudre les problèmes qui nous intéressent. Les
programmes informatiques sont le seul moyen par lequel nous pouvons apprendre à la
machine à traiter un problème donné. C’est l’utilisateur qui doit communiquer à l’ordinateur
les capacités requises pour effectuer un certain traitement : L’ordinateur ne sait faire que ce
qu’on lui a appris à faire !

Les problèmes à résoudre peuvent être simples ou complexes, comme par exemple :

Calculer la somme de deux nombres,


Convertir les dollars en francs CFA,
Calculer la circonférence et la surface d’un cercle,
Calculer le plus grand diviseur commun de deux entiers,
Résoudre une équation du second degré,
Gérer les comptes clients dans une banque,
Gérer le stock des matières premières dans une entreprise manufacturière,
Gérer la caisse d’épargne d’une mutuelle ou d’une association,
Gérer les ressources humaines dans une entreprise ou une administration.

Cette liste n’est pas exhaustive mais elle nous permet tout de même d’avoir une idée de la
diversité des problèmes qui peuvent nous intéresser.

Nous résolvons tous de nombreux problèmes chaque jour. Par exemple, nous nous habillons
correctement chaque matin pour aller au travail ou à l’école, nous préparons nos repas tous les
jours, nous apprenons nos leçons tous les soirs, et ainsi de suite. Les méthodes mises en
œuvre pour résoudre de tels problèmes sont généralement informelles parce que ne reposant
pas une démarche logique rigoureuse. Il faut cependant souligner que la formulation des
solutions préconisées implique la définition de la liste des actions à entreprendre ainsi que
l’ordre dans lequel ces actions doivent être entreprises, même si ces actions et cet ordre
peuvent ne pas être exprimés avec beaucoup de précision. Malgré cette souplesse apparente,
les méthodes informelles que nous utilisons pour résoudre nos problèmes quotidiens sont
parfaitement satisfaisantes.

Cependant, lorsque nous utilisons l’ordinateur pour résoudre un problème, nous devons être
davantage plus précis. Lorsque les ordinateurs sont impliqués, la résolution d’un problème
passe généralement par le développement d'un algorithme. De façon simple, un algorithme
est la description des opérations à effectuer pour accomplir un certain traitement. Cette
description doit spécifier la liste des opérations à effectuer actions à entreprendre une suite
d’instructions organisées et suffisamment détaillées conduisant à la résolution d’un problème.
La signification de ce terme est assez proche de celle de recette, procédé ou méthode.
Toutefois, le terme algorithme nécessite une définition plus précise lorsqu’on aborde la
programmation des ordinateurs. Les instructions qui indiquent à l’ordinateur comment

Dr Ndi Nyoungui André


2 Algorithmique et structures de données

effectuer une tâche sont nécessairement plus précises que celles qui indiquent à un cuisinier
comment préparer un gâteau ou à un fabricant de vélos comment les assembler.

Ainsi, le processus de résolution des problèmes à l’aide des ordinateurs est souvent plus
formel que le procédé que nous utilisons pour la résolution de nos problèmes quotidiens. Le
processus de résolution des problèmes à l’aide des ordinateurs se décline en trois grandes
étapes distinctes :

Étape 1. La conception d’un algorithme pour résoudre le problème,


Étape 2. La traduction de l’algorithme dans un langage de programmation,
Étape 3. L’exploitation et la maintenance du programme.

La figure 1.1 illustre l’ensemble de toutes ces étapes.

Figure 1.1. Étapes du processus de résolution d’un problème par les ordinateurs

Problème

 Analyse informatique
Algorithmique
 Algorithmique
 Structures de données

Algorithme

Programmation Langages de programmation


(Pascal, C/C++, Visual Basic,
Visual C++, Delphi, Java…)

Programme

Exploitation Maintenance

1.2 Les éléments de base d’un algorithme

Définition. Algorithme

Une algorithme est une suite d’opérations que devra effecteur un ordinateur pour arriver, en
un temps fini, à un résultat donné, que nous définirons par une postcondition, à partir d’une

Dr Ndi Nyoungui André


3 Algorithmique et structures de données

situation donnée, que nous définirons par une précondition. La suite d’opérations sera
composée d’actions élémentaires appelées instructions que nos allons présenter par la suite.

Les directives ou commandes qui indiquent à la machine ce qu’elle doit faire pour obtenir la
solution désirée sont appelées des instructions. Nous pouvons donc en première analyse
représenter un algorithme par une suite d’instructions de la forme :

instruction1 ;
instruction2 ;

instructionn ;

Par exemple,

Structure d’un algorithme

Un algorithme se divise habituellement en deux parties : l’en-tête et le corps de l’algorithme


appelé aussi bloc. L’en-tête spécifie le nom de l’algorithme et la liste des interfaces entre
l’algorithme et son environnement. Dans l’exemple ci-dessous, nous présentons un algorithme
pour lire la valeur du rayon d’un cercle, calculer la circonférence et la surface du cercle puis
afficher les résultats à l’écran.

algorithme Cercle ;
{ Programme pour calculer la surface et le périmètre d’un cercle }
const
pi = 3.14159 ;
var
Rayon, Circonférence, Surface : réel ;
début
écrire('Quelle est la valeur du rayon ? ')
lire(Rayon) ;
Circonférence  2*pi*Rayon ;
Surface  pi * Rayon * Rayon ;
écrire('Rayon : ', Rayon) ;
écrire('Circonférence : ', Circonférence) ;
écrire('Surface : ', Surface)
fin.

Dans notre exemple, l’en-tête de l’algorithme consiste en la ligne

algorithme Cercle ;

Elle commence par le mot réservé algorithme et se termine par le nom de l’algorithme qui est
ici Cercle. Le corps de l’algorithme comporte trois sections. Les deux premières décrivent les
données utilisées par l’algorithme ; elles constituent la partie des déclarations de l’algorithme.
La première section de ce bloc définit la constante symbolique pi et lui attribue la valeur
3.14159.

Dr Ndi Nyoungui André


4 Algorithmique et structures de données

La deuxième section du bloc déclare les variables Rayon, Circonférence et Surface comme
étant des variables de type réel. Déclarer une variable signifie donner un nom et réserver un
emplacement dans la mémoire centrale de l’ordinateur pour pouvoir y stocker une information
du type spécifié. C’est à travers le nom donné à l’emplacement mémoire réservé que l’on peut
accéder à l’information qui s’y trouve. Nous avons donc dans notre exemple réservé trois
emplacements mémoire pour pouvoir y stocker des informations de type réel.

Figure 1.2. Représentation des variables dans la mémoire

Mémoire centrale

Rayon Surface Circonférence

La troisième section du bloc décrit les actions devant être effectuées par l’algorithme et est
appelée la partie instruction de l’algorithme. Celle-ci consiste en la description de sept
instructions (actions) se terminant chacune par un point virgule et délimitées par les mots
réservés début et fin. D’abord l’ordinateur demande à l’utilisateur de communiquer la valeur
du rayon du cercle. Cette valeur est lue et stockée dans une variable appelée Rayon. Ensuite
on calcule la circonférence et la surface du cercle en utilisant les formules habituelles. Les
deux résultats sont respectivement stockés dans les variables appelées Circonférence et
Surface. Enfin, on affiche à l’écran les valeurs des variables Rayon, Circonférence et Surface.
L’algorithme se termine par un point.

La première instruction de notre algorithme est formée :

 du mot réservé écrire,


 d’un paramètre, entre parenthèses.

Ce paramètre est ici une chaîne de caractères entre apostrophes. Elle a pour objet de
reproduire textuellement à l’écran ou sur l’imprimante le texte :

Quelle est la valeur du rayon ?

Ce genre de messages sont fortement recommandés pour l’écriture de bons algorithmes ou de


bons programmes. Ils permettent de réaliser une espèce de dialogue entre l’ordinateur et
l’utilisateur pendant l’exécution du programme, en donnant à la machine la possibilité de dire
à l’utilisateur ce qu’il doit faire à un moment donné. Un programme qui comprend de telles
invites est appelé un programme conversationnel.

Un caractère peut être une lettre majuscule ou minuscule, un chiffre, un signe de ponctuation,
ou un espace. Une chaîne de caractères, ou chaîne pour abréger, est une suite quelconque de
caractères entre apostrophes. Cette suite peut être vide ou de longueur quelconque. Si elle
contient une apostrophe, celle-ci sera doublée.

Dr Ndi Nyoungui André


5 Algorithmique et structures de données

La deuxième instruction est formée :

 du mot réservé lire,


 d’un paramètre, entre parenthèses.

Ce paramètre est une variable de type réel. Elle a pour objet de récupérer la valeur du rayon
saisie au clavier par l’utilisateur et de la ranger dans un emplacement de la mémoire centrale
de l’ordinateur que nous avons nommé Rayon.

La troisième et la quatrième instructions ont la même structure. Elles sont formées d’une
expression à droite et du nom d’une variable à gauche, les deux étant séparées par le symbole
«  » appelé opérateur d’affectation. Par exemple, la troisième a pour objet de calculer la
valeur de l’expression 2 * pi * Rayon et de ranger le résultat dans un emplacement de la
mémoire centrale de l’ordinateur que nous avons nommé Circonférence.

Les trois dernières instructions ont également la même structure. Elles sont chacune formées :

 du mot réservé écrire,


 d’une liste de deux paramètres, entre parenthèses.

Le premier paramètre est une chaîne de caractères qui sera reproduite textuellement à l’écran
ou sur l’imprimante ; le deuxième est une variable (expression) dont la valeur sera reproduite
à l’écran ou sur l’imprimante. Par exemple, l’exécution de la sixième instruction affichera à
l’écran quelque chose du genre :

Circonférence : ddddd.dd

où ddddd.dd est la représentation en décimal de la valeur de la variable Circonférence.


La partie de l’algorithme comprise entre les accolades { } est appelée un commentaire. Elle ne
donne aucune information à l’ordinateur. Son rôle est de donner des explications au lecteur
humain. Le commentaire dans notre exemple décrit simplement la fonction de l’algorithme.
Les commentaires sont habituellement utilisés pour indiquer le nom de l’auteur, la date
d’écriture du programme, et ainsi de suite.

Exemple d’exécution de l’algorithme Cercle (en gras la réponse de l’utilisateur)

Quelle est la valeur du rayon ? 10


Rayon : 10.00
Circonférence : 62.83
Surface : 314.16

Voici d’autres exemples d’algorithmes. Nous laissons le soin au lecteur de comprendre ce que
fait chacun de ces algorithmes.

Exemple 1
algorithme Addition ;
var
Nombre1, Nombre2, Somme : entier ;

Dr Ndi Nyoungui André


6 Algorithmique et structures de données

début
écrire('Quelle est la valeur du premier nombre ? ') ;
lire(Nombre1) ;
écrire('Quelle est la valeur du deuxième nombre ? ') ;
lire(Nombre2) ;
Somme  Nombre1 + Nombre2 ;
écrire('Somme : ', Somme) ;
fin.

Exemple 2
algorithme Bienvenue ;
début
écrire('Bienvenue dans le monde des programmeurs ! ') ;
fin.

Exemple 3
algorithme Conversion ;
const
FacteurConversion = 2.54 ;
var
pouces, centimètres : réel ;
début
écrire('Quel est le nombre de pouces ? ') ;
lire(Pouces) ;
centimètres  FacteurConversion*Pouces ;
écrire(pouces, 'pouces = ' , centimètres, 'centimètres') ;
fin.

Exemple 4
algorithme Bonjour ;
var
Nom : chaîne ;
début
écrire('Comment vous appelez-vous ? ') ;
lire(Nom) ;
écrire('Bonjour', Nom) ;
fin.

Exemple 5
algorithme Bonjour2 ;
var
nom : chaîne ;
sexe : caractère ;
début
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Quel est votre sexe (M ou F) ? ') ;
lire(sexe) ;
si sexe = 'M' alors

Dr Ndi Nyoungui André


7 Algorithmique et structures de données

écrire('Bonjour monsieur', nom)


sinon
écrire('Bonjour madame', nom) ;
fin.

Un algorithme comporte en général une ou plusieurs des sections énumérées dans la figure 1.3
ci-dessous. Ces sections, à l’exception de l’en-tête et de la partie instruction, peuvent ou ne
pas être présentes dans un algorithme donné.

Figure 1.3. Structure générale d’un algorithme


Corps de l’algorithme

Partie déclaration

En-tête Algorithme NomAlgorithme ;

Section de définition des constantes


Section de définition des types
Partie instruction

Section de déclaration des variables


Section de déclaration des procédures
Section de déclaration des fonctions

Nous allons dans la suite examiner en détail le rôle et la structure des différentes sections d’un
début une explication plus précise des différents concepts
algorithme. Nous donnerons également
instruction1 ;
que nous avons introduits précédemment.
instruction2 ;
Les identificateurs .
.
.
Tous les objets manipulés dans un algorithme doivent avoir chacun un nom différent. Dans
instructionn ;
l’en-tête d’un algorithme nous avons
fin. nommé l’algorithme ; dans la section de définition des
constantes nous avons nommé des constantes ; dans la section de déclaration des variables
nous avons nommé des variables. Nous verrons dans les prochains chapitres que les types de
données, les procédures et les fonctions doivent aussi être nommés.

Définition. Identificateur

La chaîne de caractères qui constitue le nom d’un algorithme, d’une constante, d’un type de
données, d’une variable, d’une procédure ou d’une fonction est appelée un identificateur. Un
identificateur consiste en une lettre suivie par une suite de lettres ou de chiffres, aucune
distinction n’étant faite entre les lettres minuscules et majuscules. De cette façon les chaînes
Matricule et MATRICULE seront regardés comme le même identificateur.

Dr Ndi Nyoungui André


8 Algorithmique et structures de données

Remarque. Il est recommandé de choisir des identificateurs suffisamment évocateurs de telle


sorte que le nom donné à un objet soit révélateur de son rôle et de sa signification dans
l’algorithme. L’utilisation des identificateurs expressifs ou évocateurs contribue à rendre les
algorithmes plus compréhensibles pour le lecteur humain.

Tableau 1.1. Quelques exemples d’identificateurs bien choisis

Identificateur Signification
DateNaissance Date de naissance d’un étudiant
TauxHoraire Taux horaire d’un employé
Rayon Rayon d’un cercle
LieuNaissance Lieu de naissance d’un étudiant

Le langage algorithmique a certains mots clés ou mots réservés qui ne peuvent pas être
utilisés comme identificateurs. Ces mots ont une signification prédéfinie et ne peuvent donc
pas être utilisés à d’autres fins. Les mots réservés les plus courants du langage algorithmique
sont algorithme, var, début, fin, si, alors, sinon, tantque, faire, répéter, jusquà, pour,
haut et bas pour ne citer que les ceux-là. Les autres mots réservés du langage algorithme
seront présentés au fur et à mesure que nous progresserons dans notre exposé.

Le langage algorithmique offre aussi des identificateurs standards ayant une signification
prédéfinie. Ceux-ci peuvent si nécessaire être redéfinis pour avoir une signification différente,
mais la redéfinition d’un identificateur standard peut conduire à des confusions pour le lecteur
de l’algorithme. Ceci n’est donc pas recommandé ! Quelques exemples d’identificateurs
standards du langage algorithmique sont :

Constantes  : vrai (true), faux (false)


Types  : entier (integer), booléen (boolean), réel (real), caractère (char)
Fonctions : abs, arctan, cos, eof, exp, ln, odd, round, sin, sqr, sqrt, tan, tanh, cosh, sinh.
Procédures : libérer (dispose), nouveau (new), lire (read), écrire (write), ouvrir (open), fermer
(close), supprimer (delete), relire (reset), reécrire (rewrite).

1.2.4 Les commentaires

Même lorsqu’un programmeur est suffisamment averti ou expérimenté pour utiliser des
identificateurs expressifs, il est toujours très difficile de lire un algorithme écrit par une tierce
personne, et de comprendre facilement ce que font les différentes parties sans certains
commentaires explicatifs. Les commentaires peuvent facilement être insérés dans un
algorithme. Un commentaire a la forme générale suivante :

{ commentaire } ou (* commentaire *) ou /* commentaire */

Les commentaires ne sont pas des instructions exécutables ; ils servent uniquement à la
documentation de l’algorithme et sont par conséquent ignorés par le système d’exécution.

1.2.5 Les constantes symboliques

Dr Ndi Nyoungui André


9 Algorithmique et structures de données

Lorsqu’on écrit des algorithmes, il arrive souvent que l’on ait besoin d’utiliser des valeurs qui
sont connues d’avance et qui restent inchangées tout au long de l’algorithme. De telles valeurs
sont par conséquent appelées des constantes. Un exemple d’une telle valeur est la valeur
3.14159 que nous avons utilisée précédemment.

Quand on utilise une constante dans un algorithme, on peut soit utiliser la valeur littérale de la
constante, soit donner un nom symbolique à la constante et utiliser par la suite ce nom pour
faire référence à la constante en question. Pour créer une constante symbolique dans un
algorithme on utilise une instruction de définition de constante. La forme générale d’une
section de définition de constantes est la suivante :

const
identificateur1 = constante1 ;
identificateur2 = constante2 ;

identificateurn = constanten ;

Par exemple,

const
pi = 3.14159 ;
epsilon = 0.0001 ;

définit les constante symbolique pi, e et précision pour être utilisée à travers le programme.

Les variables

Définition. Variable

Une variable est un emplacement de la mémoire central de l’ordinateur auquel on attribue un


nom et qui est destiné à recevoir des valeurs d’un certain type.

Comme nous pouvons le voir dans la figure 4.1, il est important de savoir faire la différence
entre le nom d’une variable et sa valeur courante.

Figure 1.4. Représentation d’une mémoire dans la mémoire centrale

valeur Mémoire centrale

Nom de la variable

Lorsqu’une variable est déclarée dans un algorithme (programme), un emplacement physique


est réservé dans la mémoire pour contenir la valeur de la variable. La déclaration des variables
intervient dans la section de déclaration des variables du bloc. Le nom d’une variable ne peut
pas être changée à l’intérieur du bloc où elle a été déclarée. Au fur et à mesure que le système

Dr Ndi Nyoungui André


10 Algorithmique et structures de données

exécute le programme, il va éventuellement modifier les données stockées dans la mémoire


centrale. La valeur stockée dans un emplacement particulier, et par conséquent la valeur d’une
variable, sera modifiée à mesure que le programme s’exécute. C’est la raison pour laquelle on
se réfère à la valeur à un point donné comme sa valeur courante.

Pour déclarer les variables , on utilise une instruction de déclaration dont la forme générale est

var
identifiacteur1 : type1 ;
identificateur2 : type2 ;
...
identificateurn : typen ;

Une déclaration de variable spécifie le nom de la variable ainsi que le type des valeurs que
cette variable peut prendre. Le type d’une variable correspondant au nom d’ensemble des
valeurs que cette variable peut prendre. Par exemple, l’instruction

var
nombre : entier ;

déclare une variable appelé nombre pouvant prendre des valeurs entières.

1.2.7 Les types de données

Définition. Type de données

Un type de données spécifie un ensemble de valeurs de même nature pouvant être prises par
des variables dans un algorithme. Le langage algorithmique offre une grande variété de types
de données allant des types simples à des structures de données très complexes. Les types de
données les plus simples du langage algorithmique sont appelés les types scalaires. Ceci les
types de base à partir desquels tous les autres types de données sont construits. Les types de
données scalaires se divisent eux-mêmes en deux groupes : les types scalaires standards et les
types scalaires définis par l’utilisateur.

Les principaux types scalaires standards du langage algorithmique sont :

 le type entier qui est l’ensemble des valeurs entiers relatifs pouvant être représentées
dans la machine,
 le type réel qui est l’ensemble des nombres réels pouvant être représentés dans la
machine,
 le type caractère qui est l’ensemble des caractères du code utilisé par la machine (code
ASCII par exemple),
 le type booléen qui est l’ensemble des deux valeurs {vrai, faux}

Les types de données scalaires définis par l’utilisateur sont définis par l’utilisateur pour aider
à résoudre un problème particulier. Des exemples de types de données simples définis par
l’utilisateur sont :

Dr Ndi Nyoungui André


11 Algorithmique et structures de données

 le type énuméré (énumération de l’ensemble de toutes les valeurs qu’une variable de


ce type peut prendre),
 le type intervalle (restriction de l’intervalle des valeurs d’un type existant),
 le type pointeur (adresse des emplacements mémoire).

Les types de données scalaires sont non structurés. Cela signifie qu’il consistent en de simples
valeurs distinctes. Le langage algorithmique offre également des types de données structurés
qui sont composés à partir des types scalaires et pouvant prendre un ensemble ou groupe de
valeurs. Les principaux types de données structurés sont :

 les chaînes de caractères,


 les tableaux à une ou à plusieurs dimensions,
 les articles ou les structures,
 les fichiers séquentiels,
 les listes linéaires chaînées,
 les arbres,
 les objets.

1.2.8 Les expressions arithmétiques

Définition. Expression arithmétique

Une expression est une construction algorithmique qui permettant d’effectuer des calculs dans
un algorithme. Une expression arithmétique représente une donnée numérique élémentaire.
Elle peut être constituée d’un seul terme tel qu’une constante, une variable, un élément de
tableau ou un désignateur de fonction. Elle est peut aussi être composée par un certain nombre
de termes (ou opérandes) et des opérateurs au moyen desquels ces opérandes sont combinés
pour produire une valeur numérique élémentaire.

Quelques exemples d’expressions arithmétiques sont :

2*pi*rayon
Nombre1 + Nombre2 ;
FacteurConversion * Pouces

Le langage algorithmique offre un certain nombre d’opérateurs arithmétiques qui prennent des
opérandes entiers et produisent des résultats entiers. Ces opérateurs sont :

Tableau 1.2. Les opérateurs entiers du langage algorithmique

Opérateur Signification
+ Addition
- Soustraction
* Multiplication
div Division entière
mod Reste de la division euclidienne

Ces opérateurs sont des opérateurs binaires infixés, ce qui signifie qu’ils s’écrivent entre
leurs deux opérandes.

Dr Ndi Nyoungui André


12 Algorithmique et structures de données

Quelques exemples d’expressions entières utilisant ces opérateurs sont :

Opération Résultat
9+5 14
9–5 4
9*5 45
9 div 5 1
9 mod 5 4

Les opérateurs + et – peuvent aussi être utilisés avec des valeurs telles que +a et –a pour
indiquer le signe d’une expression. L’opérateur + utilisé avec un seul opérande est appelé
l’opérateur + unaire et un – utilisé avec un seul opérande est appelé un opérateur – unaire.
Bien entendu, les opérateurs entiers peuvent aussi être utilisés avec des variables. Ainsi, si on
a les déclarations suivantes :

var
compteur, milieu, pluspetit : entier ;

alors les construction suivantes sont des expressions entières valides.

compteur + milieu
compteur – milieu * pluspetit
milieu div 10 + pluspetit

Les opérateurs binaires infixés +, -, et * représentent les opérations d’addition, de soustraction


et de multiplication habituelles. L’opérateur div calcule le quotient de la division entière de
deux nombres entiers a et b. Ainsi

5 div 2 donnera 2
2 div 2 donnera 1
14 div 3 donnera 4
5 div 7 donnera 0

L’opérateur mod calcule le reste de la division euclidienne de deux nombres entiers a et b.


Ainsi

21 mod 5 donnera 1
13 mod 10 donnera 3
5 mod 6 donnera 5

Le langage algorithmique définit également un certain nombre d’opérateurs arithmétiques qui


prennent des opérandes réels et produisent des résultats réels. Ces opérateurs sont :

Tableau 1.3. Les opérateurs réels du langage algorithmique

Opérateur Signification
+ Addition
- Soustraction

Dr Ndi Nyoungui André


13 Algorithmique et structures de données

* Multiplication
/ Division réelle

Comme pour les opérateurs entiers, ces opérateurs sont des opérateurs binaires infixés. Les
opérateurs + et – peuvent aussi être utilisés comme des opérateurs + et – unaires, s’ils sont
utilisés avec un seul opérande.
Quelques exemples d’expressions réelles sont donnés ci-dessous :

Expression Résultat
3.5 + 4.3 7.8
3.5 – 4.3 -0.8
3.5 * 4.3 15.05
3.5 / 4.3 0.81395

Les opérateurs binaires +, - et * sont les mêmes que les opérateurs entiers correspondants.
L’opérateur binaire / est utilisé pour la division réelle. Comme on le verra dans le cours sur la
représentation des informations en machine, la représentation des nombres réels dans la
machine n’est pas exacte. Comme conséquence, il est possible que, pour des valeurs réelles a
et b, la relation (a/b)*b ne soit pas toujours vraie dans la machine.

La précédence des opérateurs

Lorsqu’une expression implique plusieurs opérateurs, l’ordre dans lequel les opérateurs seront
évalués peut être ambiguë. Par exemple, la valeur de 2 + 3 * 4 est-elle égale à 20 ou à 14 ?
C’est-à-dire 2 + 3 * 4 sera-t-il interprétée comme (2 + 3) * 4 ou 2 + (3 * 4) ? A cause de ce
genre d’ambiguïtés, tous les langages de programmation ont adopté des règles spécifiant
l’ordre dans lequel les opérations seront effectuées. Ces règles sont appelées les règles de
précédence des opérateurs, elles correspondent aux règles utilisées en arithmétique. La
précédence des opérateurs que nous avons présentés ci-dessus, de la plus grande à la plus
petite, sont :

Tableau 1.4. Priorité des opérateurs arithmétiques

Précédence Opérateur
1 *, /, div, mod
2 +, -

Les règles d’évaluation des expressions reflètent les différentes classes de précédence des
opérateurs et la capacité d’exprimer ces précédences en utilisant les parenthèses.

Règle1. Évaluer d’abord les expressions entre parenthèses, en commençant par les
parenthèses les plus internes.

Règle 2. Effectuer les multiplications et/ou les divisions (*, /, div, mod) de la gauche vers la
droite.

Règle 3. Effectuer les additions et/ou les soustractions de la gauche vers la droite.

Par exemple, considérons l’expression

Dr Ndi Nyoungui André


14 Algorithmique et structures de données

(Cost – Salvage)/Life

Supposons que Cost = 500.00, Salvage = 100.00 et Life = 10.0. Alors en utilisant les règles
d’évaluation des expressions, on a

Expression évaluée Résultat Reste à évaluer


Règle 1 Cost - Salvage 400.00 400.00/Life
Règle 2 400.00/Life 40.00

Considérons un autre exemple plus complexe.

(c * d div (a mod b)) + b – c * d mod a

Supposons que a = 3, b = 4, c = 5, d = 6. Alors on a

Expression évaluée Résultat Reste à évaluer


Règle 1 a mod b 3 (c * d div 3 ) + b – c * d mod a
Règle 2 c*d 30 (30 div 3) + b – c * d mod a
Règle 1 30 div 3 10 10 + b – c * d mod a
Règle 2 c*d 30 10 + b – 30 mod a
Règle 2 30 mod a 0 10 + b - 0
Règle 3 10 + b 14 14 – 0
Règle 3 14 - 0 14

Si on n’est pas sûr des règles de précédence dans un calcul particulier, on peut toujours forcer
l’ordre d’évaluation à appliquer dans la séquence que l’on veut en utilisant les parenthèses.

La compatibilité des types

En mathématiques, les entiers peuvent être considérés comme des nombres réels. Dans une
machine, ils sont effectivement représentés différemment. Le langage algorithmique nous
permet cependant d’ignorer partiellement cette différence en convertissant automatiquement
chaque entier, qui apparaît là où un réel est attendu, en un nombre réel équivalent. La
réciproque n’est cependant pas vrai. On n’acceptera pas un nombre réel là où un nombre
entier est attendu. Enfin, on peut mélanger les entiers et les réels mais le résultat d’une telle
expression sera toujours réel.

Par exemple, supposons que nous avons déclaré les variables suivantes :

var
valeur1, valeur2 : réel ;
nombre : entier ;

alors les expressions

valeur1 + valeur2
nombre * valeur1
valeur1 – 5

Dr Ndi Nyoungui André


15 Algorithmique et structures de données

(valeur1 + nombre)/5

produisent des résultats réels. Les expressions

nombre + 5
nombre div 5

produisent des résultats entiers. Seules les expressions formées uniquement d’opérandes
entiers produisent des résultats entiers. Les expressions

valeur1 div valeur2


valeur1 mod nombre

ne sont pas valides car seuls les entiers sont attendus pour les opérateurs div et mod ; les
valeurs réelles ne seront donc pas acceptées.

Les instructions

Les instructions permettent de décrire les opérations qui doivent être effectuées dans un
algorithme. Il s’agit en quelque sorte des directives ou ordres que l’on donne au calculateur
pour lui permettre d’accomplir un certain traitement. Une instruction peut être simple ou
composée. Une instruction simple exprime une action élémentaire tandis qu’une instruction
composée est une séquence d’instructions simples ou composées devant être traitées comme
un tout. La forme générale d’une instruction composée est :

début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Les mots réservés début et fin marquent le début et la fin d’une instruction composée. Les
instructions sont habituellement organisées dans des structures algorithmiques particulières ,
appelées structures de contrôle, permettant de décrire avec précision la suite des opérations
à effectuer pour réaliser un certain traitement. Les principales structures de contrôle du
langage algorithmique sont :

 Les structures séquentielles qui permettent de spécifier un ensemble d’instructions


devant être exécutées les unes à la suite des autres.
 Les structures de sélections qui permettre de choisir l’instruction ou le groupe
d’instructions qui doit être exécuté en fonction de la valeur d’une certaine condition.
 Les structures de répétition qui permettent de spécifier l’exécution plusieurs fois
d’une instruction ou d’un groupe d’instructions.

Certaines instructions sont si fondamentales qu’il est indispensable de savoir comment elles
fonctionnent avant de continuer.

Dr Ndi Nyoungui André


16 Algorithmique et structures de données

La sortie des informations

L’ordinateur a très souvent amené à communiquer une information à l’utilisateur. Pour cela, il
devra reproduire un texte ou une valeur numérique à l’écran ou sur l’imprimante. Cette
opération sera spécifiée par une instruction d’écriture dont la forme la plus simple consiste à
reproduire à l’écran ou sur l’imprimante la valeur d’une seule expression :

écrire(expression) ;

où expression est une expression numérique, logique ou une chaîne de caractères. Cette
instruction indique à l’ordinateur d’afficher à l’écran la valeur de expression.

Une expression de type chaîne de caractères est une suite de caractères entre apostrophes. Par
exemple l’instruction

écrire(‘Bonjour mon cher ami.’);

reproduira textuellement à l’écran le message Bonjour mon cher ami.

Il est cependant possible de reproduire à l’écran les valeurs de plusieurs expressions par le
moyen d’une seule instruction d’écriture. Dans ce cas, la forme générale d’une instruction
d’écriture devient :

écrire(liste-expressions);

où liste-expressions est une liste d’expressions éventuellement de types différents séparés par
des virgules. Les valeurs des expressions de la liste sont alors écrites sur une même ligne ; le
curseur restant positionné sur la même ligne.

Par exemple, l’instruction

écrire(rayon, circonférence, surface)

imprime les valeurs de rayon, circonférence et surface sur une même ligne ; le curseur restant
positionné sur la même ligne à la fin de l’opération d’écriture.

Une autre variante de l’instruction d’écriture prend la forme écrireln(). Cette version a pour
effet d’écrire les valeurs de la liste des expressions sur la même ligne et de faire passer le
curseur à la ligne suivante. Utilisée sans argument, elle permet d’écrire une ligne blanche et
de passer à la ligne suivante.

Par exemple, l’instruction

écrireln(Rayon, Circonférence, Surface)

Commande l’affichage des valeurs de rayon, circonférence et surface à l’écran sur la même
ligne et de passer à la ligne suivante.

Dr Ndi Nyoungui André


17 Algorithmique et structures de données

La lecture des données

Une instruction de lecture permet au programme d’obtenir des informations à partir du monde
extérieur. Au fur et mesure que le programme s’exécute, des valeurs sont lues et affectées aux
variables. Ceci permet que des traitements soient appliqués à des données différentes sans
changer le programme. Seules les valeurs que nous présentons à l’entrée ont besoin d’être
changées.

Une opération de lecture sera spécifiée par une instruction de lecture dont la forme la plus
simple est :

lire(identificateur)

où identificateur est un nom de variable. Elle consiste à récupérer la valeur saisie au clavier et
à la ranger dans l’emplacement mémoire associé à la variable appelée identificateur. Par
exemple, l’instruction

lire(nombre)

lit la valeur entrée au clavier et la range dans l’emplacement mémoire associé à la variable
appelé nombre. Une instruction de lecture est bloquante en ce sens que lorsque le mécanisme
d’exécution atteint une instruction de lecture, le processus d’exécution du programme est
suspendue jusqu’à ce que l’utilisateur entre la donnée attendue.

Il est possible de lire en une seule instruction plusieurs valeurs. Dans ce cas, l’instruction de
lecture prend la forme

lire (liste-identificateurs)

où liste-identicateurs est une liste d’identificateurs de variable éventuellement de types


différents séparés par des virgules. Les valeurs à lire dans les variables associées à la liste des
identificateurs sont alors tapées au clavier en les séparant par des blancs (espaces).

Par exemple, l’instruction

lire(A, B)

commande la lecture de deux valeurs et de les affecter aux variables A et B.

L’instruction d’affectation

L’instruction d’affectation est sans doute l’instruction la plus utilisée en algorithmique et dans
la plupart des langages de programmation. C’est le moyen élémentaire de faire des calculs, de
déplacer des données et de changer les valeurs des variables. La forme générale d’une
instruction d’affectation dans le langage algorithmique est :

identificateur  expression ;

Dr Ndi Nyoungui André


18 Algorithmique et structures de données

où expression est une expression du même type que celui de la variable dont l’identificateur
apparaît à gauche. Le symbole «  » est appelé l’opérateur d’affectation et se lit « est
remplacé par », ou « reçoit ». Des exemples d’instruction d’affectation que nous avons déjà
rencontrées sont :

Circonférence  2*pi*Rayon 
Somme  Nombre1 + Nombre2
Centimètres  FacteurConversion * Pouces

Une instruction d’affectation s’exécute de la manière suivante :

1. L’expression à droite de l’opérateur d’affectation est évaluée,


2. La valeur de l’expression est stockée (affectée) dans l’emplacement mémoire associé à
la variable dont l’identificateur apparaît à gauche de l’opérateur d’affectation, écrasant
ainsi l’ancienne valeur de la variable.

L’instruction d’affectation spécifie alors que le variable à gauche de l’opérateur d’affectation


reçoit une valeur équivalente à celle de l’expression à droite de l’opérateur d’affectation. Ceci
détruit la valeur que la variable contenait précédemment. Pour être capable d’affecter une
valeur à la variable à gauche de l’opérateur d’affectation, toutes les variables apparaissant
dans l’expression à droite de l’opérateur d’affectation doivent avoir des valeurs. Ceci signifie
qu’elles doivent être initialisées préalablement. Ainsi, quand on utilise les instructions
d’affectation dans un algorithme, il est important de les cadencer correctement pour éviter des
problèmes d’incohérence.

Considérons par exemple le problème qui consiste à lire deux nombres et à permuter leurs
valeurs de telle sorte que le premier nombre contienne la valeur du deuxième et le deuxième
nombre contienne la valeur du premier. Pour effectuer cette permutation, on n’a pas besoin
seulement des deux nombres ; on doit aussi avoir un troisième nombre qui servira de réserve
pour qu’on ne perde pas l’une des valeurs.

algorithme Permute ;
var
A, B, C : réel ;
début
écrireln('Quelle est la valeur du premier nombre ? ') ;
lire(A) ;
écrireln('Quelle est la valeur du deuxième nombre ? ') ;
lire(A) ;
écrireln('Avant la permutation') ;
écrireln('Premier nombre : ', A) ;
écrireln('Deuxième nombre : ', B) ;
(*permutation des deux nombres*)
C  A ;
A  B ;
B  C ;
écrireln('Après la permutation') ;
écrireln('Premier nombre : ', A) ;
écrireln('Deuxième nombre : ', B) ;

Dr Ndi Nyoungui André


19 Algorithmique et structures de données

fin.

Exemple d’exécution de l’algorithme permute (en gras les réponses de l’utilisateur)

Quelle est la valeur du premier nombre ? 100


Quelle est la valeur du deuxième nombre ? 75
Avant la permutation
Premier nombre : 100
Deuxième nombre : 75
Après la permutation
Premier nombre : 75
Deuxième nombre : 100

Les fonctions prédéfinies

Certains calculs qui apparaissent assez souvent dans les algorithmes, comme le calcul de la
valeur absolue d’un nombre, ne peuvent pas être effectués facilement en utilisant les
opérateurs arithmétiques classiques +, -, *, /, div ou mod. Pour rendre de tels calculs faciles à
effectuer, le langage algorithmique ainsi que la plupart des langages de programmation offrent
un certain nombre de fonctions mathématiques standards. Par exemple, pour calculer la valeur
absolue de –10 et affecter le résultat à la variable nombre, on a juste besoin d’écrire

nombre  abs(-10) ;

La valeur calculée par l’instance de fonction abs(-10) est +10. Cette valeur est affectée à la
variable nombre. La valeur -10 est appelée l’argument ou paramètre effectif de la fonction.
Étant donné un argument, une fonction calcule toujours une valeur élémentaire appelée la
valeur de la fonction.

La fonction abs() est directement offerte par le langage algorithmique et est par conséquent
appelée une fonction prédéfinie ou préconçue. Le tableau 1.5 ci-dessous donne les noms et
la description de quelques fonctions prédéfinies du langage algorithmique.

Tableau 1.5. Quelques exemples de fonctions prédéfinies

Nom Description Argument Valeur de la fonction


abs(x) Valeur absolue de x Entier/Réel Idem que l’argument
artan(x) Tangente inverse de x Entier/Réel Réel
cos(x) Cosinus de x Entier/Réel Réel
exp(x) Exponentiel de x Entier/Réel Réel
ln(x) Logarithme de x Entier/Réel Réel
sin(x) Sinus de x Entier/Réel Réel
sqr(x) Carré de x Entier/Réel Idem que l’argument
sqrt(x) Racine carrée de x Entier/Réel Réel
tan(x) Tangente de x Entier/ Réel Réel

Pour utiliser une fonction, on écrit le nom de la fonction suivi par son argument ou paramètre
effectif entre parenthèses. Une telle expression est appelée un désignateur de fonction. Toute
expression légale peut être utilisée comme paramètre effectif à condition que le type de

Dr Ndi Nyoungui André


20 Algorithmique et structures de données

l’expression corresponde à celui de l’argument que la fonction attend. Les constructions


suivantes sont des instructions qui utilisent des fonctions :

nombre  ln(sqr(a) + sqr(b))


cosinus  cos(2 * pi * x)
valeur  abs(3.5 * longueur/largeur – 17.0)

La représentation des algorithmes

Quand on écrit un algorithme, on utilise une notation qui exprime mieux la solution proposée
plutôt que les règles d’un langage de programmation particulier. Le principal avantage de
cette approche est que lorsqu’on résout un problème, on n’a pas de se préoccuper des détails
d’un langage particulier. On veut se sentir libre d’écrire de façon non ambiguë ce que chaque
instruction sans se être préoccupé par les aspects tel que la validité des identificateurs, l’utilisa
correcte des signes de ponctuations et d’autres détails du langage. Mais il est souvent
avantageux d’utiliser dans la description de l’algorithme, des notations proches du langage de
programme cible.

En général, pour écrire les instructions d’un algorithme, on utilise habituellement un mélange
de français, d’anglais ainsi que des notations et des structures d’un langage de programmation
particulier. Un tel dialecte constitue un pseudo langage qu’il convient d’appeler le langage
algorithmique.

Le langage algorithmique a l’avantage de rendre l’expression des algorithmes semblable à


celles des programmes qui en découleront. Toutefois, pour utiliser correctement le langage
algorithmique, on doit être familier avec les structures du langage de programmation qui sera
utilisé pour implémenter l’algorithme.

Les organigrammes sont une autre forme d’expression des algorithmes. Les organigrammes
utilisent un ensemble de symboles géométriques standards pour représenter les différentes
opérations d’un algorithme. Un organigramme décrit figurativement la suite des opérations
nécessaires pour la résolution du problème.

Symbole Signification Symbole Signification

Bloc terminal Boîte d’impression

Boîte de calcul
Boîte de commentaire

Boîte de test Sens d’exécution

Boîte de lecture

Dr Ndi Nyoungui André


21 Algorithmique et structures de données

Le pseudo langage et les organigrammes sont utilisés pour le même but. A priori, aucune des
deux approches n’est supérieure à l’autre pour représenter les algorithmes même si le pseudo
langage est en général plus proche des langages de programmation de haut niveau. La
technique utilisée n’est pas importante ; il faut seulement être confortable dans une proche et
être capable de développer des algorithmes corrects. Dans cet ouvrage, nous utilisons le
pseudo langage pour tous nos algorithmes. Périodiquement, nous utiliserons également les
organigrammes pour illustrer une instruction particulière.

L’organigramme présenté à la figure 1.5 ci-dessous correspond à quelque chose près à


l’algorithme de calcul de la surface et de la circonférence d’un cercle connaissant son rayon.

Figure 1.5. Organigramme de calcul des attributs d’un cercle

début

Rayon

Surface  2*pi*Rayon
Circonférence  pi * sqr(Rayon)

Rayon ,Surface,
Circonférence

fin

Exercices d’apprentissage

Exercice 1.1
Définir le vocable variable

Exercice 1.2
Choisir des identificateurs expressifs et pour déclarer des variables pouvant être utilisées pour
représenter les informations suivantes :

Dr Ndi Nyoungui André


22 Algorithmique et structures de données

Information Information
Prix de vente Nature du baccalauréat
Moyenne des notes Mention obtenu au baccalauréat
Lieu de naissance Lieu obtention
Prix de revient Note examen final
Numéro matricule d’un étudiant Note examen partiel
Année obtention du baccalauréat Nombre d’heures de travail effectuées
Nombre de pages d’un livre Numéro de téléphone
Âge d’un étudiant en années Nom d’un employé
Salaire de base Cotisation CNPS
Date de naissance Code de l’unité de valeur

3. La conception des algorithmes

Analyse ou définition du problème

Un aspect important du processus de développement d’une solution à un problème est


l’analyse préalable du problème. Une bonne compréhension du problème est essentielle pour
la formulation de bons algorithmes et la construction de bons programmes. Une définition
incomplète ou pauvre du problème implique que celui-ci n’est pas clairement compris.
L’importance d’une bonne définition du problème ne sera jamais exagérée. Une solution à un
faux problème n’est pas une solution du tout. Le meilleur ensemble d’instructions disponibles
sur la manière d’assembler une bicyclette ne va pas du tout nous apprendre à bien sucer une
tranche d’ananas.

Les programmeurs débutants pensent souvent que la définition du problème à résoudre est
triviale. Rien ne peut aller au delà de la vérité. En effet, une définition attentive et précise du
problème peut souvent constituer plus de la moitié de la solution complète du problème. Le
défaut de développer des solutions attentives, bien que laborieuses, peut conduire non
seulement à la résolution d’un problème mal défini, mais très souvent conduit à la résolution
d’une partie seulement du problème posé ou à une solution beaucoup plus complexe que celle
qui est nécessaire pour résoudre le problème.

Nous perdons souvent beaucoup de notre énergie à résoudre des problème mal définis, et les
résultats obtenus sont bien évidemment inutiles. Une solution partielle à un problème, à cause
d’une définition incomplète du problème, peut également être inutile, particulièrement si on
tente de résoudre avec précision la partie du problème dont la définition a été omise. Enfin, si
notre solution est plus complexe que celle qui est nécessaire pour résoudre le problème posé,
des efforts non nécessaires ont été consentis pour la développer.

Certains problèmes sont plus faciles à analyser avec précision que d’autres. Évidemment, la
conversion des grammes en kilogrammes est plus facile que la gestion du service de la
scolarité d’un collège ou d’une université. Il n’y a pas une approche universelle bien
organisée pour analyser les problèmes. Toutefois, nos chances de succès peuvent être
améliorées si nous suivons rigoureusement certaines étapes importantes. Au bout du compte,
une bonne définition du problème implique un travail ardu. Nous devons résister à la tentation
de sauter l’étape de la définition du problème pour commencer directement le travail de
développement des algorithmes.

Dr Ndi Nyoungui André


23 Algorithmique et structures de données

La définition d’un problème peut être divisée en deux phases : (1) la spécification des entrées
et (2) la spécification des sorties.

La spécification des entrées

Une étape importante de la définition d’un problème est la description des données qui
constituent les entrées du problème. Pour se faire, on doit répondre aux différentes questions
suivantes :

1. Quelles sont les entrées du problème ?


2. Quel est le format des entrées ?
3. Quelles sont les valeurs légales et illégales des entrées ?
4. Y a-t-il des restrictions sur l’utilisation des valeurs d’entrée ?
5. Quel est le volume des entrées disponibles ou comment saura-t-on que toutes les
entrées ont été identifiées ?

La spécification des sorties

De manière analogue, on a besoin de décrire les sorties désirées. La spécification des sorties
consiste généralement à répondre aux questions suivantes :

1. Quelles sont les valeurs à produire ?


2. Quel sera le format des sorties ?
3. Devra-t-on sortir des en-têtes, des annotations ou toute autre information explicative ?
4. Quel est le volume des sorties ?

Essayons maintenant de faire l’analyse d’un problème simple et voyons comment on effectue
la spécification des entrées et des sorties.

Description initiale

Nous voulons écrire un programme pour calculer la commission d’un vendeur en utilisant la
formule : Commission = 5000 F + 0.10*Ventes. La valeur Ventes sera fournie au programme.

Bien que ceci soit un problème tout fait simple, il n’est pas de cette façon bien défini. Nous
pouvons facilement améliorer sa description.

Premier raffinement

Nous voulons calculer la commission d’un vendeur en utilisant la formule : Commission =


5000 F + 0.10*Ventes. Ventes représente le montant total en francs des ventes réalisées par le
vendeur en question. La valeur de Ventes sera fournie comme entrée. Si une valeur négative
de Ventes est fournie, un message d’erreur sera affiché. Si une valeur supérieure à 500000
francs est fournie, un message demandant de vérifier la correction du montant sera affiché à
l’écran ainsi que le montant de la commission. Dans le cas contraire, la commission sera
calculée et imprimée ainsi que le montant des ventes.

Dr Ndi Nyoungui André


24 Algorithmique et structures de données

Nous avons maintenant une idée beaucoup plus claire de ce que le programme doit faire.
Nous pouvons même raffiner davantage notre description ; c’est-à-dire, définir avec plus de
précision le problème, les contraintes sur les données d’entrée et de sortie et les conditions qui
vont affecter la solution du problème.

Deuxième raffinement

1. Position du problème : Nous voulons calculer la commission d’un vendeur en


utilisant la formule : Commission = 5000 F + 0.10*Ventes. Ventes représente le
montant total en francs des ventes réalisées par le vendeur en question.
2. Entrée : L’entrée du programme consistera en un seul nombre réel.
3. Sortie : La sortie du programme consistera en la valeur originale de Ventes, ainsi que
de la valeur de Commission, et sera affichée selon le format suivant :

Ventes = ddddddd,dd francs


Commission = ddddddd.dd francs

où les ds représentent des chiffres décimaux.

4. Conditions particulières : Si une valeur négative de Ventes est fournie en entrée, le


message suivant sera affiché à l’écran.

« ERREUR : Une valeur négative des ventes a été fournie »

Si une valeur de Ventes supérieure à 500000 francs est fournie en entrée, la sortie
suivante sera générée :

Ventes = ddddddd,dd francs


Commission = ddddddd.dd francs
Attention: Ventes supérieures à 500000 F. Vérifier exactitude !

Étant donné cette dernière spécification du problème avec une description détaillée des
entrées, des sorties et du traitement des erreurs, on aura maintenant aucune difficulté pour
comprendre ce qui est attendu.

Développement des algorithmes

Une fois que le problème a été clairement défini et analysé, un algorithme doit être développé
pour résoudre le problème. Nous pouvons maintenant donner une définition plus précise du
vocable algorithme.

Définition. Un algorithme consiste en :

1. Une procédure formée d’un ensemble d’instructions non ambiguës.


2. Des opérations spécifiées par des instructions qui doivent être exécutées dans un
certain ordre.
3. Une procédure devant produire une solution à une classe générale de problèmes.
4. Une solution devant être obtenue en un nombre fini d’étapes ; c’est-à-dire que la
solution soit avoir un point terminal.

Dr Ndi Nyoungui André


25 Algorithmique et structures de données

Tous les algorithmes partagent les caractéristiques ci-dessus, à savoir :

 Chaque instruction doit décrire exactement quelle opération doit être effectuée,
 Chaque instruction doit être écrite explicitement, car il n’y a pas d’étapes supposées
ou sous-entendues,
 Les instructions doivent être écrites dans un ordre précis.

Par ailleurs, pour être utile, un algorithme doit résoudre un problème général. Un algorithme
qui calcule uniquement la moyenne des nombres 7, 10, 15 et 14 n’a aucune généralité. Un
algorithme pour calculer la moyenne de quatre nombres est un peu plus général mais un
algorithme pour calculer la moyenne de n (n quelconque) nombres est beaucoup plus général.

Enfin, un algorithme doit nécessairement se terminer. Quand un algorithme est exécuté, le


processus d’exécution doit normalement atteindre un point où il n’y a plus d’instructions à
exécuter. Étant donné qu’un algorithme est composé d’un nombre fini d’instructions, on peut
se poser la question de savoir comment un algorithme peut-il ne pas se terminer. Que reste-t-il
encore à faire lorsque toutes les instructions ont été exécutées ? Le problème est qu’un
algorithme peut demander que certaines instructions soient répétées jusqu’à ce qu’un certain
événement se produise. Si l’événement attendu ne se produit pas, la répétition de ces
instructions peut continuer indéfiniment et le programme ne se terminera jamais. On parle
alors de boucle infini.

Le processus de développement d’un algorithme pour résoudre un problème particulier n’est


pas souvent une affaire facile. Il nécessite habituellement plusieurs tentatives d’insertion, de
suppression ou de modification de l’algorithme initial. On peut même parfois être amené à
abandonner une première tentative pour commencer une nouvelle.

La conception d’un algorithme se déroule généralement en deux étapes.

1. Une description informelle de l’algorithme est développée. Cette version


initiale de la solution décrit une approche de la solution en termes de tâches qui
nécessiteront une élaboration dans les étapes suivantes.
2. La version initiale est raffinée. Chaque tâche ou sous tâche utilisée pour décrire
l’algorithme dans l’étape précédente est maintenant raffinée jusqu’à ce que l’on
obtienne une description suffisamment détaillée de l’algorithme permettant à ce
dernier d’être codé facilement dans un langage de programmation, tels que le C, le
Pascal ou le Basic. Pour atteindre cet objectif, on peut être amené à répéter cette
étape plusieurs fois.

Le raffinement par étapes

Une fois que l’on a une définition solide du problème, on conçoit l’algorithme, habituellement
par étapes. C’est-à-dire que l’on se concentre initialement sur les aspects critiques et globaux
qui sont significatifs pour la résolution du problème, en réservant les détails pour les étapes
inférieures. Chaque étape est raffinée jusqu’à ce que l’on obtienne un algorithme complet
pouvant être facilement transformé en programme. Ce procédé de conception des algorithmes
par étapes est appelé le raffinement par étapes.

Dr Ndi Nyoungui André


26 Algorithmique et structures de données

Considérons l’exemple suivant qui utilise une version simplifiée du problème défini ci-dessus,
le calcul de la commission d’un vendeur. En développant cette solution, nous allons ignorer
pour des raisons de simplicité, tous les traitements d’erreur prévus dans la définition initiale
du problème.

Description initiale de l’algorithme

début
Lire le montant des ventes
Calculer la commission
Imprimer le montant des ventes et de la commission
fin.

La description initiale de l’algorithme ne fait pas autre chose qu’identifier les composantes
majeures de la solution. La description de ces composantes est tout à fait générale à ce stade.
L’étape suivante consiste à raffiner chacune de ces composantes à son tour.

Raffinement de l’algorithme

début
Lire le montant des ventes
Calculer la commission en utilisant le barème 5000 + 0.10*ventes
Imprimer le montant des ventes
Imprimer le montant de la commission
fin ;

La première instruction de l’algorithme initial ne nécessite aucun raffinement. La deuxième


instruction nécessite une clarification. On doit préciser le barème utilisé pour le calcule de la
commission. L’instruction « Imprimer le montant des ventes et de la commission » a été
divisée en deux instructions :

Imprimer le montant des ventes


Imprimer le montant de la commission

L’algorithme est maintenant suffisamment détaillé. Une expression complète dans le langage
algorithmique est la suivante :

algorithme Paie ;
var
Ventes, Commission : réel ;
début
/*Lecture du montant des ventes*/
Écrire('Quel est le montant des ventes ? ') ;
Lire(Ventes) ;
/*Calculer la commission*/
Commission  5000 + 0.10*Ventes,
/*Imprimer le montant des ventes et de la commission*/
Écrire('Ventes : ', Ventes) ;
Écrire('Commission : ', Commission) ;

Dr Ndi Nyoungui André


27 Algorithmique et structures de données

fin.

Les commentaires contenus dans notre algorithme reflètent la définition du problème et la


description initiale de l’algorithme. On peut alors regarder le processus de développement
d’un algorithme comme étant un raffinement par étapes des commentaires contenus dans la
version initiale.

Exercices d’apprentissage

Exercice 1.3
Écrire un algorithme qui lit six nombres réels A, B, C, D, E et F et calcule la solution du
système d’équations
Ax + By = C
Dx + Ey = F
en supposant que le système admet une solution unique (déterminant non nul).

Exercice 1.4
Écrire un algorithme qui lit deux nombres réels A et B (A différent de zéro) puis calcule la
solution de l’équation Ax + B = 0.

Exercice 1.5
Écrire un algorithme qui lit le rayon d’une sphère, calcule et affiche le volume de la sphère.

Exercice 1.6
Écrire un algorithme qui calcule et affiche la longueur de l’hypoténuse d’un triangle rectangle
connaissant les longueurs des côtés perpendiculaires.

Exercice 1.7
Écrire un algorithme qui lit la longueur et la largeur d’un rectangle, calcule et affiche le
périmètre et la surface du rectangle.

Exercice 1.8
Écrire un algorithme qui lit le nom et le prénom d’un employé, le nombre d’heures de travail
qu’il a effectuées et son taux horaire puis calcule et affiche, le nom, le prénom et le salaire de
l’employé.

Exercice 1.9
Écrire un algorithme qui lit la note de contrôle continu et la note de synthèse d’un étudiant
dans une certaine unité de valeur puis calcule la moyenne de l’étudiant dans cette unité de
valeur sachant que le contrôle continu compte pour 30% et la note de synthèse pour 70%.

Exercice 1.10
Écrire un algorithme qui convertit les degrés Fahrenheit (F) en degrés Kelvin (K) en utilisant
la relation K = (5/9)(F – 32) + 273.

Exercice 1.11
Écrire un algorithme qui un montant exprimé en francs CFA puis calcule et imprime ses
équivalents en dollars américains et en dollars américains.

Dr Ndi Nyoungui André


28 Algorithmique et structures de données

Exercice 1.12
Une compagnie a quatre divisions qui vendent une variété de produits. La direction de la
compagnie veut connaître quel pourcentage des ventes totales est généré par chaque division.
Écrire un algorithme qui lit le montant des ventes générées par chaque division et imprime le
montant total des ventes ainsi que le pourcentage des ventes de chacune des divisions.

Dr Ndi Nyoungui André


29 Algorithmique et structures de données

CHAPITRE 2

LES STRUCTURES DE SELECTION

2.1 Exécution conditionnelle

Au chapitre précédent, nous avons présentée certaines notions de base de l’algorithmique qui
nous ont permis d’écrire des algorithmes simples impliquant la lecture des données, les
calculs et l’écriture des résultats. Dans les algorithmes que nous avons écrits, les instructions
étaient exécutées séquentiellement jusqu’à ce qu’on arrive à la fin du programme. Cependant,
on a souvent besoin de capacités additionnelles pour décrire complètement les opérations
devant être effectuées par un algorithme. Par exemple, il peut être utile de rendre l’exécution
d’une instruction dépendante d’une certaine condition, ou de choisir d’exécuter une ou
plusieurs instructions en fonction de la valeur d’une certaine condition. Ceci implique la
capacité de changer l’ordre dans lequel les instructions sont exécutées ou la capacité de
choisir, en fonction de ce que l’on veut faire, les instructions qui doivent être exécutées.
Considérons l’exemple suivant : on voudrait écrire un algorithme pour lire deux nombres
réels, diviser le premier par le second et imprimer le quotient.

L‘algorithme apparaît assez simple et une version initiale serait :

début
Lire le dividende et le diviseur
Diviser le dividende par le diviseur
Écrire le résultat
fin.

Lorsque cette version originale est raffinée, on obtient la version suivante :

Exemple 1. Algorithme de division de deux nombres réels

algorithme Division1 ;
var
A, B, Q : réel ;
début
lire(A) ;
lire(B) ;
Q  A/B ;
Écrire(Q) ;
fin.

L’algorithme ci-dessus marchera correctement aussi longtemps que l’on prendra la précaution
de ne pas donner au diviseur la valeur zéro. Comme la division par zéro n’est pas permise,
l’exécution de l’algorithme avec des données de ce genre va résulter en une erreur de
condition. On peut améliorer l’algorithme ci-dessus en testant d’abord la valeur du diviseur
pour voir si elle est égale à zéro et effectuer ensuite la division uniquement si elle n’est pas
nulle. L’algorithme va maintenant ressembler à quelque chose du genre :

Dr Ndi Nyoungui André


30 Algorithmique et structures de données

début
Lire le dividende et le diviseur
si diviseur = 0 alors
Écrire un message indiquant une mauvaise entrée
sinon
Diviser le dividende par le diviseur et écrire le résultat
fin.

La structure si…alors…sinon

Pour écrire l’algorithme ci-dessus, nous avons eu besoin d’un moyen de représenter une
condition devant être testée ; le résultat du test devant déterminer ce que l’on doit faire par la
suite. Nous l’avons fait en utilisant l’instruction si…alors…sinon. Dans le langage
algorithmique, cette instruction prend la forme générale suivante :

si (condition) alors
opérations
sinon
opérations;

La condition entre les mots réservés si et alors est une expression qui peut prendre soit la
valeur vrai soit la valeur faux. Si elle prend la valeur « vrai » alors les opérations qui suivent
la clause alors sont effectuées. Si elle prend la valeur faux alors les opérations qui suivent la
clause sinon sont effectuées.

Dans notre exemple, la condition correspond à l’expression :

Diviseur = 0

L’opération à effectuer si la condition prend la valeur vrai est :

Écrire un message indiquant une erreur de donnée

L’opération à effectuer si la condition prend la valeur faux est :

Diviser le dividende par le diviseur et écrire le résultat

Pour écrire correctement cet algorithme, nous avons utilisé l’instruction si… alors…sinon
dont la forme générale est la suivante :

si (condition) alors
instruction1
sinon
instruction2 ;

où instruction1 et instruction2 sont des instructions simples ou composées. Dans ce dernier


cas, la forme générale de la structure si…alors…sinon est :

Dr Ndi Nyoungui André


31 Algorithmique et structures de données

si (condition) alors
début
instruction1 ;
instruction2 ;

instructionn ;
fin
sinon
début
instruction1 ;
instruction2 ;

instructionm ;
fin ;

Cette instruction s’exécute de la manière suivante :

1. La condition qui se trouve entre les mots réservés si et alors est évaluée.
2. Si elle a la valeur « vrai », on exécute l’instruction qui suit la clause alors et l’exécution
continue avec l’instruction suivante (celle qui suit l’instruction si). Si elle a la valeur
« faux », on exécute l’instruction qui suit la clause sinon et l’exécution continue avec
l’instruction suivante.

La figure 2.1 présente l’organigramme de la structure si…alors…sinon.

Figure 2.1. Organigramme de la structure si…alors…sinon

vrai expression-logique faux

instruction 1 instruction 2

Notre algorithme peut maintenant s’écrire de la manière suivante :

algorithme Division2 ;
var
A, B : réel ;
Début
écrire('Quelle est la valeur du dividende ? ') ;
lire(A) ;
écrire('Quelle est la valeur du diviseur ? ') ;
lire(B) ;

Dr Ndi Nyoungui André


32 Algorithmique et structures de données

si B = 0 alors
Écrire('Division impossible')
sinon
Écrire(A, ' / ', B, ' = ', A/B) ;
fin.

Le type logique ou booléen

Comme nous venons de le voir, l’instruction si…alors…sinon permet de choisir laquelle


entre deux instructions on doit exécuter en fonction de la valeur de vérité d’une condition.
Pour faciliter notre travail avec des expressions qui prennent soit la valeur « vrai », soit la
valeur « faux », le langage algorithmique offre un type de données standard composé
exactement de deux valeurs possibles. Ces valeurs sont {vrai, faux}.

Les variables qui ne peuvent prendre que ces deux valeurs sont souvent appelées des variables
logiques ou booléennes, en l’honneur du mathématicien anglais du XVII ème siècle, George
Boole, qui a développé l’algèbre de la logique.

Pour déclarer une variable de type booléen ou logique, on utilise une déclaration de variable
de la forme suivante :

var
possible, trouvé : booléen ;

Les variables possible et trouvé ont été déclarées comme des variables logiques et peuvent
maintenant recevoir les valeurs vrai ou faux. Les affectations

possible  vrai ;
trouvé  faux ;

sont toutes valides. Pour imprimer la valeur d’une variable logique, on l’inclut dans la liste de
sortie d’une instruction d’écriture. Si la variable possible a la valeur vrai, l’instruction :

écrire(possible)

produira le résultat :

vrai

Les variables booléennes ne peuvent toutefois pas être utilisées dans la liste d’entrée d’une
instruction de lecture. L’instruction de lecture suivant n’est pas valide si trouvé a été déclarée
comme étant de type booléen, comme ci-dessus.

lire(trouvé) ;

Il existe cependant des astuces que l’on peut utiliser pour lire un caractère et l’interpréter
comme une valeur booléenne de telle sorte que des affectations indirectes puissent être
effectuées aux variables booléennes. Considérons par exemple le problème qui consiste à lire

Dr Ndi Nyoungui André


33 Algorithmique et structures de données

le nom de l’utilisateur et à lui demander s’il est marié ou non. S’il est marié, on imprime le
message « … est marié ». S’il est célibataire on imprime le message « … est célibataire ».

algorithme salutation ;
var
nom : chaîne ;
marié : booléen ;
Ch : char ;
début
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Êtes-vous marié (O ou N) ? ') ;
lire(Ch) ;
si Ch = 'O' alors
marié  vrai
sinon
marié  faux ;
si marié alors
écrire(nom, 'est marié')
sinon
écrire(nom, 'est célibataire') ;
fin.

Les opérateurs relationnels

Les valeurs booléennes proviennent habituellement de l’évaluation des expressions qui


impliquent des comparaisons. Par exemple, l’expression « Diviseur = 0 » que nous avons
utilisée ci-dessus produit une valeur logique parce qu’elle est soit vrai soit faux selon que la
valeur de Diviseur est égale à zéro ou non. Une expression qui produit une valeur booléenne
égale à vrai ou à faux est appelée une expression logique (booléenne). Ainsi, Diviseur = 0 est
une expression logique. Le symbole d’égalité (=) dans l’expression est appelé un opérateur
relationnel. Le langage algorithmique comprend six opérateurs relationnels présentés dans le
tableau 2.1 ci-dessous.

Tableau 2.1. Opérateurs relationnels

Opérateur Signification
= est égal à
 est différent de (n’est pas égal à)
 est inférieur à
 est supérieur à
 est inférieur ou égal à
 est supérieur ou égal à

Lorsque ces opérateurs sont utilisés avec des expressions entières ou réelles, elles produisent
des valeurs booléennes. Ainsi, on a :

Expression Valeur

Dr Ndi Nyoungui André


34 Algorithmique et structures de données

5=6 faux
2<4 vrai
3.6  4.1 vrai
7.1  7.0 faux
3+5=6 faux

Les opérateurs relationnels ont une priorité inférieure à celle des opérateurs arithmétiques ;
ainsi lorsque les deux types d’opérateurs sont impliqués dans une expression, toutes les
opérations arithmétiques sont d’abord effectuées avant que les opérateurs relationnels ne
soient appliqués. Les règles de précédence des opérateurs arithmétiques et relationnels, pris
ensemble, sont :

Tableau 2.2. Précédence des opérateurs arithmétiques et relationnels

Précédence Opérateurs
1 *, /, div, mod
2 +, -
3 =, , <, >, , 

Les exemples suivants illustrent l’évaluation d’expressions incluant les opérateurs relationnels
et arithmétiques :

Exemple 1 : 5 + 6 < 3 * 5 div 2

Expression évaluée Résultat Reste à évaluer


3*5 15 5 + 6 < 15 div 2
15 div 2 7 5+6<7
5+6 11 11 < 7
11 < 7 faux

Regardons maintenant un autre exemple impliquant des parenthèses :

Exemple 2 : 5 mod 2  (7 + 5) div 10

Expression évaluée Résultat Reste à évaluer


7+5 12 5 mod 2  12 div 10
5 mod 2 1 1  12 div 10
12 div 10 1 11
11 vrai

Comme le résultat de l’évaluation d’une expression booléenne est une valeur booléenne, il
peut être affecté à une variable booléenne. Considérons par exemple le segment de code
suivant :

variable
p, q : booléen ;
x, y : entier ;
début

Dr Ndi Nyoungui André


35 Algorithmique et structures de données

x  5 ;
y  0 ;
p  x = y ;
q  x + y  x ;
….
fin ;

Après l’exécution de ces instructions p est faux et q est vrai. L’instruction d’affectation

p  x = y ;

indique que l’expression à droite de l’opérateur d’affectation doit être évaluée et le résultat
affecté à p. Comme x = y est faux, la valeur faux est affectée à p.

Les opérateurs logiques

Les conditions et les variables booléennes peuvent être combinées en utilisant des opérateurs
logiques ou booléens pour produire d’autres expressions logiques. Ces opérateurs sont :

 le NON logique (NOT),


 le ET logique (AND),
 le OU logique (OR)
 le OU exclusif (XOR).

Les opérateurs binaires « OU », « ET » et « XOR » sont utilisés pour combiner des conditions
afin d’obtenir des conditions composées. L’opérateur unaire « NON » est utilisé pour inverser
la valeur de vérité d’une expression ou d’une variable booléenne. Les tables de vérité de ces
quatre opérateurs sont définies de la manière suivante :

Tableau 2.3. Tables de vérité des quatre opérateurs logiques

exp non (exp)


faux vrai
vrai faux

exp1 exp2 exp1 et exp2


faux faux faux
faux vrai faux
Vrai faux faux
Vrai vrai vrai

exp1 exp2 exp1 ou exp2


faux faux faux
faux vrai vrai
vrai faux vrai
vrai vrai vrai

exp1 exp2 exp1 xor exp2


faux faux faux

Dr Ndi Nyoungui André


36 Algorithmique et structures de données

faux vrai vrai


vrai faux vrai
vrai vrai faux

Voici quelques exemples où les opérateurs logiques utilisés pour construire des conditions
composées :

Expression Valeur
(15  20) ou (55 = 33) vrai
(15  20) et (55 = 33) faux
non (15  20) faux

La précédence des opérateurs logiques pris ensemble avec les opérateurs arithmétiques et
relationnels est :

Tableau 2.4. Précédence des opérateurs logiques, arithmétiques et relationnels

Précédence Opérateurs
1 NON
2 *, /, div, mod, ET
3 +, -, OU, XOR
4 =, , <, >, , 

A cause de la précédence des opérateurs logiques, on utilisera des parenthèses pour écrire les
expressions arithmétiques ou logiques qui impliquent des opérateurs logiques.

L’exemple suivant illustre l’évaluation d’une expression booléenne impliquant les trois types
d’opérateurs.

Exemple 3 : (5 + 3  3 –1) ou (7 + 5  12 div 2)

Expression évaluée Résultat Reste à évaluer


12 div 2 6 (5 + 3  3 – 1) ou (7 + 5  6)
5+3 8 (8  3 – 1) ou (7 + 5  6)
3–1 2 (8  2) ou (7 + 5  6)
7+5 12 (8  2) ou (12  6)
82 vrai vrai ou (12  6)
12  6 faux vrai ou faux
vrai ou faux vrai

Les expressions entre parenthèses sont évaluées en premier. Remarquer que l’expression

E = 12 < 14 et 2 = 5

est une expression invalide. D’après les règles de précédence décrites précédemment,
l’opérateur et a la plus grande précédence. Donc l’expression à évaluer en premier sera 14 et

Dr Ndi Nyoungui André


37 Algorithmique et structures de données

12. Ceci est une expression non sens car et est un opérateur booléen alors que 14 et 2 ne sont
pas des valeurs booléennes.

Conditions composées

Les conditions composées sont souvent utilisées pour rendre plus compréhensibles les
algorithmes ayant des instructions si imbriquées complexes. Par exemple, la condition
imbriquée

si a < 4 alors
si b > 0 alors
w  a*a – 2*b
sinon
si a = 3 alors
w  a*a – 2*b

peut être écrite en utilisant une condition composée comme suit :

si ((a < 4) et (b > 0)) ou (a = 3) alors


w  a*a – 2*b ;

Comme premier exemple d’algorithme impliquant des conditions composées, considérons le


problème qui consiste à lire trois nombres et à afficher le plus grand de ces nombres.

algorithme max3;
var
a, b, c: réel;
début
écrire('Entrez les valeurs de a, b et c') ;
lire(a, b, c) ;
si (a  b) et (a  c) alors
écrire('Le plus grand est : ', a)
sinon
si (b  a) et (b  c) alors
écrire('Le plus grand est : ', b)
sinon
écrire('Le plus grand est : ', c) ;
fin.

Le deuxième exemple porte sur la procédure de vérification de la position d’un client.


Lorsqu’un client veut faire un achat avec sa carte de crédit, le vendeur détermine ce qu’il faut
faire sur la base des informations suivantes : si l’achat ne dépasse pas 100000 francs, le tabac
ne fait pas partie des marchandises achetées et l’acheteur a sur lui sa carte de crédit, alors
l’achat est approuvé, sinon le vendeur vérifie au niveau du département du crédit.

algorithme Vérification ;
var
montant : réel ;
tabac, carte : char ;

Dr Ndi Nyoungui André


38 Algorithmique et structures de données

début
écrire('Quel est le montant des achats ? ') ;
lire(montant) ;
écrire('Possédez-vous votre carte de crédit (O ou N) ? ') ;
lire(Carte) ;
écrire('Avez-vous pris du tabac (O ou N) ? ') ;
lire(tabac) ;
si (montant < 100000) et (carte = 'O') et (tabac = 'N') alors
écrire('Achat approuvé')
sinon
écrire('Vérifier au niveau du département du crédit') ;
fin.

Exercices d’apprentissage

Exercice 2.1
Si une expression booléenne utilise des opérateurs logiques tels que ET et OU, ainsi que des
opérateurs relationnels, pourquoi doit on utiliser des parenthèses ?

Exercice 2.2
Lesquelles des expressions booléennes suivantes sont logiquement équivalentes ? p et q sont
deux variables booléennes.
(a) p  q (b) (p et non q) ou (non p et q)
(c) non(p = q) (d) (p ou q) ou non(p et q)

Exercice 2.3
Réécrire chacune des instructions suivantes sans utiliser des conditions composées.

(a) si (x < y) ou (w < z) alors


écrire('Bonjour')
sinon
Écrire('Bonsoir') ;

(b) si ((a  b) ou (c = 5)) et (d = 1) alors


Écrire('Bonjour')
sinon
Écrire('Bonsoir') ;

(c) si ((a  b) et (c = 5)) ou (d = 1) alors


Écrire('Bonjour')
sinon
Écrire('Bonsoir') ;

(d) si ((a  b) et (c = 5)) et (d = 1) alors


Écrire('Bonjour')
sinon
Écrire('Bonsoir') ;

Autres exemples d’algorithmes

Dr Ndi Nyoungui André


39 Algorithmique et structures de données

Exemple 1. Écrire un algorithme pour calculer le salaire hebdomadaire d’un employé


connaissant le taux horaire et le nombre d’heures de travail effectuées par cet employé. Le
salaire est calculé en utilisant le barème suivant : les quarante premières heures de travail sont
considérées comme des heures régulières et sont payées au taux normal ; les heures au-dessus
des quarante premières heures sont considérées comme des heures complémentaires et sont
payées une fois et demie le taux horaire.

algorithme Paie ;
var
HoursWorked, TauxHoraire : réel ;
Overtime, Régulier : réel ;
Salaire : réel ;
début
écrire('Heures effectuées : ')
lire(HoursWorked) ;
écrire('Taux horaire : ')
lire(TauxHoraire) ;
si (HoursWorked > 40.0) alors
Overtime  HoursWorked – 40.0
sinon
Overtime  0 ;
Régulier  HoursWorked – Overtime ;
Salaire  Régulier * Taux + Overtime * TauxHoraire * 1.5 ;
Écrire('Salaire : ', Salaire) ;
fin.

Exemple 2. Écrire un algorithme qui lit trois nombres réels et détermine si ces nombres
constituent les côtés d’un triangle rectangle.

algorithme TriangleRectangle ;
var
a, b, c : entier ;
début
écrire('Quelles sont les valeurs de a, b et c ? ')
lire(a, b, c) ;
si (a*a + b*b = c*c) ou (a*a + c*c = b*b) ou (b*b + c*c = a*a) alors
Écrire('Les nombres forment un triangle rectangle')
sinon
Écrire('Les nombres ne forment pas un triangle rectangle') ;
fin.

La structure si…alors

Comme nous l’avons vu, la construction si…alors…sinon est utilisée pour choisir laquelle de
deux instructions on doit exécuter sur la base de la valeur de vérité d’une expression logique.
Toutefois, l’instruction de sélection peut aussi être utilisée sans la clause sinon. Dans ce cas,
on parlera de la structure si…alors dont la forme générale est la suivante :

Dr Ndi Nyoungui André


40 Algorithmique et structures de données

si (condition) alors
instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, on a :

si (condition) alors
début
instruction1 ;
instruction2

instructionn ;
fin ;

L’organigramme de la structure si…alors est présenté dans la figure 2.2.

Figure 2.2. Organigramme de la structure si…alors

vrai
expression-logique

instruction faux

L’instruction si … alors est utile lorsqu’on veut effectuer une action si une certaine condition
est vraie et ignorer simplement cette action si la condition est fausse. Dans ce cas, la clause
sinon est superflue et peut être omise. Par exemple, considérons une autre version de
l’algorithme de paie ci-dessus.

algorithme Paie2 ;
var
HoursWorked, TauxHoraire : réel ;
Overtime, Régulier, Salaire : réel ;
début
écrire('Heures effectuées : ')
lireln(HoursWorked) ;
écrire('Taux horaire : ')
lireln(TauxHoraire) ;
Overtime  0 ;
si (HoursWorked > 40.0) alors
Overtime  HoursWorked – 40.0;
Régulier  HoursWorked – Overtime ;
Salaire  Régulier * TauxHoraire + Overtime * TauxHoraire * 1.5 ;
Écrire('Salaire : ', Salaire) ;
fin.

Dr Ndi Nyoungui André


41 Algorithmique et structures de données

On commence par initialiser Overtime à zéro, ensuite on vérifie si l’employé a effectué plus
de 40 heures de travail. Si l’employé a effectué plus de 40 heures, on calcule Complémentaire
sinon on ne fait rien. Quel que soit le résultat, on calcule ensuite le nombre d’heures
complémentaires effectuées par l’employé et enfin on calcule le salaire de ce dernier.
Comparer cet algorithme avec la version précédente où on utilise la construction si … alors
… sinon pour contrôler l’ordre d’exécution.

Les instructions de sélection imbriquées

On parle d’instructions de sélection imbriquées lorsque dans une instruction si…alors ou si…
alors…sinon, l’instruction qui suit la clause alors ou la clause sinon est elle-même une
instruction de sélection. Les instructions de sélection imbriquées permettent de tester des
conditions en cascade afin de déterminer l’instruction ou le groupe d’instructions qui doit être
exécuté. Considérons par exemple le problème qui consiste à lire deux nombres entiers et
imprimer le plus grand de ces deux nombres. Si les deux nombres sont égaux, on imprime le
message « Les deux nombres sont égaux ». Un algorithme pour faire cela est le suivant :

algorithme Compare ;
var
x, y : entier ;
début
lire(x) ;
lire(y) ;
si (x > y) alors
écrire ('Le plus grand nombre est : ', x)
sinon
si (x = y) alors
écrire('Les deux nombres sont égaux')
sinon
écrire('Le plus grand nombre est : ', y)
fin.

Un examen de cet algorithme nous amène à remarquer que nous avons eu besoin d’utiliser
une structure de sélection qui se traduit par deux instructions si…alors…sinon écrites en
cascade. Il s’agit en réalité d’une seule instruction si…alors…sinon dans laquelle
l’instruction qui suit la clause sinon est elle-même une instruction si…alors…sinon. On dira
alors que l’on à fairevrai
à des instructions si…alors…sinon imbriquées.
faux
x>y
L’organigramme correspondant à cette instruction si … alors … sinon imbriquée est présenté
à la figure 2.3 ci-dessous.
vrai faux
FigureLe2.3. Organigramme de l’algorithme de comparaison x >deux
de y nombres.
plus grand est x

Le plus grand est y


Les des deux nombres
sont égaux

Dr Ndi Nyoungui André


42 Algorithmique et structures de données

L’instruction de sélection multiple

Les conditions composées sont particulièrement utiles dans les situations où on a besoins de
tester plusieurs conditions pour décider laquelle de deux instructions on doit effectuer.
Lorsqu’on doit choisir entre plusieurs instructions, on peut souvent utiliser l’instruction à
choix multiples. Cette instruction prend la forme générale :

case (expression) de
liste-valeurs1 : instruction1 ;
liste-valeurs2 : instruction2 ;
.
.
.
liste-valeursn : instructionn ;
fin;

L’expression comprise entre les mots réservés case et de est l’expression de sélection. C’est la
valeur prise par cette expression qui détermine l’instruction qui doit être exécutée. liste-
valeurs1, …, liste-valeurs1 énumèrent les différentes valeurs que peut prendre l’expression de
sélection.

Considérons par exemple l’ensemble d’instructions « si » imbriquées suivant.

si (opération = 1) alors
CA+B
sinon
si (opération = 2) alors
CA–B
sinon
si (opération = 3) alors
C  A div B
sinon

Dr Ndi Nyoungui André


43 Algorithmique et structures de données

si (opération = 4) alors
C  A * B ;

En utilisant une instruction de sélection multiple, nous pouvons réécrire cette instruction de
la manière suivante :

case (opération) de
1 : C  A + B ;
2:CA–B;
3 : C  A div B;
4 : C  A * B ;
fin ;

Quand on exécute une instruction de sélection multiple, l’expression de sélection est évaluée.
Dans notre exemple, la variable code est l’expression de sélection. La valeur de l’expression
de sélection doit être égale à l’une des valeurs énumérées dans le corps de l’instruction.
L’instruction qui sera exécutée est celle qui correspond à la liste dans laquelle se trouve la
valeur de l’expression de sélection. Si la valeur de l’expression de sélection ne se trouve dans
aucune des listes, aucune instruction ne sera exécutée. Pour prendre en compte ce genre de
situations, on peut ajouter une clause sinon dans une instruction de sélection multiple pour
spécifier une instruction qui prendre en charge ces cas particuliers.

case (expression) de
liste-valeurs1 : instruction1 ;
liste-valeurs2 : instruction2 ;
.
.
.
liste-valeursn : instructionn ;
sinon
autre-instruction ;
fin ;

On peut aussi utiliser la convention suivante : pour prévoir que l’expression de sélection
puisse prendre une valeur différente de toutes des valeurs attendues, on introduit une étiquette
spéciale, que nous appellerons par convention défaut, qui sera la valeur par défaut de
l’expression de sélection lorsque sa valeur effective ne sera pas égale à l’une quelconque des
étiquettes listées.

case (expression) de
liste-valeurs1 : instruction1 ;
liste-valeurs2 : instruction2 ;
.
.
.
liste-valeursn : instructionn ;
défaut : autre-instruction ;
fin ;

Dr Ndi Nyoungui André


44 Algorithmique et structures de données

L’instruction de sélection multiple peut aussi être utilisée dans les situations où on travaille
avec un grand nombre de valeurs possibles. Par exemple, une mention est attribuée à un
étudiant sur la base du barème suivant :

Mention Note inférieure


A 90
B 80
C 70
D 60
E inférieur à 60

On peut penser au premier abord que l’on doit lister toutes les valeurs possibles des notes (on
suppose que les notes varient de 0 à 100) dans les étiquettes. Cependant, ceci n’est pas
nécessaire. On peut réduire considérablement le nombre d’étiquettes nécessaires en utilisant la
division euclidienne par 10, ce qui permet de ne plus considérer que le reste de la division
euclidienne de la note par 10.
L’algorithme ci-dessous lit une note et affiche la mention correspondante en utilisant une
instruction de sélection multiple.

algorithme Mention ;
var
note : entier ;
début
écrire('Quelle est la note ? ') ;
lire(note) ;
si (note < 0) ou (note > 100) alors
Écrire('Mauvaise donnée')
sinon
case (note div 10) de
10, 9 : Écrire('A') ;
8 : Écrire('B') ;
7 : Écrire('C') ;
6 : Écrire('D') ;
5, 4, 3, 2, 1, 0 : Écrire('F') ;
fin ;
fin.

On peut réécrire cet algorithme en utilisant des instructions si … alors … sinon imbriquées.
L’algorithme devient alors :

algorithme Mention ;
var
note : entier ;
début
écrire('Quelle est la note ? ') ;
lire(note) ;
si (note < 0) ou (note > 100) alors
Écrire('Mauvaise donnée')
sinon

Dr Ndi Nyoungui André


45 Algorithmique et structures de données

si note < 60 alors


Écrire('F')
sinon
si note < 70 alors
Écrire('D')
sinon
si note < 80 alors
Écrire('C')
sinon
si note < 90 alors
Écrire('B')
sinon
Écrire('A') ;
fin.

La lisibilité des algorithmes

Dans les sections précédentes nous avons vu comment l’indentation aide à rendre les
instructions si…alors…sinon imbriquées faciles à comprendre. Nous avons en effet suivi des
conventions d’indentation dans tous nos algorithmes. Ces conventions sont strictement
réservées au lecteur humain car le compilateur ignore toutes ces indentations au moment de la
compilation du programme. Par exemple, le code

si (x < 5) alors si (x = 2) alors écrire (x) sinon écrire(x*x) ; lire(x) ;

a exactement la même signification que le code suivant pour le compilateur

si (x < 5) alors
si (x = 2) alors
écrire (x)
sinon
écrire(x*x) ;
lire(x) ;

Cependant cette deuxième version est clairement plus facile à comprendre par une lecteur
humain. La deuxième fait ressortir la structure de l’instruction si…alors…sinon, alors que la
première ne fournit aucune information de ce genre.

Évidemment, il y a différentes façons d’indenter les instructions dans un algorithme pour les
rendre plus lisibles. Les conventions que nous avons utilisées sont les plus courantes. Nous
sommes libres d’utiliser nos propres conventions, mais il faut garder à l’esprit que le but de
l’indentation est d’améliorer la lisibilité des algorithmes. Les conventions que nous avons
utilisées sont les suivantes :

Convention 1. Aligner sur la marge gauche l’en-tête de l’algorithme, les mots réservés
annonçant les différentes sections de déclaration et de définition ainsi le premier début et le
dernier fin. Par exemple :

algorithme nomalgorithme ;

Dr Ndi Nyoungui André


46 Algorithmique et structures de données

const

var

début

fin.

Convention 2. Indenter toutes les définitions et déclarations par rapport à aux mots réservés
correspondants. Par exemple

const
pi = 3.1416 ;
var
x, y : entier ;
p : booléen ;

Convention 3. Indenter les instructions par rapport à la structure début…fin pour


l’algorithme entier aussi bien que par rapport à la structure début…fin pour les instructions
composées. Par exemple,

début
lire(x) ;
écrire(x) ;
fin.

Convention 4. Ne pas écrire plusieurs instructions dans une même ligne. Par exemple,
l’écriture :

lire(x) ;
écrire(x) ;

est bien meilleure que :

lire(x) ; écrire(x) ;

Convention 5. Indenter les instructions si…alors…sinon de la manière suivante :

si (condition) alors
instruction1
sinon
instruction2 ;

ou de la manière suivante si on utilise des instructions composées :

si (condition) alors
début
instruction1 ;
instruction2 ;

Dr Ndi Nyoungui André


47 Algorithmique et structures de données

...
instructionm ;
fin
sinon
début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Au fur et mesure que nous étudierons de nouvelles instructions algorithmiques, nous allons
toujours utiliser ces conventions d’indentation pour les rendre aussi lisibles que possible.

Exercices d’apprentissage

Exercice 2.4
Les longueurs des côtés d’un triangle sont stockées dans les variables réelles a, b et c. Pour
que a, b et c soient effectivement correctes, la somme de deux quelconque des trois nombres
doit être strictement supérieure au troisième. Écrire un algorithme qui lit trois nombres réels a,
b et c et détermine si a, b et c sont effectivement les côtés d’un triangle. Si oui, alors écrire le
message « Triangle » dans le cas contraire écrire le message « Pas un triangle ».

Exercice 2.5
Une année est bissextile si son millésime est un multiple de 4, sauf les années de début de
siècle qui sont bissextiles si leur millésime est divisible par 400. Écrire un algorithme qui lit
un entier naturel et détermine si cet entier représente une année bissextile.

Exercice 2.6
Une boulangerie est ouverte de 7 heures à 13 heures et de 16 heures à 19 heures, sauf le
dimanche après-midi et le lundi toute la journée. Écrire un algorithme qui lit une heure (un
entier naturel) et un jour (une chaîne de caractère) et détermine si la boulangerie est ouverte le
jour et à l’heure indiqués.

Exercice 2.7
Un entier positif est stocké dans une variable nombre. Utiliser une instruction de sélection
multiple pour imprimer un message indiquant si le nombre est paire ou impaire.

Exercice 2.8
Écrire un programme pour résoudre le système d’équations linéaires d’inconnues x et y :

Ax + By = C
Dx + Ey = F

Exercice 2.9
Écrire un algorithme qui lit un nombre représentant l’âge d’une personne et affiche le
message « vous êtes mineur » si son âge est inférieur à 18 ans et le message « vous êtes
majeur » dans le cas contraire.

Dr Ndi Nyoungui André


48 Algorithmique et structures de données

Exercice 2.10
Écrire un algorithme qui lit le nom et le sexe d’une personne et affiche le message « Bonjour,
Monsieur … » si la personne est de sexe masculin et le message « Bonjour, Madame … » si la
personne est de sexe féminin.

Exercice 2.11
Écrire un algorithme qui le nom, le sexe et le statut matrimonial d’une personne et affiche le
message « Bonjour monsieur … » si la personne est de sexe masculin, le message « Bonjour
mademoiselle … » si la personne est une demoiselle et le message « Bonjour madame … » si
la personne est une dame.

Exercice 2.12
Écrire un algorithme qui lit trois nombres et imprime le plus grand des trois nombres.

Exercice 2.13
Écrire un algorithme qui lit quatre nombres et imprime le plus grand des quatre nombres.

Exercice 2.14
Écrire un algorithme qui lit deux nombres réels A et B puis calcule la solution réelle de
l’équation Ax + B = 0.

Exercice 2.15
Écrire un algorithme qui lit trois nombres réels a, b et c puis calcule les solutions réelles de
l’équation quadratique ax 2  bx  c  0 .

Exercice 2.16
Les tarifs d’affranchissement d’une lettre sont les suivants :
en-dessous de 20g : 280 FCFA,
à partir de 20g, mais en-dessous de 50g : 440 FCFA,
à partir de 50g : 670 FCFA.
Écrire un algorithme qui lit le poids d’une lettre et imprime le montant de l’affranchissement
de la lettre.

Exercice 2.17
La variable réelle Hours contient un nombre compris entre 0 et 100. Écrire un algorithme qui
lit une heure et affiche le message correspondant comme indiqué ci-dessous.

Hours Message
[0..30] Excessive absence
]30..50] Normal
]50..70] Excessive overtime
]70..100] Crazy

En-dessous de 30 kW : 50 F :kW


À partir de 50Kw mais en dessous de 100kW : 75 F/kW

Exercice 2.18
On souhaite calculer le montant des impôts dus par un contribuable en fonction son revenu
imposable et de son nombre de parts fiscales. Les règles de calcul sont les suivantes :

Dr Ndi Nyoungui André


49 Algorithmique et structures de données

 le revenu par part fiscale est égale au quotient du revenu imposable par le nombre
de parts fiscales
 l’impôt par part fiscale est calculé selon le barème suivant :
 0 si le revenu par part fiscale est inférieur à 50000 F ;
 10% sur la tranche du revenu par part fiscale comprise entre 50000 F et
100000 F ;
 25% sur la tranche du revenu par part fiscale comprise entre 100000 F et
200000 F ; 
 50% sur le revenu par part fiscale est qui dépasse 200000 F 
 l’impôt total est égal au nombre de parts fiscales multiplié par l’impôt par part
fiscale.

Écrire un algorithme qui lit le revenu imposable et le nombre de parts fiscales d’un
contribuable puis calcule le montant des impôts dus par ce contribuable.

2.6 Études de cas

Calcul de l’impôt du par un contribuable

Le problème est d’écrire un algorithme pour calculer le montant des impôts dus par un
contribuable, connaissant son revenu imposable. Pour calculer les impôts, nous allons utiliser
le barème présenté dans le tableau ci-dessous

Revenu imposable Taux des impôts

Inférieur à 25000 F 0F

Supérieur à Mais inférieur à Du montant au-dessus de


25000 F 35000 F 14% 25000 F
35000 F 45000 F 500 F + 16% 35000 F
45000 F 65000 F 750 F + 18% 45000 F
65000 F 85000 F 1500 F + 19% 650000 F
85000 F 105000 F 1750 F + 21% 850000 F
105000 F 125000 F 2500 F + 24% 105000 F
125000 F 175000 F 3000 F + 26% 125000 F

Ceci est un barème allégé pour l’objectif du problème. Il ne sera pas difficile de le généraliser
pour traiter un barème plus complet, c’est-à-dire traiter des revenus supérieurs à 175000 F.

Calcul du jour de l’année

On veut écrire un algorithme pour calculer le jour de l’année connaissant le mois, le jour du
mois et l’année. Par exemple, si l’entrée de l’algorithme est « 2 7 1983 » la sortie de
l’algorithme serait « le 2/7/1983 est le 38 ème jour de 1983 ».

Dr Ndi Nyoungui André


50 Algorithmique et structures de données

CHAPITRE 3

LES STRUCTURES DE REPETITION

3.1 Le concept de boucle

Les algorithmes que nous avons écrits jusque là ont une caractéristique commune : ils ne
peuvent traiter qu’un seul ensemble de données pour une exécution donnée. Par exemple,
l’algorithme de Paie que nous avons écrit ne peut calculer en une exécution que le salaire d’un
seul employé. Si on souhaite calculer le salaire de plusieurs employés, on devra exécuter le
programme autant de fois que nécessaire ! Nous serions plus à l’aise si nous avions la
possibilité de communiquer à la machine les informations sur la liste des employés dont on
veut calculer les salaires, et laisser le programme traiter automatiquement toute la liste. En un
mot, on aimerait que toutes ou certaines actions de l’algorithme soient exécutées plus d’une
fois, mais avec des données différentes.

Un ensemble d’étapes exécutées de façon répétitive dans un algorithme est appelé une boucle.
On peut donc définir une boucle comme une séquence d’instructions que l’on veut exécuter
une ou plusieurs fois.

On a souvent besoin de répéter l’exécution de certaines étapes d’un algorithme pour deux
raisons principales :

a) On a besoin d’effectuer les mêmes opérations sur des données différentes,


b) On a besoin de répéter un ensemble d’opérations pour résoudre un problème.

La situation que nous avons décrite avec l’algorithme de calcul de la paie des employés tombe
dans la première catégorie. On est intéressé par la répétition des opérations qui consistent à
lire le taux horaire et le nombre d’heures de travail effectuées par un employé, à calculer et à
imprimer son salaire. On rencontrera dans la suite des exemples de la deuxième catégorie où
on commence avec une seule valeur et on répète un ensemble de calculs jusqu’à ce que l’on
obtienne le résultat désiré.

On distingue deux formes de boucles : les boucles conditionnelles ou indéterminées et les


boucles contrôlées par un compteur. Une boucle conditionnelle est répétée tant que ou
jusqu’à ce qu’une certaine condition soit vraie. Une boucle contrôlée par un compteur est
répétée un nombre de fois fixé d’avance, les itérations étant comptées par une variable de
contrôle.

3.2 Les boucles conditionnelles

Supposons que l’on veuille écrire un algorithme pour calculer la moyenne de 10 nombres.
Une première version de notre algorithme peut être la suivante :

début
Lire dix nombres
Calculer la moyenne des dix nombres
Écrire la moyenne

Dr Ndi Nyoungui André


51 Algorithmique et structures de données

fin.

Cet algorithme est correct mais il nécessite que l’on déclare dix variables, une pour chacun
des nombres dont on veut calculer la moyenne, lire d’abord les valeurs de toutes les variables,
calculer ensuite la moyenne et écrire enfin le résultat. Cette approche n’est pas seulement non
nécessaire, mais elle peut devenir intolérable si on tente de généraliser cet algorithme pour
calculer la moyenne de mille nombres par exemple.

Une meilleure approche consiste à prendre avantage du fait que l’on n’a pas besoin que tous
les nombres soient disponibles en même temps. Tout ce que nous voulons faire c’est de
conserver un total courant des nombres dont on veut calculer la moyenne. Pour calculer la
moyenne, on divise le total final par le nombre de valeurs que l’on a. Ceci suggère que l’on
lise un nombre à la fois et que l’on ajoute ce nombre au total courant jusqu’à ce que tous les
nombres soient lus. Ensuite on divise le total par dix pour obtenir la moyenne.

Pour s’assurer que tous les nombres sont effectivement lus et que le total est correctement
calculé, on utilise deux variables :
 une variable appelée total dont le rôle est de contenir le total courant des nombres
au fur et mesure qu’ils sont lus. Au départ, total est initialisé à zéro ;
 une variable appelée compteur dont le rôle est de compter les nombres au fur et
mesure de leur lecture. Au départ, compteur est initialisé à zéro.

Notre algorithme peut maintenant avoir la forme suivante :

début
initialiser total à zéro
initialiser compteur à zéro
répéter
Lire un nombre
Ajouter le nombre au total
Ajouter 1 à compteur
jusquà compteur égal 10
Calculer la moyenne
Écrire la moyenne
fin.

Dans cet algorithme, la boucle conditionnelle est exprimée en délimitant les instructions à
répéter par les mots réservés répéter et jusquà. Cet algorithme spécifie que les trois actions,

Lire un nombre
Ajouter le nombre au total
Ajouter 1 à compteur

doivent être répétées jusqu’à ce que la condition « compteur égal 10 » soit vraie. Ces trois
opérations constituent le corps de la boucle et la condition « compteur égal 10 » est appelée la
condition de terminaison. D’abord, le corps de la boucle est exécuté, ensuite la condition de
terminaison est évaluée. Si elle a la valeur vrai, le processus d’exécution quitte la boucle et
continue avec l’instruction qui suit la boucle. Si elle a la valeur faux, les instructions du corps
de la boucle sont exécutées une fois de plus.

Dr Ndi Nyoungui André


52 Algorithmique et structures de données

L’instruction répéter…jusquà

Le langage algorithmique offre une instruction de répétition conditionnelle équivalente à celle


que nous avons utilisée dans notre algorithme. Elle a la forme générale :

répéter
instruction
jusquà (condition) ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, la boucle répéter
…jusquà prend la forme générale

répéter
instruction1 ;
instruction2 ;

instructionn ;
jusquà (condition) ;

Les mots réservés répéter et jusquà sont utilisés pour délimiter le corps de la boucle. Ainsi,
les instructions de la boucle constituent une instruction composée. Ceci est un exemple
d’instruction composée où on n’utilise pas début et fin pour regrouper les instructions. La
condition qui suit le mot réservé jusquà est appelée la condition de terminaison de la
boucle, parce que la boucle se termine lorsque cette condition prend la valeur « vrai ».

L’organigramme correspondant à la boucle répéter … jusquà est présenté dans la figure ci-
dessous.

Figure 3.1. Organigramme de la structure répéter…jusquà

Corps de la boucle

faux vrai
Expression logique

L’instruction répéter … jusquà s’exécute de la manière suivante :

1. Le corps de la boucle est exécuté, puis ;


2. La condition de terminaison de la boucle est évaluée. Si elle a la valeur « vrai », la
boucle est terminée et l’exécution continue avec l’instruction qui suit la boucle. Si elle
a la valeur « faux », on reprend l’exécution du corps de la boucle.

Dr Ndi Nyoungui André


53 Algorithmique et structures de données

Remarques
 La boucle ne se terminera jamais si la condition de terminaison reste indéfiniment
fausse. Il est donc important que le corps de la boucle contienne au moins une
instruction de mise à jour de la condition de terminaison pour garantir que celle-ci
deviendra vraie après un nombre fini d’étapes.

 Une propriété importante de la boucle répéter…jusquà est que le corps de la boucle


est exécuté au moins une fois. Ceci est vrai parce que la condition de terminaison est
testée à la sortie de la boucle.

L’algorithme de calcul de la moyenne de 10 nombres peut donc s’écrire comme suit :

algorithme moyenne ;
var
nombre, total, moy : réel ;
i : entier ;
début
{Initialisation de compteur et total}
i  0 ;
total  0 ;
répéter
lire(nombre) ;
total  total + nombre ;
i  i +1 ;
jusquà (i = 10) ;

{Calcul de la moyenne}
moy  total/10.0 ;
écrire('La moyenne est : ', moy) ;
fin.

Autres exemples

Exemple 1. Écrire un algorithme pour lire les noms d’une liste de 10 personnes et imprimer à
chaque fois le message « Bonjour … ».

algorithme Bonjour ;
var
nom : chaîne ;
i : entier ;
début
i  0 ;
répéter
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Bonjour', nom) ;
i  i + 1 ;
jusquà (i = 10) ;

Dr Ndi Nyoungui André


54 Algorithmique et structures de données

fin.

Exemple 2. Écrire un algorithme pour résoudre une série de dix équations de la forme ax + b
= 0, où a et b sont des nombres réels.

algorithme Equation ;
var
a, b, x : real ;
i : entier ;
début
i  0 ;
répéter
écrire('Quel est la valeur de a ? ') ;
lire(a) ;
écrire('Quel est la valeur de b ? ') ;
lire(b) ;
i  i + 1 ;
si (a  0) alors
debut
x  -b/a ;
écrire('La solution est : ', x)
fin
sinon
si (b = 0) alors
écrire('Équation indéterminée')
sinon
écrire('Équation impossible') ;
jusquà (i = 10) ;
fin.

Exemple 3. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
grand de ces nombres.

L’approche consiste à traiter un nombre à la fois et garder trace de la plus grande valeur déjà
rencontrée. Chaque nouvelle valeur lue est comparée à la plus grande valeur courante. Si elle
est plus grande, elle devient le nouveau plus grand. Si elle est inférieure à la plus grande
valeur courante, on conserve la plus grande valeur courante et on lit la valeur suivante. Au
départ la plus grande valeur courante est initialisée au premier nombre lu.

algorithme maxliste ;
var
nombre, grand : real ;
i : entier ;
début
i  1 ;
lire(nombre) ;
grand  nombre ;
répéter
lire(nombre) ;

Dr Ndi Nyoungui André


55 Algorithmique et structures de données

i  i + 1 ;
si (nombre > grand) alors
grand  nombre ;
jusquà (i = 100) ;
écrire('Le plus grand nombre est : ', grand) ;
fin.

Exemple 4. Écrire un algorithme pour lire une suite de 100 nombres réels et déterminer le
plus petit de ces nombres.

algorithme minliste ;
var
nombre, min : real ;
i : entier ;
début
i  1 ;
lire(nombre) ;
min  nombre ;
répéter
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
jusquà (i = 100) ;
écrire('Le plus petit nombre est : ', min) ;
fin.

Exemple 5. Écrire un algorithme pour lire une suite de 100 nombres réels et déterminer le
plus petit et le plus grand de ces nombres.

algorithme maxmiliste ;
var
nombre, min, max : real ;
i : entier ;
début
i  1 ;
lire(nombre) ;
min  nombre ;
max  nombre ;
répéter
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
si (nombre > max) alors
max  nombre ;
jusquà (i = 100) ;

écrire('Le plus petit nombre est : ', min) ;

Dr Ndi Nyoungui André


56 Algorithmique et structures de données

écrire('Le plus grand nombre est : ', max) ;


fin.

Exemple 6. Écrire un algorithme pour lire une suite de 100 nombres réels et déterminer le
plus petit de ces nombres ainsi que sa position dans la liste. On envisagera le cas de la
première occurrence et le cas de la dernière occurrence.

Algorithme posminliste ;
variable
nombre, min : real ;
i, indice : entier ;
début
i  1 ;
lire(nombre) ;
indice  1 ;
min  nombre ;
répéter
lire(nombre) ;
i  i + 1 ;
si nombre < min alors
début
min  nombre ;
indice  i ;
fin ;
jusquà (i = 100) ;
écrire('Le plus petit nombre est : ', min) ;
écrire('Sa position dans la liste est : ', indice) ;
fin.

Exemple 7. Écrire un algorithme pour traiter une liste de 10 étudiants. Pour chaque étudiant
l’algorithme lit le nom de l’étudiant, sa note de contrôle continu et sa note de synthèse dans
une certaine unité de valeur, calcule la moyenne de l’étudiant et affiche le message «… a pour
moyenne :…). La note de contrôle continu compte pour 30% et la note de synthèse pour 70%.

algorithme gestionote ;
var
nom : chaîne ;
note1, note2, moyenne : real ;
n: entier ;
début
n  0 ;
répéter
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Votre note de contrôle continu ? ') ;
lire(note1) ;
écrire('Votre note de synthèse ? ') ;
lire(note2) ;
moyenne  0.3*note1 + 0.7*note2 ;

Dr Ndi Nyoungui André


57 Algorithmique et structures de données

n  n + 1 ;
écrire(nom, 'a pour moyenne : ', moyenne);
jusquà (n = 10) ;
fin.

Exemple 8. Écrire un algorithme pour calculer les salaires hebdomadaires de n employés


temporaires. Pour chaque employé, le programme demande le nom, le taux horaire et le
nombre d’heures de travail effectuées, puis calcule le salaire de l’employé. Les règles
suivantes sont utilisées pour calculer le salaire d’une employé. Les 40 premières heures sont
considérées comme des heures régulières et payées au taux normal. Les heures effectuées au
delà des 40 premières heures sont considérées comme des heures complémentaires et payées
une fois et demie le taux horaire.

Algorithme Paie ;
variable
nom : chaîne ;
Rate, HoursWorked : réel ;
Salary, Regular, Overtime : réel ;
i, n: entier ;
début
écrire('Nombre employés :') ;
lire(n) ;
i  0 ;
répéter
écrire('Nom employé :') ;
lire(nom) ;
écrire('Taux horaire : ') ;
lire(Rate) ;
écrire('Heures effectuées : ') ;
lire(HoursWorked) ;
i  i + 1 ;
si HoursWorked > 40 alors
Overtime  HoursWorked – 40.0
sinon
Overtime  0 ;

Regular  HoursWorked – Overtime ;


Salary  Rate * Regular + 1.5*Rate*Overtime ;
écrire('Nom :', Nom) ;
écrire('Salaire : ', Salary);
jusquà (i = n) ;
fin.

L’instruction faire…tantque

Le langage algorithmique offre une autre instruction de répétition très proche de l’instruction
répéter…jusquà. Il s’agit de l’instruction faire…tantque dont la forme générale est :

faire

Dr Ndi Nyoungui André


58 Algorithmique et structures de données

instruction
tantque (condition) ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, la boucle faire…
tantque prend la forme générale

faire
instruction1 ;
instruction2 ;

instructionn ;
tantque (condition) ;

Les mots réservés début et fin sont utilisés pour délimiter le corps de la boucle. La condition
qui suit la clause tantque est appelée la condition de répétition de la boucle parce que la
boucle est répétée tant que cette condition a la valeur vrai.
L’organigramme correspondant à la boucle faire…tantque est présenté dans la figure 3.2 ci-
dessous.

Figure 3.2. Organigramme de la structure faire…tantque

Corps de la boucle

vrai faux
Expression logique

La boucle faire…tantque s’exécute de la manière suivante :

1. Le corps de la boucle est exécuté, puis ;


2. La condition de répétition est évaluée. Si elle a la valeur faux, la boucle est terminée et
l’exécution continue avec l’instruction suivante. Si elle a la valeur vrai, on reprend
l’exécution du corps de la boucle.

Remarques
 La boucle ne se terminera jamais si la condition de répétition reste indéfiniment vraie.
Il est donc important que le corps de la boucle contienne au moins une instruction de
mise à jour de la condition de répétition pour garantir que celle-ci deviendra fausse
après un nombre fini d’étapes.

 Une propriété importante de la boucle faire…tantque est que le corps de la boucle est
exécuté au moins une fois. Ceci est vrai parce que la condition de répétition est testée
à la sortie de la boucle.

Dr Ndi Nyoungui André


59 Algorithmique et structures de données

L’algorithme de calcul de la moyenne peut donc s’écrire de la manière suivante en utilisant la


boucle faire…tantque.

algorithme moyliste ;
variable
nombre, total, moy : réel ;
nb : entier ;
début
{Initialisation de compteur et total}
nb  0 ;
total  0 ;
faire
écrire(‘Entrez un nombre :’) ;
lire(nombre) ;
total  total + nombre ;
nb  nb +1 ;
tantque (nb < 10) ;

{Calcul de la moyenne}
moyenne  total/10 ;
écrire('La moyenne est : ', moy) ;
fin.

Exercice 3.1
Généraliser l’algorithme ci-dessus pour lire une suite de n nombres et calculer la moyenne de
ces n nombres ; le nombre n de nombres devant être lu dans l’algorithme.

Autres exemples

Exemple 1. Écrire un algorithme pour lire les noms d’une liste de 10 personnes et écrire à
chaque fois le message « Bonjour … ».

algorithme Bonjour ;
var
nom : chaîne ;
i : entier ;
début
i  0 ;
faire
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Bonjour', nom) ;
i  i + 1 ;
tantque (i < 10) ;
fin.

Exemple 2. Écrire un algorithme pour résoudre une suite de 10 équations de la forme ax + b =


0, où a et b sont des nombres réels.

Dr Ndi Nyoungui André


60 Algorithmique et structures de données

algorithme Equation ;
var
a, b, x : real ;
i : entier ;
début
i  0 ;
faire
écrire('Quel est la valeur de a ? ') ;
lire(a) ;
écrire('Quel est la valeur de b ? ') ;
lire(b) ;
i  i + 1 ;
si (a  0) alors
début
x  -b/a ;
écrire('La solution est : ', x)
fin
sinon
si b = 0 alors
écrire('Équation indéterminée')
sinon
écrire('Équation impossible') ;
tantque (i < 10) ;
fin.

Exemple 3. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
grand de ces nombres.

L’approche consiste à traiter un nombre à la fois et garder trace de la plus grande valeur déjà
rencontrée. Chaque nouvelle valeur lue est comparée à la plus grande valeur courante. Si elle
est plus grande, elle devient le nouveau plus grand. Si elle est inférieure à la plus grande
valeur courante, on conserve la plus grande valeur courante et on lit la valeur suivante. Au
départ la plus grande valeur courante est initialisée au premier nombre lu.

algorithme maxliste ;
var
nombre, grand : real ;
i : entier ;
début
i  1 ;
écrire(‘Entrez un nombre :’) ;
lire(nombre) ;
grand  nombre ;
faire
écrire(‘Entrez un nombre :’) ;
lire(nombre) ;
i  i + 1 ;
si (nombre > grand) alors

Dr Ndi Nyoungui André


61 Algorithmique et structures de données

grand  nombre ;
tantque (i < 100) ;
écrire('Le plus grand nombre est : ', grand) ;
fin.

Exemple 4. Généraliser l’algorithme ci-dessus pour qu’il lise une suite de n nombres et
détermine le plus grand de ces n nombres ; le nombre n de nombres devant être lu dans
l’algorithme.

algorithme maxliste ;
var
nombre, grand : réel ;
n, i : entier ;
début
écrire('Quelle est la valeur de n ?') ;
lire(n) ;
si (n  0) alors
écrire('Valeur positive attendue ! ')
sinon
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  1 ;
grand  nombre ;
faire
écrire(4ntrez un nombre :’) ;
lire(nombre) ;
i  i + 1 ;
si (nombre > grand) alors
grand  nombre ;
tantque (i < n) ;

{Écriture du plus grand nombre}


écrire ('Le plus grand nombre est : ', grand) ;
fin ;
fin.

Exemple 5. Écrire un algorithme pour lire une suite de 100 nombres réels et déterminer le
plus petit de ces nombres.

algorithme minliste ;
var
nombre, min: réel ;
i : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;
min  nombre ;

Dr Ndi Nyoungui André


62 Algorithmique et structures de données

faire
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
tantque (i < 100) ;
écrire('Le plus petit nombre est : ', min) ;
fin.

Exemple 6. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
petit et le plus grand de ces nombres.

algorithme maxminliste ;
var
nombre, min, max : réel ;
i : entier ;
début
i  1 ;
écrire('Entre un nombre : ') ;
lire(nombre) ;
min  nombre ;
max  nombre ;
faire
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
si (nombre > max) alors
max  nombre ;
tantque (i < 100) ;

écrire('Le plus petit nombre est : ', Min) ;


écrire('Le plus grand nombre est : ', Max) ;
fin.

Exemple 7. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
petit de ces nombres ainsi que sa position dans la liste. On envisagera le cas de la première
occurrence et le cas de la dernière occurrence.

algorithme posminliste ;
var
nombre, min : real ;
i, indice : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;

Dr Ndi Nyoungui André


63 Algorithmique et structures de données

indice  1 ;
min  nombre ;
faire
écrire('Entre un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
début
min  nombre ;
indice  i ;
fin ;
tantque (i < 100) ;
écrire('Le plus petit nombre est : ', min) ;
écrire('Sa position dans la liste est : ', indice) ;
fin.

Exemple 8. Écrire un algorithme pour traiter une liste de 10 étudiants. Pour chaque étudiant
l’algorithme lit le nom de l’étudiant, sa note de contrôle continu et sa note de synthèse dans
une certaine unité de valeur, calcule la moyenne de l’étudiant et affiche le message «… a pour
moyenne :…). Le note de contrôle continu compte pour 30% et la note de synthèse pour 70%.

algorithme gestionote;
var
nom : chaîne ;
note1, note2, moyenne : real ;
nb : entier ;
début
nb  0 ;
faire
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Votre note de contrôle continu ? ') ;
lire(note1) ;
écrire('Votre note de synthèse ? ') ;
lire(note2) ;
moyenne  0.3*note1 + 0.7*note2 ;
nb  nb + 1 ;
écrire(nom, 'a pour moyenne : ', moyenne);
tantque (nb < 10) ;
fin.

Exemple 8. Écrire un algorithme pour calculer les salaires hebdomadaires de n employés


temporaires. Pour chaque employé, le programme demande le nom, le taux horaire et le
nombre d’heures de travail effectuées, puis calcule le salaire de l’employé. Les règles
suivantes sont utilisées pour calculer le salaire d’une employé. Les 40 premières heures sont
considérées comme des heures régulières et payées au taux normal. Les heures effectuées au
delà des 40 premières heures sont considérées comme des heures complémentaires et payées
une fois et demie le taux horaire.

Dr Ndi Nyoungui André


64 Algorithmique et structures de données

algorithme Paie ;
var
nom : chaîne ;
Rate, HoursWorked, Salary : réel ;
Regular, Overtime : réel ;
i, n : entier ;
début
écrire('Nombre employés :') ;
lire(n) ;
i  0 ;
faire
écrire('Nom employé : ') ;
lire(nom) ;
écrire('Taux horaire : ') ;
lire(Rate) ;
écrire('Heures effectuées : ') ;
lire(HoursWorked) ;
i  i + 1 ;
si (HoursWorked > 40) alors
Overtime  HoursWorked – 40.0
sinon
Overtime  0 ;
Regular  HoursWorked – Overtime ;
Salary  Rate * Regular + 1.5*Rate*Overtime ;
écrire('Nom : ', nom) ;
écrire('Salaire : ', Salary);
tantque (i < n) ;
fin.

La structure tantque … faire

Le langage algorithmique offre une autre forme d’instruction permettant d’exprimer les
boucles conditionnelles. Il s’agit de l’instruction tantque…faire dont la forme générale est :

tantque (condition) faire


instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, l’instruction
tanque … faire prend la forme générale :

tantque (condition) faire


début
instruction1 ;
instruction2 ;
...
instructionn ;
fin ;

Dr Ndi Nyoungui André


65 Algorithmique et structures de données

L’instruction simple ou composée qui suit le mot réservé faire constitue le corps de la boucle.
La condition entre les mots réservés tantque et faire est la condition de répétition de la
boucle. La figure 2.3 ci-dessous présente l’organigramme de l’instruction tantque…faire.

Figure 2.3. Organigramme de la structure tantque…faire

faux
Expression logique

vrai

Corps de la boucle

L’instruction tantque … faire s’exécute de la manière suivante :

1. On évalue la condition de répétition de la boucle.


2. Si elle a la valeur vrai, on exécute le corps de la boucle et on recommence à l’étape 1.
Si elle a la valeur faux, on quitte la boucle et l’exécution continue avec l’instruction
qui suit la boucle tantque …faire.

Comme dans le cas des instructions répéter…jusquà et faire…tantque, la boucle tantque


…faire ne se terminera jamais si la condition de répétition reste indéfiniment vraie. Il est
donc important que le corps de la boucle contienne au moins une instruction de mise à jour de
la condition de terminaison pour garantir que celle-ci deviendra fausse après un nombre fini
d’étapes.

Au contraire des instructions répéter…jusquà et faire…tantque, la condition de répétition


de l’instruction tantque…faire est évaluée à l’entrée de la boucle plutôt qu’à la sortie. Ainsi,
la corps d’une boucle tantque...faire peut ne pas être exécutée du tout. Cela arrive lorsque la
condition de répétition est initialement fausse. Dans ce cas le corps de la boucle est tout
simplement ignoré et l’exécution continue avec l’instruction suivante. L’expression logique
dans une instruction répéter…jusquà ou dans une instruction faire…tantque est une
condition de terminaison tandis que l’expression logique dans une instruction tantque…
faire est une condition de répétition. Les codes ci-dessous illustrent cette différence.

(*Multiplier les entiers de 1 à 10*)


produit  1 ;
i  1 ;
répéter
produit  produit * i ;
i  i + 1 ;
jusquà (i > 10) ;

Dr Ndi Nyoungui André


66 Algorithmique et structures de données

En réécrivant la boucle répéter…jusquà ci-dessus comme une boucle tantque…faire, on


obtient :

(*Multiplier les entiers de 1 à 10*)


produit  1 ;
i  1 ;
tantque (i  10) faire
début
produit  produit * i ;
i  i + 1 ;
fin ;

En général, une boucle conditionnelle peut être exprimée soit par une instruction répéter…
jusquà, soit par une instruction faire…tantque, soit par une instruction tanque…faire. La
plupart des langages de programmation implémentent une ou deux de ces instructions.
L’avantage d’avoir les différentes variantes est que ceci permet d’utiliser celle qui semble la
plus appropriée pour un problème particulier. Réécrivons maintenant notre algorithme de
calcul de la moyenne de 10 nombres en utilisant la boucle tantque … faire. Dans ce cas
particulier, aucune des instructions n’est plus appropriée que les autres. Cependant, dans
plusieurs applications la boucle tantque … faire est plus appropriée que les deux autres
variantes parce qu’elle autorise que le corps de la boucle ne soit pas exécuté du tout.

algorithme moyliste ;
var
nombre, total, moyenne : réel ;
i : entier ;
début
(*Initialisation de compteur et total*)}
i  0 ;
total  0 ;
tantque i < 10 faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
total  total + nombre ;
i  i + 1 ;
fin ;
(*Calculer et écrire la moyenne*)
moyenne  total/10.0 ;
écrire('La moyenne est : ', moyenne) ;
fin.

Autres exemples

Exemple 1. Écrire un algorithme pour lire les noms d’une liste de 10 personnes et imprimer à
chaque fois le message « Bonjour … ».

algorithme Bonjour ;
var

Dr Ndi Nyoungui André


67 Algorithmique et structures de données

nom : chaîne ;
i : entier ;
début
i  0 ;
tantque (i < 10) faire
début
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Bonjour', nom) ;
i  i + 1 ;
fin ;
fin.

Exemple 2. Écrire un algorithme pour résoudre une suite de 10 équations de la forme ax + b =


0, où a et b sont des nombres réels.

algorithme Equation ;
var
a, b, x : real ;
i : entier ;
début
i  0 ;
tantque i < 10 faire
début
écrire('Quel est la valeur de a ? ') ;
lire(a) ;
écrire('Quel est la valeur de b ? ') ;
lire(b) ;
i  i + 1 ;
si (a  0) alors
début
x  -b/a ;
écrire('La solution est : ', x)
fin
sinon
si b = 0 alors
écrire('Équation indéterminée')
sinon
écrire('Équation impossible') ;
fin ;
fin.

Exemple 3. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
grand de ces nombres.

L’approche consiste à traiter un nombre à la fois et garder trace de la plus grande valeur déjà
rencontrée. Chaque nouvelle valeur lue est comparée à la plus grande valeur courante. Si elle
est plus grande, elle devient le nouveau plus grand. Si elle est inférieure à la plus grande

Dr Ndi Nyoungui André


68 Algorithmique et structures de données

valeur courante, on conserve la plus grande valeur courante et on lit la valeur suivante. Au
départ la plus grande valeur courante est initialisée au premier nombre lu.

algorithme maxliste ;
var
nombre, max : réel ;
i : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;
max  nombre ;
tantque (i < 100) faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre > max) alors
max  nombre ;
fin ;
écrire('Le plus grand nombre est : ', max) ;
fin.

Exemple 4. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
petit de ces nombres.

algorithme minliste ;
var
nombre, min : réel ;
i : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;
min  nombre ;
tantque (i < 100) faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
fin ;
écrire('Le plus petit nombre est : ', min) ;
fin.

Exemple 5. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
petit et le plus grand de ces nombres.

Dr Ndi Nyoungui André


69 Algorithmique et structures de données

algorithme maxminliste ;
var
nombre, min, max : réel ;
i : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;
min  nombre ;
max  nombre ;
tantque (i < 100) faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
min  nombre ;
si (nombre > max) alors
max  nombre ;
fin ;
écrire('Le plus petit nombre est : ', min) ;
écrire('Le plus grand nombre est : ', max) ;
fin.

Exemple 6. Écrire un algorithme pour lire une suite de 100 nombres et déterminer le plus
petit de ces nombres ainsi que sa position dans la liste. On envisagera le cas de la première
occurrence et le cas de la dernière occurrence.

algorithme posminliste ;
var
nombre, min : réel ;
i, indice : entier ;
début
i  1 ;
écrire('Entrez un nombre : ') ;
lire(nombre) ;
indice  1 ;
min  nombre ;
tantque (i < 100) faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
i  i + 1 ;
si (nombre < min) alors
début
min  nombre ;
indice  i ;
fin ;
fin ;

Dr Ndi Nyoungui André


70 Algorithmique et structures de données

écrire('Le plus petit nombre est : ', min) ;


écrire('Sa position dans la liste est : ', indice) ;
fin.

Exemple 7. Écrire un algorithme pour traiter une liste de 10 étudiants. Pour chaque étudiant
l’algorithme lit le nom de l’étudiant, sa note de contrôle continu et sa note de synthèse dans
une certaine unité de valeur, calcule la moyenne de l’étudiant et affiche le message «… a pour
moyenne :…). La note de contrôle continu compte pour 30% et la note de synthèse pour 70%.

algorithme gestionote ;
var
nom : chaîne ;
note1, note2, moyenne : real ;
n: entier ;
début
n  0 ;
tantque (n < 10) faire
début
écrire('Quel est votre nom ? ') ;
lire(nom) ;
écrire('Votre note de contrôle continu ? ') ;
lire(note1) ;
écrire('Votre note de synthèse ? ') ;
lire(note2) ;
moyenne  0.3*note1 + 0.7*note2 ;
n  n + 1 ;
écrire(nom, 'a pour moyenne : ', moyenne);
fin ;
fin.

Exemple 8. Écrire un algorithme pour calculer les salaires hebdomadaires de n employés


temporaires. Pour chaque employé, le programme demande le nom, le taux horaire et le
nombre d’heures de travail effectuées, puis calcule le salaire de l’employé. Les règles
suivantes sont utilisées pour calculer le salaire d’un employé. Les 40 premières heures sont
considérées comme des heures régulières e payées au taux normal. Les heures effectuées au
delà des 40 premières heures sont considérées comme des heures complémentaires et payées
une fois et demie le taux horaire.

algorithme Paie ;
var
nom : chaîne ;
Rate, HoursWorked : réel;
Salary, Regular, Overtime : réel ;
i, n: entier ;
début
écrire('Nombre employés :') ;
lire(n)
i  0 ;
tantque (i < n) faire

Dr Ndi Nyoungui André


71 Algorithmique et structures de données

début
écrire('Nom employé :') ;
lire(nom) ;
écrire('Taux horaire :') ;
lire(Rate) ;
écrire('Heures effectuées :') ;
lire(HoursWorked) ;
i  i + 1 ;
si (HoursWorked > 40) alors
Overtime  HoursWorked – 40.0
sinon
Overtime  0 ;
Regular  HoursWorked – Overtime ;
Salary  Rate* Regular + 1.5*Rate*Overtime ;
écrire('Nom :', Nom) ;
écrire('Salaire : ', Salary);
fin ;
fin.

Utilisation de boucles pour la lecture des données

Considérons le problème suivant : écrire un algorithme pour calculer la moyenne d’un


ensemble quelconque de nombres. Ce qui différencie ce problème de celui que nous avons
traité précédemment est que nous ne connaissons plus d’avance combien de nombres nous
allons traiter. Dans le problème précédent nous savions que nous devions traiter exactement
10 nombres. Nous étions par conséquent capable de compter les nombres au fur et à mesure
de leur lecture et terminer la boucle dès que le compteur indique que toutes les valeurs
attendues ont déjà été lues. Dans la présente situation où nous ne connaissons pas a priori le
nombre de nombres que nous allons traiter, il se pose la question de savoir comment devra-t-
on procéder pour déterminer que toutes les valeurs ont déjà été lues et qu’il est temps de
terminer la boucle pour passer au calcul de la moyenne ?

Une première approche naïve consiste à compter manuellement les nombres et utiliser le
compte final comme dans le cas précédent. Cette approche trop simpliste est non seulement
encombrante mais inappropriée dans la plupart des cas. Une approche plus élégante pour
résoudre ce problème consiste à utiliser une sentinelle pour indiquer la fin des données. Les
données du problème sont préparées comme d’habitude mais une valeur spéciale, qui ne peut
pas être confondue avec les vraies données est ajoutée à la fin de la liste. Cette valeur, que
l’on appelle la sentinelle est là pour indiquer la fin des données. Par exemple, si on suppose
que les nombres dont on veut calculer la moyenne doivent tous être positifs, alors on peut
utiliser la valeur –1 comme sentinelle.

Les données d’entrée de l’algorithme se présenterons alors de la forme suivante :

valeur valeur …. valeur -1

Dr Ndi Nyoungui André


72 Algorithmique et structures de données

Toutes les valeurs à l’exception de –1 sont des données réelles et doivent être utilisées dans le
calcul de la moyenne. Le –1 est la sentinelle et ne devra pas être utilisé dans le calcul de la
moyenne. Une liste composée uniquement de la valeur –1 sera considérée comme une liste
vide.

Pour donc écrire un algorithme pour calculer la moyenne d’une liste de nombres positifs se
terminant –1, nous allons utiliser la valeur lue pour contrôler la boucle. En d’autres termes,
nous allons continuer à lire et à traiter les valeurs jusqu’à ce l’on lise la valeur de la sentinelle.
L’algorithme de calcul de la moyenne devient alors :

algorithme CalculMoyenne ;
var
nombre, total, moyenne : réel ;
nb : entier ;
début
(*Initialisation de compteur et total*)
nb  0 ;
total  0 ;
écrire('Entrez un nombre (-1 pour terminer) ') ;
lire(nombre) ;
tantque (nombre  -1) faire
début
total  total + nombre ;
nb  nb +1 ;
écrire('Entrez un nombre (-1 pour terminer) ') ;
lire(nombre) ; 
fin ;

(*Calculer et écrire la moyenne}


si (nb > 0) alors
début
moyenne  total/nb ;
écrire('La moyenne est : ', Moyenne) ;
fin
sinon
écrire('La liste est vide. ') ;
fin.

Autres exemples

Exemple 1. Écrire un algorithme pour lire les noms et les années de naissance des enfants
d’une personne et imprimer à chaque fois le message, « … est âgé de … ans ». La répétition
prend fin quand on entre un nom égal à ‘*’.

algorithme agesenfants ;
var
nom : chaîne ;
naissance, age, année : entier ;
début

Dr Ndi Nyoungui André


73 Algorithmique et structures de données

écrire('Quelle année de référence ? ') ;


lire(année) ;
écrire('Entrez un nom (* pour terminer) ') ;
lire(nom) ;
tantque nom  '*' faire
début
écrire('Quelle est son année de naissance ? ') ;
lire(naissance) ;
age  année – naissance ;
écrire(nom, 'est âgé de', age, 'ans') ;
écrire('Entrez le prochain nom (* pour terminer) ') ;
lire(nom) ;
fin ;
fin.

Exemple 2. On peut imaginer que l’utilisateur s’amuse à répéter indéfiniment la boucle de


l’algorithme ci-dessus en refusant de frapper un nom égal à ‘*’. Nous allons donc forcer que
la boucle prenne fin au bout de 10 itérations au maximum. Il faut donc ajouter une deuxième
condition de terminaison contrôlée par un compteur.

algorithme enfants ;
var
nom : chaîne ;
i, naissance, âge, année : entier ;
début
écrire('Quelle année de référence ? ') ;
lire(année) ;
i  0 ;
écrire('Entrez un nom (* pour terminer) ') ;
lire(nom) ;
tantque (nom  '*') et (i < 10) faire
début
écrire('Quelle est son année de naissance ? ') ;
lire(naissance) ;
âge  année – naissance ;
écrire(nom, 'est âgé de', âge, 'ans') ;
écrire('Entrez le prochain nom (* pour terminer) ') ;
lire(nom) ;
i  i + 1 ;
fin ;
fin.

Exercices d’apprentissage

Exercice 3.3
Lesquelles des assertions suivantes s’appliquent à la structure répéter…jusquà  seule ? A la
structure tantque…faire seule ? Aux deux à la fois ?
i. Le corps de la boucle est exécuté au moins une fois.
ii. L’expression logique qui contrôle la boucle est évaluée avant l’entrée de la boucle.

Dr Ndi Nyoungui André


74 Algorithmique et structures de données

iii. Doit utiliser la construction début…fin si plusieurs instructions doivent être répétées.
iv. Le corps de la boucle peut ne pas être exécuté du tout.
v. Utilise une condition de terminaison.
vi. Utilise une condition de répétition.

Exercice 3.4
Écrire un algorithme qui lit le nom et le sexe d’une personne et affiche le message « Bonjour,
Monsieur … » si la personne est de sexe masculin. Si la personne est de sexe féminin, le
programme lui demande si elle est mariée ou non. Si la personne est mariée, le programme
affiche le message « Bonjour, madame … » sinon il affiche le message « Bonjour,
Mademoiselle … ».

Exercice 3.5
Écrire un algorithme qui va répéter le traitement ci-dessus pour un nombre quelconque de
personne. La répétition s’arrête lorsque l’utilisateur entre un nom égal à ‘*’.

Exercice 3.6
Écrire un algorithme qui demande à l’utilisateur de donner son nom, son sexe, son année de
naissance et une année de référence puis affiche le message « Monsieur … votre âge est de …
ans » si la personne est de sexe masculin et le message « Madame … votre âge est de … ans »
si la personne est de sexe féminin.

Exercice 3.7
Écrire un algorithme qui va répéter le traitement ci-dessus pour un nombre quelconque de
personne. La répétition s’arrête lorsque l’utilisateur entre un nom égal ‘*’.

Exercice 3.8
Écrire un algorithme qui va répéter le traitement ci-dessus pour n personnes au plus (n lu). La
répétition s’arrête lorsque l’utilisateur entre un nom égal à ‘*’ ou que le nombre de personnes
traitées devient supérieur à n.

Exercice 3.9
Écrire un algorithme qui lit une suite de nombres positifs se terminant par –1 et détermine le
plus grand de ces nombres.

Exercice 3.10
Écrire un algorithme qui lit une suite de nombres positifs se terminant par –1 et détermine le
plus grand de ces nombres ainsi que sa position dans la liste.

3.3 Les boucles contrôlées par un compteur

Dans certains cas où nous devons utiliser une instruction répéter…jusquà, faire…tantque
ou tantque…faire, le langage algorithmique nous offre une autre possibilité qui simplifie
davantage la construction des boucles. Nous pouvons le faire lorsque nous connaissons
d’avance combien de fois la boucle doit être exécutée. Considérons la boucle tantque…faire
suivante utilisée pour imprimer les 10 premiers entiers positifs :

nombre  1 ;
tantque nombre  10 faire

Dr Ndi Nyoungui André


75 Algorithmique et structures de données

début
écrire(nombre) ;
nombre  nombre + 1 ;
fin ;

La valeur de la variable nombre est utilisée pour contrôler la boucle. D’abord, nombre est
initialisé à sa valeur initiale (ici 1). La valeur courante de nombre est ensuite comparée à sa
valeur finale (ici 10). Si elle est inférieure ou égale à 10 le corps de la boucle est exécuté puis
la valeur de nombre est incrémentée de 1 et le processus, à l’exception de l’initialisation est
répété. Une seule instruction algorithmique permet de combiner certaines de ces étapes ; il
s’agit de l’instruction pour…haut…faire dont la forme générale est :

pour compteur  valeurinitiale haut valeurfinale faire


instruction ;

En utilisant l’instruction pour…haut…faire, nous pouvons réécrire la boucle tantque…faire


ci-dessus de la manière suivante :

pour nombre  1 haut 10 faire


écrire(nombre) ;

Cette boucle pour…haut…faire répète l’exécution de l’instruction écrire(nombre) au fur et à


mesure que la valeur de la variable nombre varie de 1 à 10. La valeur initiale et la valeur
finale peuvent être des expressions. Par exemple, si j est égal à 2 et k égal à 23 alors la boucle

pour nombre  j + 3 haut j * k - 4 faire


écrire(nombre) ;

est équivalente à :

pour nombre  5 haut 42 faire


écrire(nombre) ;

L’instruction pour…haut…faire permet uniquement la répétition d’une seule instruction à la


suite du mot réservé faire. Si nous voulons répéter plusieurs instructions, nous devons utiliser
une instruction composée :

pour compteur  valeurinitiale haut valeurfinale faire


début
instruction1 ;
instruction2 ;
...
instructionn ;
fin ;

L’instruction pour…haut…faire s’exécute de la manière suivante :

1. La variable de contrôle (compteur) est initialisée à sa valeur initiale.

Dr Ndi Nyoungui André


76 Algorithmique et structures de données

2. La valeur courante de la variable de contrôle est comparée à sa valeur finale. Si elle est
inférieure ou égale à la valeur finale, le corps de la boucle est exécuté.
3. La variable de contrôle est automatiquement incrémentée de 1.
4. Les étapes 2 et 3 sont répétées jusqu’à ce que la valeur de la variable de contrôle
devienne supérieure à la valeur finale.

La boucle pour…haut…faire est utile dans les problèmes où on connaît d’avance les valeurs
initiale et finale de la variable de contrôle. Ceci est généralement le cas lorsqu’on travaille
avec les vecteurs, un sujet que nous aborderons ultérieurement.

L’organigramme correspondant de la boucle pour…haut…faire est présenté dans la figure


ci-dessous :

Figure 3.4. Organigramme de la structure pour…haut…faire

i  valeurinitale

faux
i  i +1 i  valeurfinale

vrai

Corps de la boucle

Boîte
Considérons l’exemple additionnel suivant. Il s’agit de calculer et d’imprimer le produit des
termi
dix premiers entiers naturels non nuls. nal

produit  1 ;
pour i  1 haut 10 faire
produit  produit * i ;
écrire(produit) ;

Comparer ce code à celui que l’on obtiendrait en utilisant la boucle répéter … jusquà, faire
… tantque ou tantque … faire.

Considérons maintenant le problème suivant : on Boîte veut imprimer les entiers de 1 à 100 dans
l’ordre inverse. Pour faire cela on peut utiliser la forme
de suivante de la boucle pour :
calcul
pour nombre  100 bas 1 faire
écrire(nombre) ;

La boucle pour…bas…faire permet de spécifier la répétition d’une instruction en faisant


décrémenter la valeur de la variable de contrôle. Dans ce cas la boucle est exécutée si la
valeur initiale est supérieure ou égale à la valeur finale. Nous aurions pu utiliser la boucle

Dr Ndi Nyoungui André


77 Algorithmique et structures de données

pour …bas…faire pour calculer le produit des 10 premiers entiers naturels non nuls comme
illustré dans un exemple précédent :

produit  1 ;
pour i  10 bas 1 faire
produit  produit * i ;
écrire(produit) ;

La forme générale de l’instruction pour…bas…faire est la suivante :

pour compteur  valeurinitiale bas valeurfinale faire


instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, l’instruction pour
prend la forme

pour compteur  valeurinitiale bas valeurfinale faire


début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

L’instruction pour…bas…faire s’exécute de la manière suivante :

1. La variable de contrôle (compteur) est initialisée à sa valeur initiale.


2. La valeur courante de la variable de contrôle est comparée à sa valeur finale. Si la
valeur courante de la variable de contrôle est inférieure ou égale à la valeur finale, le
corps de la boucle est exécuté.
3. La variable de contrôle est automatiquement décrémentée de 1.
4. Les étapes 2 et 3 sont répétées jusqu’à ce que la valeur de la variable de contrôle
devienne inférieure à la valeur finale.

L’organigramme correspondant à la boucle pour…bas…faire est présenté dans la figure 3.5


ci-dessous.

La variable de contrôle, la valeur initiale et la valeur finale doivent être de type entier. Nous
allons assouplir cette condition plus tard, mais le point important est qu’elles ne peuvent pas
être de type réel. La valeur initiale de la variable de la variable de contrôle est affectée une
seule fois. Elle change au fur et à mesure que la boucle est répétée. Cependant, lorsque la
boucle se termine, la variable de contrôle est indéfinie. Ceci signifie que l’on ne peut pas
supposer que la variable de contrôle a une valeur égale à la valeurfinale + 1 (ou égale à la
valeurinitiale – 1). On traitera la variable de contrôle à la fin de la boucle pour comme une
variable indéfinie (non initialisée).
i  valeurinitale
Figure 3.5. Organigramme de la structure pour…bas…faire.

faux
i  i -1 i  valeurfinale
Dr Ndi Nyoungui André
vrai

Corps de la boucle
Boîte
termi
nal
78 Algorithmique et structures de données

Boîte
de
calcul

On peut utiliser l’instruction pour…haut…faire pour la lecture des données à condition que
celles-ci soient correctement arrangées. Pour utiliser la boucle pour…haut…faire, on doit
connaître exactement d’avance combien de valeurs on va lire. Ceci peut être accompli en
comptant d’abord les valeurs à lire et en incluant le compte comme la première valeur à lire.

Comme exemple, considérons le problème qui consiste à calculer la moyenne de n nombres ;


le nombre n constituant la première donnée à lire.

algorithme CalculMoyenne ;
var
nombre, total, moyenne : réel ;
i, n : entier ;
début
total  0 ;
écrire('Quelle est la valeur de n ?') 
lire(n) ;
for i 1 haut n faire
début
écrire('Entrez un nombre : ') ;
lire(nombre) ;
total  total + nombre ;
fin ;
moyenne  total/n ;
écrire('La moyenne est : ', moyenne) ;
fin.

Exercices d’apprentissage

Exercice 3.11
Quelle sera la sortie du segment d’algorithme ci-dessous ?

pour i  1 haut 5 faire


début
pour j  1 haut i faire
écrire('1') ;
écrireln ;
fin ;

Dr Ndi Nyoungui André


79 Algorithmique et structures de données

Exercice 3.12
Quelle sera la sortie du segment d’algorithme ci-dessous ?

pour i  1 haut 6 faire


début
pour j  1 haut 6 faire
si (i = j) ou (i + j = 7) alors
écrire('1')
sinon
écrire('0') ;
écrireln ;
fin ;

Exercice 3.13
Écrire un algorithme qui calcule la somme des carrés des n premiers entiers naturels non nuls.

Exercice 3.14
Écrire un algorithme qui lit un entier positif et calcule le factoriel de ce entier.

Exercice 3.15
Le chercheur médiéval Leonardo (Fibonacci) da Pisa, a proposé la suite infinie dans laquelle
chaque terme est la somme des deux termes précédents. Le n ème nombre de Fibonacci est
défini récursivement de la manière suivante :

1. Si n est égal à 0 ou à 1 alors le nème nombre de Fibonacci est égal à n,


2. Si n est supérieur ou égal à 2 alors le nème nombre de Fibonacci est égal à la somme des
deux nombres de Fibonnaci précédents.

Écrire un algorithme qui calcule et imprime les 100 premiers nombres de Fibonacci.

Exercice 3.16
Écrire un algorithme qui calcule et imprime les rapports entre les 100 premières paires de
nombres de Fibonacci consécutifs.

Exercice 3.17
La série de Maclaurin de la fonction exp(x) est donnée par :


x x2 xn xn
exp( x )  1       
1! 2! n! n  0 n!

Clairement, lorsque n devient grand, la contribution de chaque terme additionnel devient très
petite. Écrire un algorithme qui calcule et imprime les valeurs de exp(x) obtenues en utilisant
un nombre croissant de termes dans la série de Maclaurin de exp(x). L’itération s’arrête dès
que le terme courant devient inférieur à un certain epsilon.

Exercice 3.18
Lorsqu’un capital est déposé dans une banque qui paie les intérêts une fois par mois, et
lorsque le capital initial et les intérêts restent en dépôt, les intérêts sont dits composés. La

Dr Ndi Nyoungui André


80 Algorithmique et structures de données

formule de calcul du capital en dépôt Pn , lorsque un capital P0 est déposé initialement et


lorsque les intérêts sont payés à un taux d’intérêt I pour n mois est :
Pn  P0*(1 I)n

Écrire un algorithme qui calcule et imprime le capital en dépôt Pn lorsque le capital initial
P0 est placé au taux d’intérêt I à la fin de chacun des n premiers mois.

3.4 Autres types de données scalaires

Jusqu’à ce moment nous n’avons considéré que les types de données entier, réel et booléen.
Nous introduisons maintenant les autres types scalaires du langage algorithmique. Les types
que nous allons étudier sont le type caractère, en abrégé char, et les types définis par
l’utilisateur. Ces deux types sont très utiles pour écrire les boucles.

Le type caractère

Même si nous avons utilisé des variables booléennes pour contrôler le cours de l’exécution
d’un algorithme, nous avons jusque là travaillé principalement avec des valeurs numériques.
Nous avons utilisé des chaînes de caractères pour formater les sorties dans les instructions
d’écriture ; cependant, ces chaînes de sont des constantes et nous ne pouvons pas changer
leurs valeurs. Il est important dans certaines situations d’avoir des variables qui prennent des
valeurs caractères. De nombreuses applications nécessitent la manipulations de données
caractères. Par exemple, un algorithme qui imprime les factures des clients doit être capable
de travailler non seulement avec des données numériques impliquant les charges et les
paiements, mais doit aussi traiter des données alphabétiques tels que le nom du client, son
adresse, et ainsi de suite.
Le langage algorithmique offre le type de données standard caractère. Les éléments du
caractère sont les caractères individuels qui sont représentables dans un code donné tel que le
code ASCII. Une constante caractère est une chaîne de caractère entre apostrophes.

Pour déclarer une variable de type caractère, on utilise une instruction de la forme :

var
identificateur : char ;

Pour lire une variable de type caractère, on utilise une instruction de la forme :

lire(lettre)

si lettre est une variable de type caractère. Cette instruction un caractère et affecte sa valeur à
la variable lettre.

Examinons maintenant un algorithme qui lit des caractères. L’algorithme ci-dessous lit une
suite de caractères et compte le nombre de blancs contenus dans la chaîne.

algorithme CompteBlancs ;
const
Blank = ' ' ;
var

Dr Ndi Nyoungui André


81 Algorithmique et structures de données

nbre : entier ;
Ch: char ;
début
nbre  0 ;
lire(Ch) ;
tantque (Ch  '.') faire
début
si (Ch = Blank) alors
nbre  nbre + 1;
lire(Ch) ;
fin;
écrireln('Nombre de blancs :', nbre) ;
fin.

L’algorithme ci-dessous lit une suite caractère se terminant par un point et imprime chaque
mot sur une ligne séparer. On suppose que les mots sont séparés les uns des autres par un seul
caractère blanc.

algorithme Words;
const
Blank = ' ';
var
Ch : char;
début
lire(Ch);
tantque (Ch  '.') faire
début
si (Ch = Blank) alors
écrireln
sinon
écrire(Ch) ;
lire(Ch) ;
fin;
fin.

Exercices de contrôle connaissances

Question 1. Considérons l’algorithme suivant. Quel sera la sortie si l’entrée est :

« Ceci est une question sur les caractères, cependant, elle n’est pas difficile. »

algorithme Words;
var
Ch: char;
début
répéter
lire(Ch);
si (Ch  ',') alors
écrire(Ch)

Dr Ndi Nyoungui André


82 Algorithmique et structures de données

sinon
écrireln;
jusquà (Ch = '.');
fin.

Question 2. Que fait l’algorithme suivant ?

algorithme Words;
const
Blank = ' ';
var
Ch: char;
début
lire(Ch);
tantque (Ch  Blank) faire
début
tantque (Ch  Blank) faire
début
écrire(Ch) ;
lire(Ch) ;
fin ;
lire(Ch) ;
fin;
fin.

Les types de données définis par l’utilisateur

Les types de données que nous étudiés jusque là (entier, réel, caractère, booléen) sont tous des
types standards. Le langage algorithmique donne aussi aux programmeurs la possibilité de
définir leurs propres types de données. Ceci est souvent utile pour simplifier les structures de
contrôle et donc améliorer la lisibilité des algorithmes. Un programmeur peut définir un
nouveau type scalaire de deux manières :

1. En énumérant toutes les valeurs qu’une variable de ce type peut prendre, ou


2. En restreignant l’intervalle des valeurs d’un type existant.

 Les types énumérés

Supposons que l’on ait besoin d’une boucle pour calculer le revenu hebdomadaire d’un
employé connaissant son revenu journalier pour les jours allant de lundi à vendredi. Une
solution peut prendre la forme suivante :

total  0 ;
pour day 1 haut 5 faire
début
lire(dailypay) ;
total  total + dailypay ;
fin;

Dr Ndi Nyoungui André


83 Algorithmique et structures de données

On suppose bien entendu que des déclarations appropriées ont été effectuées auparavant. Plus
spécifiquement, on suppose que la variable day a été déclarée de la manière suivante :

var
day : entier ;

Une meilleure approche consisterait à déclarer la variable day de la manière suivante :

var
day : (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi) ;

Ce que cette déclaration fait est d’énumérer toutes les valeurs possibles que la variable day
peut prendre. On peut maintenant réécrire la boucle comme suit :

total  0 ;
pour day  lundi haut vendredi faire
début
lire(dailypay) ;
total  total + dailypay ;
fin;

Le sens de la boucle est clairement plus apparent dans cette deuxième expression. La variable
day dans cette deuxième illustration est dite être de type défini par l’utilisateur. On aurait pu
rendre ce type plus explicite en utilisant une définition de type pour avoir un type avec son
propre identificateur :

type
tweekday = (dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi) ;

var
Day : tweekday ;

La définition de type ci-dessus rend disponible un identificateur pour le nouveau type scalaire
tweekday. On peut ensuite utiliser ce nouveau type pour déclarer des variables de ce type
comme nous l’avons fait avec la variable Day.

La forme générale d’une définition de type scalaire énuméré est :

type
identificateur = (identificateur1, identificateur2, …, identificateurn) ;

On se rappellera que lorsqu’on utilise une définition de type, l’identificateur que l’on choisit
pour le type n’est pas un nom de variable et que l’on ne peut par conséquent pas lui affecter
des valeurs. Dans l’exemple ci-dessus, le rôle joué par l’identificateur WeekDay est
exactement le même que celui joué par les identificateurs prédéfinis entier, réel, caractère ou
booléen. En effet, le type booléen est un type énuméré prédéfini. Sa définition est :

type
booléen = (faux, vrai) ;

Dr Ndi Nyoungui André


84 Algorithmique et structures de données

Nous n’avons bien entendu pas besoin de spécifier cette définition dans nos algorithmes, car
elle est directement offerte par le langage algorithmique.

De même que l’identificateur de type n’est pas une variable, il en est de même des
identificateurs qui représentent les différentes valeurs possibles de ce type. Nous ne pouvons
pas affecter de valeur à lundi ou à mardi, de même que nous ne pouvons pas affecter de valeur
à vrai ou à faux. La définition d’un type scalaire énuméré introduit non seulement un nouvel
identificateur de type mais fait aussi la chose suivante :

1. Enumère les identificateurs dénotant les constantes du nouveau du type ;


2. Définit l’ordre de ces constantes.

L’ordre dans lequel les valeurs sont listées dans la définition de type établit l’ordre pour ce
type. Dans l’exemple ci-dessus, dimanche a été listé le premier. Ceci rend dimanche inférieur
à toutes les autres valeurs de la liste. De même, lundi est inférieur à toutes les autres valeurs
qui le suivent. A cause de cet ordre, tout identificateur qui est utilisé pour spécifier une valeur
dans un type énuméré ne peut apparaître que dans une seule définition de type à la fois. Les
deux définitions de type suivantes, prises ensemble, sont invalides :

type
tcouleur = (rouge, bleu, jaune) ;
tnuance = (rose, gris, jaune, rouge);

Si les deux définitions sont admises, on ne pourra pas décider si jaune < rouge ou rouge <
jaune.

Les types de données scalaires énumérés sont à usage interne uniquement. On peut affecter et
tester de telles valeurs dans un algorithme, mais on ne peut pas les lire ou écrire en utilisant
une instruction de lecture ou une instruction d’écriture. Il existe cependant des astuces que
nous pouvons utiliser pour lire ou écrire indirectement de telles valeurs. Supposons par
exemple que la variable sexe soit déclarée de la manière suivante :

var
sexe : (Féminin, Masculin) ;

L’instruction ci-dessous peut être utilisée pour affecter une valeur à la variable sexe.

lire(Ch) ;
si Ch = 'M' alors
sexe  Masculin
sinon
si Ch = 'F' alors
sexe  Féminin ;

De même, l’instruction ci-dessous peut être utilisée pour écrire la valeur de la variable sexe

si sexe = Masculin alors


écrire('Masculin')

Dr Ndi Nyoungui André


85 Algorithmique et structures de données

sinon
écrire('Féminin') ;

 Les types intervalles

Il arrive souvent qu’une valeur soit destinée à recevoir des valeurs d’un certain type dans
un intervalle spécifique uniquement. Par exemple, on peut avoir la déclaration suivante :

var
année : entier ;

Cependant dans l’algorithme, les valeurs de la variable année seront toujours comprises entre
1900 et 2002. La déclaration

var
année : 1900..2020 ;

réalise cette restriction. Par ailleurs, le système génère un code pour vérifier qu’aucune valeur
en dehors de cet intervalle n’est affectée à année lorsque le programme s’exécute. Si une telle
affectation est tentée, elle résulte en une erreur d’exécution et le programme s’arrête. Ce genre
de vérification peut être utile lorsque le programme est entrain d’être exécuté.

Tout comme dans le cas des types scalaires énumérés, on peut définir explicitement un type
intervalle en utilisant une instruction de définition de type. La forme générale d’une définition
de type intervalle est :

type
identificateur = valeurinf .. valeursup ;

Des exemples de définitions de type intervalle sont les suivants :

type
tettre = 'A'..'Z' ;
tpériode = 1900..2220 ;
tworkday = lundi..vendredi ;

La dernière définition de type est uniquement valide si le type énuméré WeekDay, comme
nous l’avons vu précédemment, a été défini avant la définition de tworkday. Les types
intervalles peuvent être définis pour les entiers, les caractères et les types énumérés. Les types
intervalles ne sont pas autorisés pour les réels.

Fonctions ayant des paramètres scalaires

Le langage algorithmique a des fonctions prédéfinies qui sont utiles quand on travaille avec
des types scalaires. Ces fonctions sont les suivantes :

Tableau 3.1. Quelques fonctions prédéfinies sur les types ordinaux

Nom Description Type argument Type résultat

Dr Ndi Nyoungui André


86 Algorithmique et structures de données

succ(x) Délivre le successeur de x Un type ordinal Idem


pred(x) Délivre le prédécesseur de x Un type ordinal Idem
ord(x) Délivre la valeur ordinale de x Un type ordinal Entier
chr(x) Délivre le caractère associé à x Entier Caractère

Les fonctions succ et pred sont utilisées avec n’importe lequel type scalaire, à l’exception du
type réel, pour obtenir le successeur ou le prédécesseur d’une valeur donnée. Quand elles sont
utilisées avec les entiers, les fonctions pred et succ peuvent être employées pour incrémenter
ou décrémenter les compteurs.
Le prédécesseur de la première valeur et le successeur de la dernière valeur dans un type
énuméré ne sont pas définis. L’ordre des valeurs dans un type énuméré est défini par l’ordre
dans lequel les constantes de ce type sont énumérées.
Chaque valeur dans un type énuméré est associée à un nombre qui indique sa position dans la
liste. La première valeur est associée à la position 0, la deuxième à la position 1, et ainsi de
suite. Les nombres qui indiquent la position des constantes dans la liste sont appelés les
nombres ordinaux des constantes correspondantes. La fonction prédéfinie ord retourne le
nombre ordinal de son argument.

3.5 La programmation structurée

Nous avons maintenant étudié toutes les structures de contrôle du langage algorithmique. Il y
a trois structures de contrôle de base au moyen desquelles on peut contrôler l’exécution des
instructions dans un algorithme. Ces structures sont : la séquence, la sélection et la répétition.
Ce sont les blocs de construction de base de tous les algorithmes.

Dans une structure séquentielle, les instructions sont écrites les unes à la suite des autres.
Dans le langage algorithmique le prototype d’une structure séquentielle est une instruction
composée. Une telle structure est délimitée par les mots réservés début et fin. De cette façon,
une structure séquentielle peut s’écrire de la manière suivante :

début
instruction1 ;
instruction2 ;
...
instruction2 ;
fin ;

La sélection peut être implémentée de trois façons différentes : la structure si…alors…sinon,


la structure si…alors et la structure de sélection multiple. La structure de contrôle si…
alors…sinon est illustrée par l’organigramme ci-dessous.

Figure 3.6 Organigramme de la structure si…alors…sinon

vrai faux
Condition

instruction 1 instruction 2

Dr Ndi Nyoungui André


87 Algorithmique et structures de données

Elle s’exprime de la manière suivante

si (condition) alors
instruction1
sinon
instruction2 ;

où instruction1 et instruction2 sont des instructions simples ou composées. Dans ce dernier


cas, elle prend la forme suivante :

si (condition) alors
début
instruction1 ;
instruction ;

instructionn ;
fin
sinon
début
instruction1 ;
instruction2 ;

instructionm ;
fin ;

La structure de contrôle si … alors est illustrée par l’organigramme ci-dessous :

Figure 3.7. Organigramme de la structure si…alors

vrai
expression-logique

faux
instruction

Elle s’exprime de la manière suivante

si (condition) alors

Dr Ndi Nyoungui André


88 Algorithmique et structures de données

instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, elle prend la
forme suivante :

si (condition) alors
début
instruction ;
instruction2 ;

instructionn ;
fin ;

Enfin, l’instruction de sélection multiple est illustrée par l’organigramme de la figure 3.9 ci-
dessous.

Figure 3.8. Organigramme de la structure de sélection multiple

expression

instruction 1 instruction 2 … instruction n

Elle s’exprime de la manière suivante :

case (expression) de
liste-valeur1 : instrcution1 ;
liste-valeur2 : instruction2 ;
...
liste-valeurn : instruction n ;
fin ;

ou encore

case (expression) de
liste-valeur1 : instrcution1 ;
liste-valeur2 : instruction2 ;

liste-valeurn : instruction n ;
sinon

Dr Ndi Nyoungui André


89 Algorithmique et structures de données

autre-instruction 
fin ;

Les structures de répétition offrent au programmeur un mécanisme permettant de spécifier la


répétition d’un groupe d’instructions. Comme nous l’avons vu précédemment, le langage
algorithmique offre cinq instructions permettant d’écrire les boucles :

 l’instruction répéter … jusquà,


 l’instruction faire…tantque,
 l’instruction tantque…faire,
 l’instruction pour…haut…faire,
 l’instruction pour…bas…faire.

L’organigramme correspondant à l’instruction répéter…jusquà est présenté ci-dessous.

Figure 3.9. Organigramme de la structure répéter…jusquà

instruction

faux vrai
Expression
logique

Elle s’exprime de la manière suivante :

répéter
instruction
jusquà (condition) ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, elle prend la
forme suivante :

répéter
instruction1 ;
instruction2 ;

instructionn ;
jusquà (condition) ;

La boucle faire…tantque est illustrée par l’organigramme ci-dessous.

Figure 3.10. Organigramme de la structure faire…tantque


instruction

Dr Ndi Nyoungui André


vrai faux
expression logique
90 Algorithmique et structures de données

Elle s’exprime de la manière suivante :

faire
instruction
tantque (expression-logique) ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, elle prend la
forme suivante :

faire
instruction1 ;
instruction2 ;

instruction ;
tantque (expression-logique) ;

La boucle tantque…faire est illustrée dans le diagramme ci-dessous.

Figure 3.12. Organigramme de la structure tantque…faire

faux
expression logique

vrai

instruction

Elle s’exprime de la manière suivante :

tantque (expression-logique) faire


instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, elle prend la
forme suivante :

Dr Ndi Nyoungui André


91 Algorithmique et structures de données

tantque (expression-logique) faire


début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Enfin, l’instruction pour…haut…faire est illustrée par l’organigramme de la figure 3.12 ci-
dessous :

Figure 3.12. Organigramme de la structure pour…haut…faire

i  valeurinitiale

faux
ii+1 i  valeurfinale

vrai

instruction

Elle s’exprime de la manière suivante :

pour i  valeurinitiale haut valeurfinale faire


instruction ;

où instruction est une instruction simple ou composée. Dans ce dernier cas, elle prend la
forme suivante :

pour i  valeurinitiale haut valeurfinale faire


début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Enfin, pour illustrer les différentes structures de contrôle que nous venons de présenter,
considérons l’algorithme ci-dessous. Il s’agit d’un algorithme qui calcule et imprime l’âge
moyen des femmes ainsi que l’âge moyen des hommes présents dans une liste quelconque de
personnes.

Dr Ndi Nyoungui André


92 Algorithmique et structures de données

algorithme AgeMoyen ;
var
sexe : char ;
age, rotalfem, totalhom, nbfem, nbhom : entier ;
moyfem, moyhom : réel ;
début
totalfem  0 ;
nbfem  0 ;
totalhom  0 ;
nbhom  0 ;
écrire('Quel est votre sexe (M ou F)?') ;
lire(sexe) ;
tantque sexe  '*' faire
début
écrire('Quel est votre âge ?') ;
lire(age) ;
si sexe = 'F' alors
début
totalfem  totalfem + age ;
nbfem  nbfem + 1 ;
fin
sinon
début
totalhom  totalhom + age ;
nbhom  nbhom + 1 ;
fin ;
écrire('Quel est votre sexe (M ou F)?') ;
lire(sexe) ;
fin ;
si nbfem = 0 alors
écrire('Aucune femme dans la liste') ;
sinon
début
moyfem  totalfem/nbfem ;
écrire('Âge moyen des femmes : ', moyfem) ;
fin ;
si nbhom = 0 alors
écrire('Aucun homme dans la liste') ;
sinon
début
moyhom  totalhom/nbhom ;
écrire('Âge moyen des hommes : ', moyhom) ;
fin ;
fin.

3.6 Études de cas

Transaction sur un compte bancaire

Dr Ndi Nyoungui André


93 Algorithmique et structures de données

Le programme de transaction va lire le solde courant, le montant du chèque et la montant de


chaque versement. Il va imprimer, sous une forme tabulaire, le montant de chaque transaction
et le solde après que la transaction est appliqué au solde.

Calcul de la racine carrée d’un nombre

Examinons maintenant une application utilisant une boucle qui commence par une valeur
singulière et répète un ensemble d’opérations jusqu’à ce que la solution désirée soit obtenue.
Nous allons examiner un problème mathématique classique : le calcul de la racine carrée d’un
nombre. Il y a plusieurs algorithmes pour calculer les racines carrées. Nous allons utiliser
celui que l’on attribue à Isaac Newton. La méthode de Newton utilise l’approche suivante : si
r est une approximation de la racine carrée d’un nombre x, alors (x + x/r)/2 est une meilleure
approximation de la racine carrée de x. On commence donc par une approximation arbitraire,
par exemple 1, et on répète le calcul de l’approximation jusqu’à ce que la différence entre le
carré de l’approximation et le nombre devienne inférieure à un epsilon donné. Écrire un
algorithme qui lit un nombre et calcule sa racine carrée.

Dr Ndi Nyoungui André


94 Algorithmique et structures de données

CHAPITRE 4

PROCEDURES ET FONCTIONS

4.1 Introduction

Dans les algorithmes que nous avons écrits jusque là, nous avons utilisé des procédures et des
fonctions qui sont directement offertes par le langage algorithmique. On les appelle ainsi des
fonctions ou procédures prédéfinies. Ces fonctions ou procédures implémentent la plupart des
opérations usuelles que nous utilisons très souvent dans les algorithmes.

Des exemples de fonctions prédéfinies du langage algorithmique sont :

Tableau 4.1. Quelques exemples de fonctions prédéfinies

Nom Description
abs(x) Calcule la valeur absolue de x
arctan(x) Calcule arc tangente de x
cos(x) Calcule le cosinus de x
cosh(x) Calcule le cosinus hyperbolique de x
exp(x) Calcule l’exponentiel de x
ln(x) Calcule le logarithme naturel de x
log(x) Calcule le logarithme décimal de x
pred(x) Calcule le prédécesseur de x dans une énumération
round(x) Calcule la partie entière de x
sin(x) Calcule le sinus de x
sinh(x) Calcule le sinus hyperbolique de x
Sqr(x) Calcule le carré de x
sqrt(x) Calcule la racine carrée de x
succ(x) Calcule le successeur de x dans une énumération
tan(x) Calcule la tangente de x

Des exemples de procédures préfinies du langage algorithmique sont :

Tableau 4.2. Quelques exemples de procédures prédéfinies

Nom Description
écrire Imprimer des données à l’écran
écrireln Imprimer des données à l’écran
libérer Libérer la mémoire occupée par une variable dynamique
Lire Lire des données au clavier
lireln Lire des données au clavier
nouveau Initialiser une variable dynamique
réécrire Ouvrir un fichier en écriture
relire Ouvrir un fichier en lecture

Dr Ndi Nyoungui André


95 Algorithmique et structures de données

Cependant, le langage algorithmique offre aux programmeurs la possibilité de définir leurs


propres fonctions ou procédures. Ces fonctions/procédures contribuent à étendre les capacités
du langage du langage algorithmique et sont en général conçues pour effectuer une tâche bien
précise. Parce que ces fonctions/procédures sont définies par le programmeur, on les appelle
habituellement les fonctions/procédures définies par l’utilisateur.

4.2 Les fonctions définies par l’utilisateur

Les fonctions prédéfinies du langage algorithmique que nous avons étudiées dans les
chapitres précédents, effectuent des opérations qui ne sont pas facilement réalisables avec les
opérateurs usuels. En outre, les programmeurs peuvent définir leurs propres fonctions pour
étendre les capacités du langage algorithmique. Comme exemple, supposons que nous avons
besoin d’une fonction pour calculer le factoriel d’un entier positif. Nous pouvons créer cette
fonction en utilisant la définition de fonction suivante :

fonction factoriel(n : entier) : entier ;


var
i, produit : entier ;
début
produit  1 ;
pour i  1 haut n faire
produit  produit*i ;
factoriel  produit ;
fin ;

Une définition de fonction a la même structure générale qu’un algorithme, à l’exception du


fait qu’elle commence par une en-tête de fonction au lieu d’une en-tête d’algorithme. Une en-
tête de fonction commence par le mot réservé fonction. Il est suivi par un identificateur qui
spécifie le nom de la fonction. Dans notre exemple, le nom de la fonction est factoriel. C’est
par ce nom que la fonction sera appelée ou invoquée dans le reste de l’algorithme. La partie
de l’en-tête comprise entre parenthèses est la liste des paramètres formels de la fonction. Le
terme paramètre formel désigne une variable qui est utilisée par la fonction. Lorsque la
fonction est invoquée, le paramètre formel reçoit la valeur du nombre dont on veut calculer le
factoriel. La fonction effectue ses calculs en utilisant cette valeur comme donnée d’entrée, et
retourne ensuite le résultat au programme appelant. La liste de paramètres (n : entier) identifie
le paramètre formel n et spécifie son type. Le type de la valeur que la fonction retourne est
spécifié à la suite de la liste des paramètres. Dans notre exemple, la fonction factoriel retourne
un nombre entier.

En général, la liste des paramètres formels peut contenir un nombre quelconque de paramètres
de types différents. Le corps de la fonction, délimité par les mots réservés début et fin, décrit
les actions et/ou les traitements qui doivent être effectuées sur les données d’entrée lorsque la
fonction est exécutée. Pour communiquer le résultat des calculs à l’algorithme appelant, ce
résultat doit être affecté au nom de la fonction. Dans notre exemple, le corps de la fonction
contient une boucle qui calcule le factoriel de n et une instruction qui affecte cette valeur au
nom de la fonction. Cette valeur peut ensuite être communiquée à la portion de l’algorithme
qui a appelé la fonction.

Dr Ndi Nyoungui André


96 Algorithmique et structures de données

Voyons maintenant comment une fonction définie par l’utilisateur fonctionne. L’exemple ci-
dessous illustre la définition et l’utilisation d’une fonction dans un algorithme.

algorithme AppelFonction ;
variable
nombre, n : entier ;
(*déclaration de la fonction*)
fonction factoriel(x : entier) : entier ;
var
fact : entier ;
début
fact  1 ;
pour i  1 haut x faire
fact  fact*i ;
factoriel  fact ;
fin ;
(*algorithme principal*)
début
n  0 ;
tantque n  10 faire
début
nombre  factoriel(n) ;
écrire(n, ' ! = ', nombre) ;
n  n + 1 ;
fin ;
fin.

On remarquera que les fonctions sont définies après la déclaration des variables. On notera
aussi que le mot réservé fin qui termine la définition d’une fonction est suivi par un point
virgule et non un point. Seul l’algorithme principal se termine par un point. La forme générale
d’une définition de fonction est la suivante :

fonction nomfonction (liste des paramètres formels) : type-résultat ;


Section déclaration des variables locales
début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Type-résultat est le type de la valeur que la fonction délivre (entier, réel, caractère, booléen,
chaîne, pointeur ou un type énuméré défini par l’utilisateur). On notera que le corps de la
fonction doit contenir au moins une instruction qui affecte une valeur au nom de la fonction.
Cette valeur est retournée comme le résultat de la fonction. La section de déclaration des
variables locales de la fonction est utilisée pour déclarer les variables utilisées uniquement par
la fonction et nulle part ailleurs dans l’algorithme.

Les paramètres d’une fonction

Dr Ndi Nyoungui André


97 Algorithmique et structures de données

Le but d’une définition de fonction est de décrire les calculs qui doivent être effectués sur les
données de la liste des paramètres formels. Il est important de souligner qu’aucun traitement
n’est effectué à la suite de la définition d’une fonction. L’exécution effective des instructions
du corps de la fonction intervient uniquement lorsque la fonction est invoquée dans un
algorithme. Dans l’exemple ci-dessus, l’instruction

nombre  factoriel(n) ;

invoque la fonction factoriel. Cette instruction provoque l’exécution de la fonction factoriel.


Au début de l’exécution, la valeur de n affectée au paramètre formel x. A la fin de l’exécution
de la fonction, la valeur du factoriel de n est affectée à la variable nombre. L’expression
factoriel(n) est appelée un désignateur de fonction et la variable n est appelée le paramètre
effectif ou argument de la fonction. Pour invoquer une fonction, on utilise le désignateur de
la fonction dans une expression.

Lorsque l’exécution d’un algorithme rencontre un désignateur de fonction (1) la valeur du


paramètre effectif est affectée au paramètre formel, (2) les instructions de la fonction sont
exécutées, et (3) le résultat de la fonction est retourné comme la valeur de la fonction. Cette
valeur peut ensuite être utilisée dans l’algorithme.

Nous avons vu qu’une fonction définie par l’utilisateur peut avoir plusieurs paramètres
formels. Considérons la fonction ci-dessous qui calcule le plus grand entre deux nombres.

fonction plusgrand2(a, b : réel) : réel ;


début
si a > b alors
plusgrand2  a
sinon
plusgrand2  b ;
fin ;

Le désignateur de fonction

plusgrand(7, 15)

invoque la fonction plusgrand avec 7 et 15 comme paramètres effectifs. Le nombre de


paramètres effectifs dans un désignateur de fonction doit être égal au nombre de paramètres
formels dans la définition de la fonction. La correspondance entre les paramètres effectifs et
les paramètres formels est déterminée par l’ordre des paramètres dans chaque liste. Ceci
signifie que le premier paramètre effectif correspondra toujours au premier paramètre formel,
le deuxième paramètre effectif au deuxième paramètre formel, et ainsi de suite. Ainsi, lorsque
le désignateur de fonction

plusgrand(7, 15)

est évalué, la constante 7 est affectée à a et la constante 15 est affectée à b. Le paramètre


effectif dans un désignateur de fonction peut être n’importe quelle expression dont le type est

Dr Ndi Nyoungui André


98 Algorithmique et structures de données

le même que celui du paramètre formel correspondant. Par exemple, les expressions suivantes
sont toutes des invocations valides de la fonction plusgrand :

Désignateur de fonction Valeur


plusgrand(4, 5) 5
plusgrand(4, 3 – 2) 4
plusgrand(3 * 2, 75 div 4) 18
plusgrand(12, sqr(4)) 16
plusgrand(2, plusgrand(3, 1)) 3

Dans les deux derniers exemples, un des paramètres effectifs est lui-même un désignateur de
fonction. Lorsqu’une expression contient des appels imbriqués à des fonctions, l’appel le plus
interne est exécuté le premier et le résultat de cet appel est utilisé comme paramètre effectif
dans l’exécution de l’appel supérieur.
La liste des paramètres formels d’une définition fonction peut contenir paramètres scalaires
aussi que des paramètres structurés, tels que des vecteurs, des articles ou des listes chaînées.

Autres exemples de fonctions

Exemple 1. Écrire une fonction qui prend en entrée trois nombres réels et retourne le plus
grand de ces nombres.

fonction max3(a, b, c : réel) : réel ;


début
si (a  b) et (a  c) alors
max3  a
sinon
si (b  a) et (b  c) alors
max3  b
sinon
max3  c ;
fin ;

Exemple 2. Écrire une fonction qui prend en entrée quatre nombres réels et retourne le plus
grand de ces nombres.

Première solution

Cette solution utilise une instruction si…alors…sinon imbriquée pour calculer le maximum
des quatre nombres.

fonction max4(a, b, c, d : réel) : réel ;


début
si a > b alors
si a > c alors
si a > d alors
max4  a
sinon
max4  d

Dr Ndi Nyoungui André


99 Algorithmique et structures de données

sinon
si c > d alors
max4  c
sinon
max4  d
sinon
si b > c alors
si b > d alors
max4  b
sinon
max4  d
sinon
si c > d alors
max4  c
sinon
max4  d ;
fin ;

Deuxième version

On peut éviter d’utiliser une imbrication très profonde des instructions si…alors … sinon en
utilisant une succession d‘instruction si…alors. L’algorithme devient alors.

fonction max4(a, b, c, d : réel) : réel ;


variable
max : réel ;
début
max  a ;
si b > max alors
max  b ;
si c > max alors
max  c ;
si d > max alors
max  d ;
max4  max ;
fin ;

Troisième version

On peut éviter d’utiliser une imbrication très profonde des instructions si…alors … sinon en
utilisant les conditions composées. L’algorithme devient alors.

fonction max4(a, b, c, d : réel) : réel ;


début
si (a  b) et (a  c) et (a  d) alors
max4  a
sinon
si (b  a) et (b  c) et (b  d) alors

Dr Ndi Nyoungui André


100 Algorithmique et structures de données

max4  b
sinon
si (c  a) et (c  b) et (c  d) alors
max4  c
sinon
max4  d ;
fin ;

Exercices d’apprentissage

Exercice 4.1
Écrire une fonction qui prend en entrée deux entiers positifs n et p et retourne Anp , le nombre
d’arrangements p à p de n éléments.

Exercice 4.2
Une année est bissextile si son millésime est un multiple de 4, sauf les années de début de
siècle qui sont bissextiles si leur millésime est divisible par 400.
Écrire une fonction booléenne qui reçoit en entrée un entier naturel et détermine si cet entier
représente une année bissextile.

Exercice 4.3
Une boutique est ouverte de 7 heures à 13 heures et de 16 heures à 22 heures, sauf le
dimanche après-midi et le lundi toute la journée. Écrire un fonction booléenne qui reçoit en
entrée une heure (un entier naturel) et un jour (une chaîne de caractère) et détermine si la
boutique est ouverte le jour et à l’heure indiqués..

Exercice 4.4
Les tarifs d’affranchissement d’une lettre sont les suivants :
en-dessous de 20g : 280 FCFA,
à partir de 20g, mais en-dessous de 50g : 440 FCFA,
à partir de 50g : 670 FCFA.
Écrire une fonction qui prend en entrée le poids d’une lettre et retourne le montant de
l’affranchissement de la lettre.

Exercice 4.5
Un entier positif est premier s’il est supérieur à deux et s’il n’est divisible que par un et par
lui-même. Écrire une fonction qui prend en entrée un entier positif et détermine si cet entier
est un nombre premier.

Exercice 4.6
On souhaite calculer le montant des impôts dus par un contribuable en fonction son revenu
imposable et de son nombre de parts fiscales. Les règles de calcul sont les suivantes :
- le revenu par part fiscale est égale au quotient du revenu imposable par le nombre de parts
fiscales
- l’impôt par part fiscale est calculé selon le barème suivant :
 0 si le revenu par part fiscale est inférieur à 50 000 F ;
 10% sur la tranche du revenu par part comprise entre 50 000 F et 100 000 F ;
 25% sur la tranche du revenu par part comprise entre 100 000 F et 200 000 F 
 50% sur le revenu par part fiscale est qui dépasse 200 000 F 

Dr Ndi Nyoungui André


101 Algorithmique et structures de données

- l’impôt total est égal au nombre de parts fiscales multiplié par l’impôt par part fiscale

Écrire une fonction qui prend en entrée le revenu imposable et le nombre de parts fiscales
d’un contribuable et retourne le montant des impôts dus par ce contribuable.

Exercice 4.7
Lorsqu’un capital est déposé dans une banque qui paie les intérêts une fois par mois et que le
capital initial et les intérêts restent en dépôt, on dit que les intérêts sont composés. La formule
qui donne le capital courant P lorsque le capital initial est A et que les intérêts sont payés au
taux annuel I au bout de N mois est :
P  A1 I  N

Écrire une fonction qui prend en entrée le capital initial A, le nombre d’années N et calcule le
capital courant P au bout des N mois.

4.3 Les procédures

La forme générale d’une définition de procédure est :

procédure nomprocédure (liste des paramètres formels) ;


Section des déclarations locales
début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

La première ligne d’une définition de procédure est l’en-tête de la procédure. L’identificateur


qui suit le mot réservé procédure spécifie le nom de la procédure. La liste des paramètres
formels spécifie les paramètres formels et leur type. Toutes les variables déclarées dans la
section de déclaration des variables locales de la procédure sont locales à la procédure. Cela
signifie que ces variables ne sont définies que lorsque la procédure est exécutée. Enfin le
corps de la procédure décrit les opérations à effectuer par la procédure. Si la procédure n’a
aucun paramètre, alors la liste des paramètres formels et ses parenthèses peuvent être omises.

Les principales différences entre une définition de procédure et une définition de fonction sont
les suivantes :

 On ne spécifie pas dans l’en-tête de la procédure un type pour la procédure elle même.
Ceci n’est pas nécessaire car une procédure peut ne pas retourner de résultat du tout,
ou elle peut retourner plusieurs valeurs de différents types.
 Il n’est pas permis d’affecter une valeur au nom de la procédure dans le corps de la
procédure. Le nom de la procédure est utilisé uniquement pour identifier la procédure.
 Enfin, le mot réservé procédure, au lieu de fonction, est utilisé dans l’en-tête de la
procédure.

Comme exemple, considérons la définition de procédure ci-dessous, qui est équivalente à la


fonction factoriel que nous avons définie précédemment.

Dr Ndi Nyoungui André


102 Algorithmique et structures de données

procédure factoriel(x : entier ; var y : entier) ;


variable
i, produit : entier ;
début
produit  1 ;
pour i  1 haut x faire
produit  i*produit ;

y  produit ;
fin ;

La procédure factoriel a deux paramètres. Le paramètre x représente le nombre entier dont on


veut calculer le factoriel. Le paramètre y représente le résultat de x! et sa valeur est retournée
par la procédure. Le mot réservé var qui précède y indique que y est un paramètre variable
ou paramètre transmis par adresse. Les paramètres transmis par adresse sont utilisés par la
procédure pour retourner des valeurs à l’algorithme qui l’invoque. Les paramètres transmis
par adresse peuvent aussi être utilisés pour transmettre des valeurs à la procédure. Le
paramètre x qui n’est pas précédé par le mot var est appelé un paramètre transmis par
valeur, ou plus simplement un paramètre valeur. Les paramètres transmis par valeur sont
utilisés uniquement pour transmettre des valeurs à la procédure.

L’algorithme ci-dessous montre comment la procédure factoriel est déclarée et invoquée dans
un algorithme.

algorithme AppelProcédure ;
var
n, p, nombre : entier ;

(*déclaration de la procédure*)
procédure factoriel(x : entier ; var y : entier) ;
variable
i, produit : entier ;
début
produit  1 ;
pour i  1 haut x faire
produit  produit*i ;
y  produit ;
fin ;
(*algorithme principal*)
début
factoriel(10, b) ;
écrire('10! = ', b) ;
n  0 ;
tantque n  10 faire
début
factoriel(n, nombre) ;
écrire(n, ' ! = ', nombre) ;
n  n + 1 ;

Dr Ndi Nyoungui André


103 Algorithmique et structures de données

fin ;
fin.

L’algorithme principal contient deux instructions d’appel de procédure qui invoquent la


procédure factoriel. L’instruction d’appel de procédure,

factoriel(10, b)

invoque le procédure factoriel et passe la valeur 10 à la procédure en l’affectant au paramètre


formel x. La procédure calcule le factoriel de n et affecte le résultat au paramètre effectif b. La
procédure est encore invoquée par l’instruction d’appel de procédure :

factoriel(n, nombre)

Ici, la valeur de n est passée à la procédure, qui calcule le factoriel de cette valeur et retourne
le résultat comme la valeur de la variable nombre. On notera que les appels de procédure sont
écrits comme des instructions indépendantes alors que les désignateurs de fonction sont
toujours utilisés comme des composantes d’une expression.

Pour comprendre comment les valeurs sont transmises à une procédure et retournées par la
procédure, examinons maintenant en détail le mécanisme de transmission des paramètres.

4.4 La transmission des paramètres

Comparons l’instruction d’appel de procédure

factoriel(10, b)

et l’en-tête de procédure

procédure factoriel(x : entier ; var y : entier) ;

La correspondance entre les paramètres effectifs de l’appel de procédure et les paramètres


formels de la définition de la procédure est :

factoriel(10, b)

procédure factoriel(x : entier ; var y : entier) ;

Le paramètre effectif 10 correspond au paramètre formel x, qui est un paramètre transmis


par valeur. Le paramètre effectif b correspond au paramètre formel y, qui est un paramètre
transmis par adresse. Les paramètres transmis par valeur sont utilisés pour passer des
données à la procédure. Ils ne peuvent pas être utilisés pour passer des résultats en retour.
C’est pourquoi nous avons dessiné une flèche unidirectionnelle pour établir la correspondance
entre les paramètres formel et effectif.

Dr Ndi Nyoungui André


104 Algorithmique et structures de données

Lorsque les données sont communiquées du programme appelant à une procédure en utilisant
des paramètres transmis par valeur, ces valeurs sont affectées aux paramètres formels
correspondants. Conceptuellement, les paramètres transmis par valeur offrent un mécanisme
pour passer des données à la procédure. Un paramètre formel qui est un paramètre transmis
par valeur est distinct du paramètre effectif correspondant. C’est une nouvelle variable avec
un identificateur et un emplacement mémoire propres. Quand une procédure est appelée, le
système affecte les valeurs des paramètres effectifs correspondants aux paramètres transmis
par valeur appropriés. Lorsque la procédure se termine, la mémoire allouée aux paramètres
transmis par valeur est libérée et la valeur du paramètre transmis par valeur n’existe plus.

Dans l’instruction d’appel de procédure

factoriel(10, b) ;

la valeur de la constante 10 est affectée au paramètre formel x. La procédure effectue ensuite


tous ses calcule en utilisant x. De même, lorsque l’instruction d’appel de procédure

factoriel(n, nombre) ;

est exécutée, la valeur de n est affectée à x. La procédure effectue ensuite tous ses calculs
avec x. La valeur de n reste inchangée même si x est modifiée dans la procédure. En effet, elle
reste encore inchangée lorsque la procédure se termine, et x cesse d’exister. C’est précisément
parce que les paramètres transmis par valeur obtiennent leurs valeurs par une affectation des
valeurs des paramètres effectifs aux paramètres formels correspondants qu’on les appelle des
paramètres transmis par valeur. Comme les affectations sont utilisées pour communiquer les
valeurs aux paramètres formels transmis par valeur, les paramètres effectifs correspondants
aux paramètres transmis par valeur peuvent être des constantes, des variables ou des
expressions. Si une expression est utilisée comme paramètre effectif, elle est d’abord évaluée
et sa valeur est ensuite affectée au paramètre formel.

Le paramètre formel y de l’en-tête de procédure est un paramètre transmis par adresse, ou


paramètre variable. Le mot réservé var qui précède l’identificateur de variable est utilisé pour
désigner les paramètres transmis par référence. Les paramètres transmis par référence sont
utilisés pour passer les données à la procédure et pour retourner les résultats. Lorsque des
paramètres transmis par référence sont utilisés, les valeurs des paramètres effectifs ne sont pas
affectées aux paramètres formels correspondants. En effet, on peut regarder les paramètres
effectifs et formels comme des synonymes ; c’est-à-dire des identificateurs différents de la
même variable. Lorsque les paramètres transmis par référence sont utilisés, de nouvelles
variables ne sont pas créées. En effet, pendant l’exécution de la procédure, un emplacement
mémoire, qui existe déjà et dont l’identificateur est celui du paramètre effectif correspondant,
reçoit un autre noml’identificateur du paramètre transmis par référence. Ainsi, tout ce qui
s’applique au paramètre transmis par référence sera automatiquement appliqué au paramètre
effectif correspondant.

Comme les paramètres transmis par référence communiquent en établissant une équivalence
entre les identificateurs des variables correspondantes, les paramètres effectifs correspondants
aux paramètres transmis par référence doivent être des variables. Ils ne peuvent pas être des
constantes ou des expressions. En général, on utilise des paramètres transmis par valeur pour
retourner les résultats. On peut bien entendu utiliser les paramètres transmis par référence à la

Dr Ndi Nyoungui André


105 Algorithmique et structures de données

fois pour passer des données à la procédure et retourner les résultats. Toutefois, comme les
paramètres transmis par référence modifieront automatiquement les paramètres effectifs
correspondants, si les paramètres formels sont modifiés dans la procédure, on évitera de les
utiliser lorsque les valeurs des paramètres effectifs doivent être protégées.

Autres exemples

Exemple 1. écrire une procédure qui reçoit en entrée un entier n, lit n nombres et le retourne
la somme de ces nombres.

procédure sommeliste(n : entier ; var somme : réel) ;


variable
nombre : réel ;
i : entier ;
début
i  0 ;
somme  0 ;
tantque i  n faire
début
lire(nombre) ;
somme  somme + nombre ;
i  i + 1 ;
fin ;
fin ;

Exemple 2. écrire une procédure qui reçoit en entrée un entier n, lit n nombres calcule la
différence entre la somme des nombres de rang impair et la somme des nombres de rang pair.

procédure différence(n : entier ; var somme : réel) ;


var
nombre : réel ;
i : entier ;
pair : booléen ;
début
i  0 ;
somme  0 ;
pair  faux ;
tantque i  n faire
début
lire(nombre) ;
i  i + 1 ;
si pair alors
somme  somme + nombre ;
sinon
somme  somme - nombre ;
pair  non pair ;
fin ;
fin ;

Dr Ndi Nyoungui André


106 Algorithmique et structures de données

Exercices d’apprentissage

Exercice 4.8
Écrire une procédure qui reçoit en entrée un entier naturel n, lit n nombre réels et retourne le
plus grand de ces nombres.

Exercice 4.9
Écrire une procédure qui prend en entrée un entier naturel n, lit n nombres et retourne le plus
grand de ces nombres ainsi que sa position dans la liste.

Exercice 4.10
Écrire une procédure qui prend en entrée un entier n, lit n nombres et retourne le plus grand et
le plus petit de ces nombres ainsi que leurs positions respectives dans la liste.

Exercice 4.11
Écrire une procédure qui teste si un nombre n, non négatif, est un carré parfait. Cette
procédure fournit deux résultats : un booléen qui est vrai si et seulement si n est un carré
parfait, et un entier égal à la partie entière de la racine carrée de n.

Exercice 4.12
Utiliser la procédure ci-dessus dans une autre procédure, qui, pour un nombre entier, imprime
la racine de sa racine carrée ou de la partie entière de sa racine carrée, ou un message d’erreur
si le nombre est négatif.

Exercice 4.13
Écrire une fonction qui calcule n p, où p est un entier naturel et n un entier quelconque.

Exercice 4.14
Écrire une procédure qui prend en entrée trois nombres a, b et c et les permute de telle sorte
que l’on obtienne a  b  c.

4.5 Précondition et postcondition

Un outil important pour créer des algorithmes fiables est de préciser clairement les contraintes
sur les paramètres de la fonction ou de la procédure. On peut prédire ce que fera une
procédure ou une fonction si et seulement si toutes les préconditions de la procédure ou de la
fonction sont satisfaites. Inversement, une postcondition est la spécification de ce que fera
une fonction ou une procédure lorsque toutes les préconditions sont satisfaites.

Quand on utilise les concepts de précondition et de postcondition, la forme générale d’une


définition de fonction est la suivante :

fonction nomfonction(liste des paramètres formels) : type-résultat ;


précondition : xxxxx
postcondition : xxxxx
Déclaration des variables locales
début
instruction1 ;

Dr Ndi Nyoungui André


107 Algorithmique et structures de données

instruction2 ;

instructionn ;
fin ;

De même, quand on utilise les concepts de précondition et de postcondition, la forme


générale d’une définition de procédure est la suivante :

procédure nomprocédure (liste des paramètres formels) ;


précondition : xxxxx
postcondition : xxxxx
Déclaration des variables locales
début
instruction1 ;
instruction2 ;

instructionn ;
fin ;

Il est aussi possible de combiner les préconditions et les postconditions dans une seule
instruction de spécification. Dans ce cas, on insère la spécification à la suite de l’en-tête de la
procédure ou de la fonction : le mot spécification, suivi d’une précondition, d’une flèche
() et d’une postcondition.

Par exemple, la fonction factoriel marche correctement lorsque le paramètre n reçoit une
valeur positive. Nous pouvons donc réécrire cette fonction de la manière suivante :

fonction factoriel(n : entier) : entier ;


Spécification : {n  0}  {résultat = n !}
variable
i, produit : entier ;
début
produit  1 ;
pour i  1 haut n faire
produit  produit * i ;
factoriel  produit ;
fin ;

Mais il n’est pas préciser comment on va procéder pour que ces contraintes soient respectées.
Les commentaires du code source peuvent être facilement ignorés, et peuvent devenir
obsolètes à cause des modifications opérées dans l’algorithme. Pour cela, certains langages de
programmation, à l’instar du C++, offre un moyen facile d’inclure des macros de vérification
des préconditions dans le code source des fonctions, un sujet qui sera examiné dans le cadre
du cours de programmation en C++.

4.6 La récursivité

Dr Ndi Nyoungui André


108 Algorithmique et structures de données

Une fonction ou procédure récursive est une fonction ou procédure qui s’appelle elle-même
directement ou indirectement. Par exemple, rappelons que le factoriel d’un entier naturel est
défini par :

1, si n  0
n!  
n( n  1 )! , sinon

Cette définition comprend deux parties :

1. Une définition de la fonction en terme d’elle-même.


2. Une valeur concrète de la fonction en un certain point. La définition de la fonction en
termes d’elle-même doit aussi conduire à la valeur concrète si elle est appelée certain
nombre de fois.

C’est à ce genre de problèmes que la récursivité s’applique avec succès. Ceci parce que :

1. La solution est facile à calculer pour certains cas simples appelés cas de base. (Dans
notre exemple, le cas de base est le calcul de 0 ! qui est égal à 1).
2. Pour les autres cas, il existe un ensemble bien défini de règles qui conduisent
éventuellement à un cas de base. Ce sont les étapes de la récursivité. (Dans notre
exemple, pour n > 0 nous avons une règle qui nous permet de redéfinir le problème
avec une valeur de n plus petite. Ceci conduit éventuellement à une définition du
problème avec une valeur de 1 pour n!, et ainsi on a atteint le cas de base).

À partir de cette définition récursive, une version récursive de la fonction qui calcule le
factoriel d’un entier naturel est la suivante :

fonction factoriel(n :entier) : entier ;


début
si n = 0 alors
factoriel  1
sinon
factoriel  n*factoriel(n - 1) ;
fin ;

Comme nous l’avons déjà vu, une version itérative de la fonction factoriel est :

fonction factoriel(n : entier) : entier ;


var
produit, i : entier ;
début
produit  1 ;
pour i  n bas 1 faire
produit  produit*i ;
factoriel  produit ;
fin ;

On a donc le choix entre une approche itérative et une approche récursive. La question est
maintenant de savoir laquelle des approches est supérieure à l’autre ? Dans la plupart des cas,

Dr Ndi Nyoungui André


109 Algorithmique et structures de données

l’approche itérative est toujours plus efficace. L’ordinateur prend beaucoup plus de temps et
utilise beaucoup plus de mémoire pour effectuer des appels et des retours récursifs plus qu’il
n’en prend lorsqu’il exécute les boucles. Toutefois, la récursivité est très utile lorsqu’on ne
peut pas trouver directement une solution au problème alors que l’on peut trouver un moyen
de transformer le problème original en un problème identique plus simple ou plus petit. La
récursivité est aussi utile lorsque le problème est initialement exprimé de façon récursive. De
nombreux problèmes mathématiques sont de cette nature.

Exemple 1. Un exemple de fonction particulièrement adaptée à une définition récursive est


l’algorithme d’Euclide pour le calcul du plus grand diviseur commun de deux entiers positifs.
En effet, l’algorithme d’Euclide stipule que si a et b sont des entiers positifs (avec a supérieur
b) alors le plus grand diviseur commun de a et b est égal au dernier reste non nul dans la suite
des divisions successives de a par b. Ceci conduit donc à la définition récursive suivante :

b , si b  a et a mod b  0

GCD( a ,b )   GCD( b , a ), si a  b
 GCD( b , a mod b ), sinon

Il est donc maintenant facile de trouver une version récursive de la fonction qui calcule le plus
grand diviseur commun de deux entiers positifs.

fonction GCD(a, b : entier) : entier ;


début
si (a < b) alors
GCD  GCD(b,a) 
sinon
si (a mod b = 0) alors
GCD  b 
sinon
GCD  GCD(b, a mod b)
fin ;

Exemple 2. Le deuxième exemple de problème récursif que nous présentons est celui du
calcul des nombres de Fibonacci. Le nème nombre de Fibonacci est défini récursivement de la
manière suivante :

1. Si n est égal à 0 ou à 1 alors le nème nombre de Fibonacci est égal à n.


2. Si n est supérieur ou égal à 2 alors le nème nombre de Fibonacci est égal à la somme des
deux nombres de Fibonnaci précédents.

À partir de cette définition, on obtient facilement la fonction récursive suivante qui calcule le
nème nombre de Fibonacci.

fonction Fibonacci(n : entier) : entier ;


début
si (n = 0) ou (n = 1) alors
Fibonacci  n
sinon
Fibonacci  Fibonacci(n - 1) + Fibonacci(n - 2) ;

Dr Ndi Nyoungui André


110 Algorithmique et structures de données

fin ;

Exemple 3. Le troisième exemple de problème récursif est le calcul des coefficients


binomiaux. En effet, le nombre de combinaisons p à p de n objets est défini par la relation de
récurrence :

 n   n  1  n  1   n  n n
        ,       1 ,    0 , si n  p .
 p   p   p  1  0  n  p

À partir de cette définition, on obtient facilement la fonction récursive suivante qui calcule les
coefficients binomiaux..

fonction binôme(n, p : entier) : entier ;


début
si (n < p) alors
binôme  0
sinon
si (n = p) ou ( p = 0) alors
binôme  1
sinon
binôme  binôme(n - 1, p) + binôme(n - 1, p - 1) ;
fin ;

Exemple 4. Le quatrième exemple de problème récursif concerne le calcul des nombres Anp .
En effet, on démontre que le nombre d’arrangements de p éléments d’un ensemble à n
éléments est donné par la relation de récurrence :
Anp  Anp1 pAnp11  ;
An0 1;
Ann n!
À partir de cette définition, on obtient facilement la fonction récursive suivante qui calcule les
nombres Anp .

fonction nbarrange(n, p : entier) : entier ;


début
si (n < p) alors
nbarrange  0
sinon
si ( p = 0) alors
nbarrange  1
sinon
si ( n = p) alors
nbarrange  factoriel(n)
sinon
nbarrange  nbarrange(n-1, p) + p*nbarrange(n - 1, p-1) ;
fin ;

Exercices d’apprentissage

Dr Ndi Nyoungui André


111 Algorithmique et structures de données

Exercice 4.15
Considérons la fonction suivante :

fonction Mystère(a : entier) : entier ;


début
si a < 0 alors
écrire(‘Erreur’)
sinon
si a = 0 alors
Mystère  0
sinon
Mystère  a + Mystère(a – 1)
fin ;

Quelle est la sortie de cette fonction en utilisant les paramètres effectifs suivants ?
(a) 0 (b) –1
(c) 5 (d) 10

Exercice 4.16
Réécrire la fonction ci-dessus sous forme itérative.

Exercice 4.17
Écrire une version itérative de la fonction qui calcule le plus grand diviseur commun de deux
entiers positifs.

Exercice 4.18
Écrire une version itérative de la fonction qui calcule le nième nombre de Fibonacci.

Exercice 4.19
Écrire une version itérative de la fonction qui calcule les coefficients binomiaux.

4.6 La conception modulaire des algorithmes

La modularité

Nous avons examiné en détail dans les chapitres précédents, le processus de conception des
algorithmes pour des problèmes plus ou moins complexes. Nous allons voir maintenant
comment un algorithme complexe peut être décomposés en unités fonctionnelles plus petites
appelées modules et comment on peut utiliser ces modules pour faciliter la conception et la
validation de gros algorithmes.

Un module est une partie d’un système plus grand, plus complexe qui effectue une tâche
spécifique bien définie. Quand on parle de système complexe, que ce soit une pièce dans une
machine ou un algorithme, on peut le comprendre plus facilement si on le regarde comme
étant composé d’unités fonctionnelles distinctes. Par exemple, on peut regarder une
automobile comme consistant en un corps, un train de puissance (moteur et transmission), un
système de refroidissement, un système de suspension et ainsi de suite. Chacune de ces
composantes est un module.

Dr Ndi Nyoungui André


112 Algorithmique et structures de données

Quand on conçoit une automobile, on peut le faire en termes de ces modules et ensuite
concevoir les modules eux-mêmes en termes d’autres modules. Par exemple, le train de
puissance, comme nous l’avons dit, consiste en un moteur et un système de transmission. Le
moteur lui-même peut être subdivisé en composants.

En programmation, ce processus de décomposition peut continuer jusqu’à ce que l’on


obtienne des modules pouvant être exprimés en termes d’instructions élémentaires disponibles
dans un langage de programmation donné. Ces différents modules forment une hiérarchie de
modules comme le montre la figure ci-dessous.

Figure 1.2. Représentation d’une hiérarchie de modules

Module
Niveau 1

Module Module Module


Niveau 2.1 Niveau 2.2 Niveau 2.3

Module Module Module Module Module


Niveau 3.1 Niveau 3.2 Niveau 3.3 Niveau 3.4 Niveau 3.5

Le module de niveau 1 représente le système entier. Les modules de niveau 2 sont les
composantes majeures du système. Les modules du niveau 3 sont les blocs de construction à
partir desquels les modules du niveau 2 sont construits, et ainsi de suite.

Lorsque le système en considération est un programme informatique, le module de niveau 1


représente le programme principal. Les modules des niveaux inférieurs représentent les
procédures et les fonctions.

Conception descendante

L’organisation hiérarchique d’un programme s’accommode naturellement à une conception


descendante. En utilisant cette méthode, on conçoit d’abord le module de niveau 1 ; c’est-à-
dire le programme principal. On le fait en termes de modules de niveau 2 ; cela signifie que
l’on suppose que l’on a à sa disposition les procédures et les fonctions de niveau 2, et on
conçoit le module de niveau 1 en utilisant ces blocs de construction pas encore écrits. Pendant
que l’on travaille sur le programme principal, on ne se soucie pas de savoir comment les
modules du niveau 2 vont effectuer leurs tâches respectives. Tout ce qui retient notre attention
ce sont les tâches qu’ils vont effecteur et comment ils vont interfacer (communiquer) avec le
programme principal.

Dr Ndi Nyoungui André


113 Algorithmique et structures de données

Une fois que le module de niveau 1 à été conçu et validé, on commence à travailler sur les
modules de niveau 2. Ici encore, on procède un module à la fois et on conçoit chaque module
en fonctions des modules de niveau 3 qui lui sont apparentés. Une fois de plus, on n’est pas
intéressé par les détails de ces sous-modules, même pas des autres modules du même niveau.
Dans des gros projets, il est probable que des équipes différentes travaillent sur les autres
modules.

En procédant de cette manière, on conçoit le programme de haut en bas. À chaque étape du


processus on est intéressé par la conception d’un seul module en termes de ses modules
directement subordonnés.

Test descendant et test ascendant

Une première approche pour effecteur le test d’un algorithme consiste à commencer par tester
en premier lieu le module de niveau 1. On teste ensuite les modules du niveau 2, et ainsi de
suite, jusqu’à ce que tous les modules du programme soient testés. Les modules sont ici testés
en suivant l’ordre de décomposition de l’algorithme. Cette approche est appelée le « test
descendant ». Dans cette approche, chaque module est testé dans le même environnement
dans lequel il sera utilisé dans le programme final. Il y a cependant un problème. Comment
peut-on tester le module de niveau 1 alors que les modules de niveau 2 (qui sont appelés par
le module de niveau 1) ne sont pas encore écrits ? La réponse consiste à remplacer ces
modules pas encore écrits par des maquettes. Une maquette est un module factice qui prend la
place du module effectif pendant le test des modules de niveau supérieur. Ce qu’une maquette
fait dépend de la tâche que le module qu’il remplace devra accomplir.

Une deuxième approche consiste à tester les modules dans l’ordre inverse de décomposition.
On commence alors par tester les modules de plus bas niveau, puis les modules de niveau
juste au-dessus et ainsi de suite jusqu’à ce que le module de niveau 1 soit testé. Une telle
approche est appelée le « test ascendant ».

Avantages de la conception modulaire

4.7 Conclusion

Nous avons vu comment le programmeur peut définir ses propres fonctions et procédures
pour enrichir le langage algorithmique. Ces fonctions et procédures sont en général écrites
pour répondre à un besoin bien précis.

Une fois rendues disponibles, ces fonctions et procédures peuvent être utilisées pour écrire
des algorithmes plus complexes, conformément au concept de modularité que nous venons de
présenter. Les algorithmes que nous allons écrire dans le cadre des cours qui vont suivre
seront exprimés exclusivement sous forme de fonction ou de procédure, sous-entendu qu’on
pourra les mettre ensemble pour résoudre des problèmes plus complexes.

La récursivité est une technique de programmation par laquelle une procédure ou fonction
s’appelle elle-même, directement ou indirectement. L’utilisation de la récursivité a deux
inconvénients majeurs : primo, elle a un impact négatif sur la vitesse d’exécution à cause de la

Dr Ndi Nyoungui André


114 Algorithmique et structures de données

surcharge qui découle des appels et des retours répétés de la même fonction ; secundo, les
appels récursifs tendent à utiliser abondamment le tas, car le programme sauvegarde l’adresse
de retour de chaque appel de fonction avec les arguments et les variables locales. Mais ces
désavantages sont souvent supplantés par un avantage clair pour le programmeur : une
solution récursive à un problème peut être la plus simple, et par conséquent plus facile à
débugger et à comprendre.

4.8 Etudes de cas

Calcul de la dépréciation d’un bien économique

Dans le but de calculer les taxes, les biens économiques peuvent être dépréciés en utilisant
l’une de trois méthodes différentes. L’approche la plus simple est appelée la dépréciation
linéaire « straight-line depreciation ». En utilisant cette méthode, la valeur du bien décroît de
la même quantité chaque année sur la période totale. Ainsi, si un article est déprécié sur une
période de n années, 1/n de la valeur totale est déprécié chaque année.
Une deuxième méthode utilise le « double-declining balance depreciation ». Dans cette
méthode, la dépréciation autorisée chaque année est égale à la valeur courante multipliée par
2/n, où n est le nombre d’années au cours desquelles l’article doit être déprécié.
Une troisième méthode, appelée la méthode de la somme des chiffres « sum-of-the-digits
method », fonctionne de la manière suivante : D’abord, la somme des nombres de 1 à n est
calculée, où n est le nombre d’années au cours desquelles le bien est déprécié. Ensuite, la
dépréciation autorisée à la i-ème année est égale à la valeur originale multipliée par (n – i) + 1
divisée par la somme des nombres.
Écrire trois fonctions différentes pour calculer la dépréciation d’un article en utilisant chacune
des méthodes ci-dessus.

Construction du triangle de Pascal

On veut écrire une procédure pour construire les n premières lignes du triangle de Pascal. Le
triangle de Pascal est un tableau de nombres dont les entrées sont les coefficients binomiaux
 p n!
  
 n  p! ( n  p )!

Les entrées du triangle sont telles que les nombres situés sur une ligne correspondent à la
même valeur de n tandis que les nombres situés sur une colonne correspondent à la même
valeur de p.

Dr Ndi Nyoungui André


115 Algorithmique et structures de données

Chapitre 5

Introduction aux structures de données

5.1 Introduction

Toutes les variables utilisées dans un algorithme doivent être déclarées et leur types doivent
être spécifiés. Un type de données spécifie un ensemble de valeurs de même nature pouvant
être prises par des variables dans un algorithme. Le langage algorithmique offre une variété de
types de données allant des types de données simples aux structures de données très
complexes. Les types de données simples du langage algorithmique sont appelés les types
scalaires. Ce sont les types de base à partir desquels tous les autres types sont construits. Les
types de données scalaires sont eux mêmes divisés en deux groupes : les types scalaires
standards et les types scalaires définis par l’utilisateur.

Les types de données définis par l’utilisateur sont définis par le programmeur pour aider à la
résolution d’un problème particulier. Les types de données scalaires sont des types non
structurés, c’est-à-dire qu’ils consistent en des valeurs simples distinctes. Une variable de type
simple ne peut prendre qu’une seule valeur de ce type à un point donné de l’algorithme. Ainsi,
une variable de type entier ne peut recevoir qu’une seule valeur de type entier. De même une
variable de type caractère ne peut recevoir qu’une seule valeur de type caractère. Le langage
algorithmique offre aussi des types de données structurés qui sont construits à partir d’autres
types de donnés et dont les éléments sont des collections ou groupes valeurs.

Les principaux types scalaires standards du langage algorithmique sont :

 le type entier qui est l’ensemble des valeurs entières (positives ou négatives) qui
peuvent être représentés dans la machine.
 le type réel qui est l’ensemble des valeurs de nombres réels.
 le type caractère qui est l’ensemble des caractères représentés par le code utilisé
par la machine (code ASCII par exemple).
 le type booléen qui est l’ensemble formé par les deux valeurs vrai et faux

Les types de données scalaires définis par l’utilisateur sont définis par l’utilisateur pour aider
à résoudre un problème particulier. Des exemples de types de données scalaires définis par
l’utilisateur sont :

 le type énuméré (énumération de l’ensemble de toutes les valeurs qu’une variable


de ce type peut prendre)
 le type intervalle (restriction de l’intervalle des valeurs d’un type existant)
 le type pointeur (adresse des emplacements mémoire)

Les types de données structurés sont construits à partir d’autres types données, scalaires ou
structurés. Une donnée structurée est une collection de valeurs scalaires ou structurées. Un
type de données structuré est un type de données dans lequel les valeurs sont des collections

Dr Ndi Nyoungui André


116 Algorithmique et structures de données

de valeurs de même type ou de types différents. Il existe plusieurs manières de structurer ou


d’organiser des données pour construire des structures de données composés.

Les principaux types de données structurés sont :

 les chaînes de caractères


 les articles ou enregistrements
 les ensembles
 les tableaux à une ou à plusieurs dimensions
 les fichiers
 les listes linéaires chaînées
 les arbres
 les objets.

5.2 Les types articles

5.2.1 Le concept d’article

Une structure de données qui est capable de stocker des informations de différents types a de
nombreuses applications pratiques. Par exemple, quand on travaille avec les informations de
paie d’un employé, on a besoin de stocker et de manipuler les variables suivantes :

 le nom de l’employé (une chaîne de caractères)


 le numéro matricule de l’employé (un entier)
 le salaire de l’employé (un réel)

Il sera plus commode de regrouper toutes ces informations sous un seul identificateur pouvant
être manipulé comme une entité unique. En informatique, une structure de données qui permet
de regrouper et de manipuler différents types de données comme une entité unique est appelée
un article ou un enregistrement. Plus formellement, un article est une structure consistant en
un nombre fixe de composantes appelées champs ou membres pouvant être de types
différents.

5.2.2 Définition et déclaration des articles

Les informations de paie ci-dessus peuvent être regroupées dans un article de la manière
suivante :

type
temployé = article
nom : chaîne ;
matricule : entier ;
salaire : réel ;
fin ;

Les mots réservés article et fin sont utilisés pour délimiter la définition. Une fois qu’un type
d’article a été défini et rendu disponible, il peut ensuite être utilisé pour déclarer des variables
de ce type :

Dr Ndi Nyoungui André


117 Algorithmique et structures de données

variable
employé : temployé ;

Comme autre exemple de type d’articles, considérons la représentation d’une date. On peut
l’envisager comme un article comprenant trois champs : une variable définie par l’utilisateur
pour le mois et deux entiers pour le jour et l’année :

type
tmois = (janvier, février, mars avril, mai, juin , juillet, août, septembre,
octobre, novembre, décembre) ;
tdate = article
mois : tmois;
jour : 1..31 ;
année : entier;
end;

variable
Today, Birthday : tdate;

Les variables Today et Birtday sont de type tdate et chacune est capable de stocker toutes les
composantes de cette structure.

La forme générale d’une définition de type d’article est la suivante :

type
télément = article
champ1 : Type1 ;
champ2 : Type2 ;
.
.
.
champn : Typen ;
fin ;

où champ1, …, champn sont les différents champs de l’article avec leurs types respectifs.
Type1, …, Typen peuvent être n’importe quel type scalaire (entier, réel, caractère, booléen,
énuméré) ou un type structuré (vecteur, article, pointeur).

Voici d’autres exemples de définitions de type d’article :

type
tabonné = article
nom : chaîne20 ;
adresse : chaîne30 ;
numéro : chaîne8 ;
fin ;

tpersonne = article
nom : chaîne20 ;

Dr Ndi Nyoungui André


118 Algorithmique et structures de données

sexe : (masculin, féminin) ;


age : entier;
statut : (single, married, divorced, widowed) ;
fin;

Le type article tabonné a trois champs. Chacun des champ est une chaîne de caractères de
longueur différente. Le type article tpersonne a quatre champs. Noter qu’un type défini par
l’utilisateur peut être spécifié dans une définition d’article, comme cela est le cas avec les
champs Sexe et Statut. Les champs d’un article ne sont pas nécessairement de type scalaire.
Nous allons voir dans la suite qu’ils peuvent souvent être eux-mêmes des articles.

5.2.3 Primitives d’accès aux articles

Étant donné les définitions ci-dessus, on peut maintenant déclarer des variables de ce type.
Par exemple, des variables de type tpersonne peuvent être déclarées de la manière suivante :

variable
Myself, Yourself : tpersonne ;

Les champs d’un article sont les composantes d’une même variable et sont référencés en
spécifiant le nom de la variable, suivi par l’identificateur de champ, séparés par un point. Une
telle expression est appelée un désignateur de champ. Un désignateur de champ désigne
l’emplacement mémoire associé à la composante correspondante de l’article. Les instructions
d’affectation

Myself.Nom  'Mvondo Louis' ;


Myself.Sexe  masculin;
Myself.Age  25;
Myself.Statut  single;

affectent des valeurs aux champs de l’article Myself.

Les désignateurs de champ peuvent être utilisés comme tous les autres identificateurs de
variables. Ainsi, l’instruction

Écrire(Myself.nom)

imprime la valeur du champ Nom de l’article Myself. L’instruction

Lire(Yourself.age)

lit une valeur pour le champ Age de la variable Yourself. Enfin, l’instruction

Différence  Myself.age – Yourself.age

calcule la différence entre les deux âges.

La valeur d’un article entier peut être affectée à un autre article de même type en utilisant une
seule instruction d’affectation. L’instruction d’affectation

Dr Ndi Nyoungui André


119 Algorithmique et structures de données

Yourself  Myself

est équivalente à :

Yourself.nom  Myself.nom ;
Yourself.sexe  Myself.sexe;
Yourself.age  Myself.age;
Yourself.statut  Yourself.statut;

La lecture ou l’écriture d’une variable article nécessite habituellement de longues procédures,


dépendant du nombre et du type des champs impliqués. La lecture d’une variable article
implique en effet la lecture de chacun des champs de l’article. De même, l’écriture d’un
article implique l’écriture de chacun des champs individuels de l’article.

Par exemple, la procédure ci-dessous lit un article de type tpersonne.

procédure lirepersonne(var pers : tpersonne) ;


variable
nombre : entier ;
début
{lecture du nom}
écrireln('Nom : ') ;
lire(pers.Nom) ;
{lecture du sexe}
écrireln('Sexe (1 : féminin, 2 : masculin) ') ;
lire(nombre) ;
si nombre = 1 alors
pers.Sexe  féminin
sinon
pers.Sexe  masculin ;
{lecture de l’âge}
lire(pers.Age) ;
{lecture du statut}
écrireln('Statut (1 : Single, 2 : Married, 3 : Divorced, 4 : Widowed) ') ;
lire(nombre) ;
sélection nombre de
1 : pers.statut  single ;
2 : pers.statut  married ;
3 : pers.statut  divorced ;
4 : pers.statut  widowed ;
fin ;
fin ;

De même, la procédure ci-dessous écrit un article de type tpersonne.

procédure écrirepersonne(pers : tpersonne) ;


début
{écriture du nom}

Dr Ndi Nyoungui André


120 Algorithmique et structures de données

écrireln('Nom : ', pers.Nom) ;


{écriture du sexe}
si pers.Sexe = masculin alors
écrireln('Sexe : Masculin')
sinon
écrireln('Sexe : Féminin') ;
{écriture de l’âge}
écrireln('Age : ', pers.Age) ;
{écriture du statut}
sélection pers.Statut de
Célibataire : écrireln('Statut : Célibataire') ;
Marié : écrireln('Statut : Marié') ;
Divorcé : écrireln('Statut : Divorcé') ;
Veuf : écrireln('Statut : Veuf');
fin ;
fin ;

5.2.4 Les articles imbriqués

La syntaxe d’un article en algorithmique n’interdit pas qu’un champ dans un article soit lui-
même un autre article. Considérons les définitions de types suivantes :

type
tdéduction = article
CFC : réel ;
CRTV : réel ;
CNPS : réel ;
fin ;

temployé = article
Nom : chaîne20 ;
Matricule : entier ;
SalaireBrute : réel ;
Taxes : tdéduction ;
SalaireNet : réel ;
fin ;

variable
déd : tdéduction ;
employé : temployé;

La variable Employé est un article ayant cinq champs. Le champ « Taxes » est lui-même un
article ayant ses propres champs. C’est un sous-article de temployé. Pour accéder à un champ
quelque de Taxes, on doit spécifier toutes les variables impliquées. Pour affecter des valeurs à
tous les champs de employé, on écrit :

Employé.Nom  'Mohamadou' ;
Employé.Matricule  101498092 ;
Employé.SalaireBrute  3132.50 ;

Dr Ndi Nyoungui André


121 Algorithmique et structures de données

Employé.Taxes.CFC  650.75 ;
Employé.Taxes.CRTV  139.17 ;
Employé.Taxes.CNPS  133.55 ;
Emploué.SalaireNet  2794.03

Un champ d’un sous-article de Employé est référencé par un désignateur de champ impliquant
deux identificateurs. Le désignateur de champ

Employé.Taxes.CFC

référence le champ CFC du sous-article employé.Taxes. Noter que pour accéder à un champ
tel que SalaireBrute, un seul identificateur de champ est utilisé car aucun sous-raticle n’est
impliqué. Le désignateur de champ

employé.Taxes

référence un article de type tdéduction et peut par conséquent recevoir une valeur de ce type.

Exercices d’apprentissage

Exercice 5.1
Écrire des définitions d’articles ,pour représenter les données suivantes :
(a) Un employé ayant des champs pour le nom, le numéro matricule, le salaire brute et le net à
percevoir.
(b) Une équipe de football ayant les champs pour le nom de l’équipe, le nombre de matchs
joués, le nombre de matchs gagnés, le nombre matchs nuls, le nombre de matchs perdus et le
pourcentage de matchs gagnés.
(c) Un compte bancaire ayant des champs pour le numéro de compte, le nom du titulaire du
compte, l’adresse du titulaire, le solde courant et les intérêts cumulés.
(d) Un numéro de téléphone ayant des champs pour la code de région, le préfixe et le numéro.

Exercice 5.2
Écrire des procédures pour lire et écrire des articles de type temployé.

5.2.5 Les articles avec variantes

Les articles que nous avons considérés jusque là sont constitués uniquement de champs fixes.
Cela signifie que une fois que l’on a défini un type d’articles consistant en un nombre de
champs de différents types, toutes les variables de ce type contiennent toutes ces mêmes
champs. Souvent, il est utile d’avoir des articles contenant seulement quelques champs
identiques. Cela signifie que l’on a parfois besoin de considérer des articles de même type
même si leurs structures peuvent différer d’une certaine manière, comme par exemple avoir
un certain nombre de champs différents, possiblement de types différents. Pour illustrer cela,
considérons les définitions suivantes :

type
toption = (GBIO, MIP, GTE, GIN) ;
tétudiant = article
Nom : chaîne20 ;

Dr Ndi Nyoungui André


122 Algorithmique et structures de données

Sexe : (Masculin, Féminin) ;


Age : entier ;
case Option : toption of
GBIO : (Chimie, Biochimie : réel);
MIP : (Electronique, Electricité, Mécanique, Automatique : réel) ;
GTE : (Themique, Electromécanique, Régulation : réel) ;
GIN : (Algorithmique, Programmation, Analyse, Infographie : réel) ;
fin ;

Dans cette définition nous avons rendu l’existence de certains champs dépendante de la valeur
d’un autre champ. Tous les étudiants ont certaines informations en commun : Nom, Sexe,
Age, Option. Cependant, dépendant de la valeur de Option, on voit que les informations
restantes sont différentes. Les étudiants de l’option Mathématiques ont en plus les champs
Analyse et Algèbre. Pour les étudiants de l’option Informatique on a en plus des champs
communs, les champs Langages, Réseaux et Infographie. Enfin les étudiants de l’option
Physique ont en plus les champs Optique, Electricité et Mécanique. Un article qui permet de
telles variations dans sa structure est appelé un article avec variantes.

Examinons plus en profondeur la définition d’article avec variantes ci-dessus. La définition


consiste en deux parties : la partie fixe et la partie variable. Lorsqu’elle existe, la partie fixe
ou invariante doit précéder la partie variable. Dans cet exemple, elle est constituée de tous les
champs listés avant le mot réservé case : Nom, Sexe et Age. Le champ Option est appelé le
champ de sélection. Même si dans cet exemple, le champ de sélection est de type énuméré, il
peut être de n’importe quel scalaire, à l’exception du type réel. Le champ de sélection est un
champ effectif de l’article et est utilisé pour stocker des valeurs. C’est un champ fixe.

À la suite du mot réservé of, on a une liste de déclarations de champs variables, la partie
variable de l’article. La déclaration de chaque champ variable consiste en une constante de
même type que le champ de sélection, suivi par deux points, suivi par une liste de déclarations
de champs entre parenthèses. La liste de déclarations de champs peut être vide, signifiant que
cette variante de l’article n’a pas besoin de champs additionnels autres que ceux listés dans la
partie fixe.

Dans les articles variables, les identificateurs de champs doivent être distincts. Un même
identificateur de champ ne peut pas apparaître dans deux listes de champs de la partie variable
ou dans la partie fixe et dans la partie variable. Une définition d’article ne peut avoir qu’une
seule partie variable. Cependant, les parties variables peuvent être imbriquées. Remarquer
qu’un seul mot réservé fin termine la partie variable et la définition de l’article.

Une variable déclarée comme étant une variable de type article avec variantes doit toujours
contenir tous les champs fixes, y compris le champ de sélection. Ainsi, étant donné la
déclaration :

variable
étudiant : tétudiant ;

les références à étudiant.Nom, étudiant.Sexe, étudiant.Age et étudiant.Option sont toujours


valides. Par contre, les champs variables qui peuvent être référencés dépendent de la valeur du
champ de sélection. Étant donné l’affectation suivante du champ de sélection :

Dr Ndi Nyoungui André


123 Algorithmique et structures de données

étudiant.Option  GIN

les champs Analyse et Algèbre, associés à la valeur Mathématiques du champ de sélection


sont maintenant valides, et il est possible de leur affecter des valeurs. Les affectations

étudiant.Algorithmique  68 ;
étudiant.Analyse  78

sont maintenant valides. Toutefois, c’est une erreur de référencer un champ variable non
associé à la valeur courante du champ de sélection. On utilise habituellement l’instruction de
sélection multiple pour éviter ces erreurs. L’utilisation de cette approche est illustrée dans les
exemples ci-dessous qui lise et écrive un article de type tétudiant.

Exemple 1. Procédure pour écrire un article de type tétudiant.

procédure écrireétudiant(étudiant : tétudiant) ;


début
{écriture de la partie fixe}
écrireln('Nom : ', étudiant.Nom) ;
si étudiant.Sexe = Masculin alors
écrireln('Sexe : Masculin')
sinon
écrireln('Sexe : Féminin') ;
écrireln('Age : ', étudiant.Age) ;
{écriture du champ de sélection}
sélection étudiant.option de
GBIO : écrireln('Option : Génie Biologique')
MIP : écrireln('Option : Maintenance Industrielle Productique') 
GTE : écrireln('Option : Génie Thermique et Energétique') ;
GIN : écrireln('Option : Génie Informatique') ;
fin ;
{écriture de la partie variable}
sélection étudiant.Option de
GBIO : début
écrireln('Chimie : ', étudiant.Chimie) ;
écrireln('Biochimie : ', étudiant.Biochimie) ;
fin ;
MIP : début
écrireln('Electronique : ', étudiant.Electronique) ;
écrireln('Electricité : ', étudiant.Electricité) ;
écrireln('Mécanique : ', étudiant.Mécanique) ;
écrireln('Automatique : ', étudiant.Automatique) ;
fin ;
GTE : début
écrireln('Electromécanique : ', étudiant.Electomécanique) ;
écrireln('Thermique : ', étudiant.Thermique) ;
écrireln('Régulation : ', étudiant.Régulation) ;
fin ;

Dr Ndi Nyoungui André


124 Algorithmique et structures de données

GIN : début
écrireln('Algorithmique : ', étudiant.Algorithmique) ;
écrireln('Programmation : ', étudiant.Programmation) ;
écrireln('Analyse : ', étudiant.Analyse) ;
écrireln('Infographie : ', étudiant.Infographie) ;
fin ;
fin ;

Exemple 2. Procédure pour lire les informations sur un étudiant.

procédure lireétudiant(var étudiant : tétudiant) ;


variable
ch : caractère ;
choix : entier ;
début
{lecture de la partie fixe}
écrire('Nom : ')
lireln(étudiant.Nom) ;
écrire('Sexe (F ou M) :') ;
lireln(ch) ;
si ch = 'F' alors
étudiant.Sexe  Féminin
sinon
étudiant.Sexe  Masculin ;
écrire('Âge : ')
lireln(étudiant.Age) ;
{lecture du champ de sélection}
écrire('Option (1 : GBIO, 2 : MIP, 3 : GTE, 4 : GIN) :');
lireln(choix) ;
sélection choix de
1 : étudiant.Option  GBIO ;
2 : étudiant.Option  MIP ;
3 : étudiant.Option  GTE ;
4 : étudiant.Option  GIN ;
fin ;
{lecture de la partie variable}
sélection étudiant.Option de
GBIO : début
écrire('Chimie : ') ;
lireln(étudiant.Chimie) ;
écrire('Biochimie : ') ;
lireln(étudiant.Biochimie) ;
fin ;
MIP : début
écrire('Electronique : ') ;
lireln(étudiant.Electronique) ;
écrire('Mécanique : ') ;
lireln(étudiant.Mécanique) ;
écrire('Automatique : ') ;

Dr Ndi Nyoungui André


125 Algorithmique et structures de données

lireln(étudiant.Automatique) ;
écrire('Electricité : ') ;
lireln(étudiant.Electricité) ;
fin ;
GTE : début
écrire('Electromécanique : ') ;
lireln(étudiant.Electromécannique) ;
écrire('Thermique : ') ;
lireln(étudiant.Thermique) ;
écrire('Régulation : ') ;
lireln(étudiant.Régulation) ;
fin ;
GIN : début
écrire('Algorithmique : ') ;
lireln(étudiant.Algorithmique) ;
écrire('Programmation : ') ;
lireln(étudiant.Programmation) ;
écrire('Analyse : ') ;
lireln(étudiant.Analyse) ;
écrire('Infographie : ') ;
lireln(étudiant.Infographie) ;
fin ;
end ;
end ;

Exercices d’apprentissage

Exercice 5.3
Définir un type d’article avec variantes qui pourrait être utilisée pour stocker les informations
concernant une figure géométrique. L’article doit stocker la forme (carré, triangle, rectangle
ou cercle) et les dimensions appropriées. Écrire ensuite une procédure pour lire les
informations sur une figure et une fonction qui prend en entrée une figure géométrique et
retourne sa surface.

Exercice 5.4
Soit le type article date, formé de trois nombres entiers qui indiquent respectivement le jour,
le mois et l’année.

type
tdate = article
jour, mois, année : entier ;
fin ;

Écrire une fonction booléenne qui prend entrée deux dates date1 et date2 et détermine si la
date date1 vient avant la date date2.

5.3 Les types ensembles

Dr Ndi Nyoungui André


126 Algorithmique et structures de données

5.3.1 Le concept d’ensemble

Considérons le problème suivant : Les informations sur un questionnaire sont enregistrées


comme dans la figure 5.1 suivante.

Figure 5.1. Résultat d’un questionnaire

 

Art Théâtre Tennis Baseball Football Politique Lecture

On a besoin d’un moyen de représenter ces informations tel que l’on puisse facilement ajouter
ou supprimer des intérêts, déterminer si une personne particulière est intéressée par un certain
sujet, comparer les intérêts de différentes personnes et travailler avec ces données.

Une approche consiste à représenter ces informations dans un vecteur de type booléen de la
manière suivante :

type
tdomaine = (Art, Théâtre, Tennis, Baseball, Football, Politique, Lecture) ;
tintérêts : vecteur[Domaine] de booléen ;

En utilisant le vecteur booléen Intérêts, on peut stocker les informations sur le questionnaire
de la manière suivante :

faux faux faux vrai faux vrai faux

Art Théâtre Tennis Baseball Football Politique Lecture

Avec cette méthode de représentation, on peut ajouter des intérêts en remplaçant un faux par
un vrai ou supprimer un intérêt en remplaçant un vrai par un faux. On peut aussi déterminer si
une personne a un intérêt particulier en examinant l’emplacement approprié du vecteur pour
déterminer s’il contient un vrai ou un faux.
Les vecteurs booléens sont un moyen convenable de représenter ce genre de données. Le
langage algorithmique a cependant un moyen plus convenable de faire cela, la structure de
données ensemble.
En mathématiques, un ensemble est une collection d’objets. En mathématiques, un ensemble
est représenté par des accolades contenant la liste des objets qui constituent la collection. Par
exemple, l’ensemble des entiers positifs impaires inférieurs à 10 est représenté par

{1, 3, 5, 7, 9}

en mathématiques. En algorithmique, on utilise des crochets carrés pour représenter les


ensembles. Ainsi, l’ensemble ci-dessus sera dénoté par :

[1, 3, 5, 7, 9]

Dr Ndi Nyoungui André


127 Algorithmique et structures de données

Les objets qui appartiennent à un ensemble sont appelés ses éléments. Ce qui rend les
ensembles utiles est le fait que les opérations telles que l’ajout ou la suppression des éléments
sont faciles à effectuer. Il est aussi facile de déterminer si un objet particulier est oui ou non
un élément d’un ensemble donné.

5.3.2 Déclaration des ensembles

Un type ensemble peut être défini de la manière suivante :

type
identifiacteur = ensemble de télément ;

où télément est le type des éléments de l’ensemble.

La notation [ ] dénote l’ensemble vide ou ensemble nul, c’est-à-dire l’ensemble qui ne


contient aucun élément. Chaque type d’ensembles contient l’ensemble vide comme valeur
possible quelque soit le type de ses éléments. L’ensemble qui contient toutes les valeurs
possibles du type de base est appelé l’ensemble universel.

Considérons les définitions de types et les déclarations de variables suivantes :

type
tAfrique = (Cameroun, Gabon, Guinée, Tchad, Centrafrique, Congo, Nigéria, Ghana) ;
tCommunauté = ensemble de tAfrique ;
variable
AfriqueCentrale : tCommunauté ;

Les valeurs possibles de la variable Communauté sont les différents sous-ensembles de Pays.
Par exemple, un valeur possible de AfriqueCentrale est [Cameroun, Gabon, Guinée].

5.3.3 Les constructeurs ensemblistes

Avant qu’un ensemble ne soit manipulé, il doit recevoir une valeur. La forme générale d’une
instruction d’affectation ensembliste est :

identificateur  expression-ensembliste ;

L’expression ensembliste peut être une constante de type approprié, un autre identificateur
d’ensemble, ou elle peut spécifier une ou plusieurs opérations ensemblistes. La forme la plus
simple d’une instruction d’affectation consiste en un identificateur à gauche de l’opérateur
d’affectation et une constante ensembliste à droite. Les constantes ensemblistes sont créées
par le moyen des constructeurs ensemblistes. Des exemples de constructeurs ensemblistes
sont :

[Cameroun]
[Gabon, Nigéria, Guinée]
[Cameroun..Centrafrique]
[Cameroun..Guinéé, Congo..Ghana]

Dr Ndi Nyoungui André


128 Algorithmique et structures de données

5.3.4 Les opérateurs ensemblistes

Une des raisons de l’utilité des ensembles en algorithmique est le fait que les opérations de
réunion, d’intersection et de différence ensembliste sont disponibles comme des opérations
primitives. Ceci rend évidentes les opérations d’insertion et de suppression des éléments dans
un ensemble.

Opérateur Signification
+ Union
* Intersection
- Différence ensembliste

1) x + y est l’ensemble des valeurs qui appartiennent à x ou à y,


2) x * y est l’ensemble des valeurs qui appartiennent à x et à y,
3) x – y est l’ensemble des valeurs de x qui ne sont pas dans y.

Les priorités des opérateurs +, * et - , quand ils sont utilisés comme opérateurs ensemblistes,
sont les mêmes que lorsqu’ils sont utilisés comme opérateurs arithmétiques. Les parenthèses
peuvent être utilisées dans les expressions ensemblistes de la manière que dans les
expressions arithmétiques.

La réunion et la différence ensembliste sont particulièrement utiles pour ajouter ou supprimer


des éléments. La réunion est aussi utile pour la lecture des valeurs d’un ensemble. L’exemple
ci-dessus illustre la lecture d’un ensemble.

procédure lirepays(var liste : tAfriqueCentrale) ;


variable
pays : tAfrique ;
Ch : caractère ;
début
liste  [ ];
pour pays  Cameroun haut Ghana faire
début
lire(Ch) ;
si Ch = 'O' alors
liste  liste + [pays]
fin ;
fin ;

5.3.5 Les opérateurs relationnels

Le langage algorithmique offre cinq opérateurs relationnels qui peuvent être utilisés pour
vérifier la relation entre des ensembles, et l’appartenance d’un élément à un ensemble.

Opérateur Signification
= Égalité entre ensemble
 Inégalité entre ensembles
 Inclusion ensembliste
 Contenance ensembliste

Dr Ndi Nyoungui André


129 Algorithmique et structures de données

in Appartenance

L’opérateur dans est utilisé pour tester l’appartenance d’un élément à un ensemble. Le
premier opérande doit être un type scalaire, le type de base, et le deuxième opérande doit être
le type ensemble associé. Soit X un ensemble a une valeur appartenant au type de base de X,
alors l’expression « a in X » est vrai si a appartient à X et faux sinon.

L’opérateur in est habituellement utilisé pour simplifier l’écriture des structures de sélection
dans les algorithmes qui impliquent les ensembles. Par exemple,

si Ch in ['A', 'E', 'I', 'O', 'U'] alors


nbvoyelles  nbvoyelles + 1 ;

ou

si Ch in ['A', 'E', 'I', 'O', 'U'] alors


début
écrire(Ch) ;
lire(Ch) ;
fin ;

L’opérateur dans est aussi utilisé pour simplifier des expressions booléennes complexes. Par
exemple, supposons que l’on souhaite tester si une variable entière nombre a l’une des valeurs
suivantes : 1, 3, 4, 6 ou 9. Une manière de le faire est d’utiliser l’instruction si…alors, avec
une condition composée :

si (nombre = 1) ou (nombre = 3) ou (nombre = 4) ou (nombre = 6) ou (nombre = 9) alors


instruction ;

Une instruction si…alors plus simple qui donne le même résultat est :

si nombre in [1, 3, 4, 6, 9] alors


instruction ;

Une certaine attention doit être exercée lorsqu’on utilise l’opérateur in pour simplifier les
expressions booléennes. Une utilisation correcte de l’opérateur in nécessite que l’opérateur
scalaire à gauche de in soit une valeur qui appartient au type de base de l’ensemble à droite de
l’opérateur in.

L’opérateur booléen non peut être utilisé avec les opérateurs relationnels ensemblistes. Il faut
cependant se rappeler l’opérateur non doit toujours précéder une expression booléenne. Ainsi,
l’expression

non (5 in [1..20])

est valide, et sa valeur est faux. Dans cet exemple, l’expression booléenne qui est l’opérande
de non est 5 in [1..20].

Dr Ndi Nyoungui André


130 Algorithmique et structures de données

Les opérateurs relationnels ensemblistes ont tous le même niveau précédence, et ce niveau de
précédence est le même que celui des opérateurs arithmétiques. Donc, tous les opérateurs
relationnels ont une précédence inférieure à celle des opérateurs ensemblistes *, + et -.

Conclusion

Nous avons présenté les types de données les plus courants du langage algorithmique.
Certains d’entre eux sont implémentés par différents langages de programmation. D’autres
par contre ne sont implémentés que par des langages particuliers.

5.4 Études de cas

Exercice 5.6

Le service informatique d’un centre touristique conserve les informations sur les clients en
utilisant la structure suivante :

type
tdomaine = (Cinéma, Musique, Sport, Lecture) ;
tclient = article
nom : chaîne20 ;
sexe : (féminin, masculin) ;
domaine : ensemble de tdomaine ;
âge : entier ;
fin ;

1. Écrire une procédure qui lit les informations sur un nouveau client et crée un article
pour ce client.
2. Écrire une procédure qui imprime les informations d’un client.
3. Écrire une fonction booléenne qui vérifie que deux clients sont compatibles. Pour être
compatibles, les clients doivent être de sexe opposé, avoir un écart d’âge de six ans au
plus et avoir au moins deux intérêts en commun.
4. Écrire une procédure qui prend en entrée les informations sur un client et une liste de n
clients puis imprime la liste des clients qui lui sont compatibles.

Algorithme d’Eratosthene

On veut écrire une procédure pour imprimer tous les entiers premiers compris entre 1 et un
certain entier positif arbitraire, nombremax. Pour obtenir les nombres premiers, nous allons
utiliser un algorithme classique connu comme l’algorithme d’Eratosthènes. L’algorithme peut
être décrit de la manière suivante :
1. Commencer avec l’ensemble des entiers consécutifs compris entre 1 et nombremax.
2. Supprimer de cet ensemble tous les multiples de 2. Ceux-ci ne sont pas certainement
des nombres premiers.
3. Trouver le prochain entier restant dans l’ensemble supérieur à celui dont les multiples
viennent d’être supprimés et supprimer tous ses multiples. Ceux-ci ne peuvent pas être
des nombres premiers.
4. Répéter l’étape 3 jusqu’à ce que l’entier dont les multiples viennent d’être supprimés
soit supérieur ou égal à la racine carrée de nombremax. Cette condition de terminaison

Dr Ndi Nyoungui André


131 Algorithmique et structures de données

pour la suppression des entiers non premiers est basée sur le fait que les deux facteurs
dans un produit ne peuvent pas être tous les deux supérieurs à la racine carrée du
produit.
5. Les nombres qui restent dans l’ensemble après le processus de suppression sont les
entiers premiers compris entre 1 et nombremax.

Dr Ndi Nyoungui André


132 Algorithmique et structures de données

Chapitre 6

Les vecteurs

6.1 Le concept de vecteur

Considérons le problème suivant : on a des notes pour 35 étudiants et on voudrait obtenir la


moyenne de toutes ces notes. On voudrait aussi déterminer et afficher la différence entre
chacune des notes et la moyenne. On ne peut pas aborder ce problème de la même manière
que nous avons abordé le calcul de la moyenne d’un ensemble de nombres. Lorsque nous
voulions calculer uniquement la moyenne, nous étions capables de lire chaque nombre,
terminer son traitement en l’ajoutant au total courant, et lire le nombre suivant. Cependant,
lorsque le prochain nombre était lu, le dernier nombre lu était perdu car la même variable était
utilisée pour stocker la nouvelle valeur.

Ici, on n’a pas besoin seulement de calculer la moyenne d’un ensemble de notes, mais on doit
aussi calculer la différence entre chaque note et la moyenne. Pour cela, on doit soit lire toutes
les notes deux fois, une première fois pour le calcul de la moyenne et une deuxième fois pour
le calcul des différences à la moyenne, ou alors stocker toutes les notes de manière qu’elles
soient disponibles au moment où on a besoin de calculer les différences. En utilisant les
structures de données que nous avons étudiées jusque là, on aura besoin de noms de variables
séparées pour chaque note. Trente cinq variables, telles que note1, note2, …, note35 de type
approprié doivent être déclarées. Cela est bien possible mais l’algorithme résultant sera lourd.
Il impliquera trente cinq instructions de lecture séparées pour les trente cinq notes, le calcul de
la moyenne et ensuite le calcul de la différence entre chaque note et la moyenne. On aura
besoin de 35 instructions d’affectation pour calculer la différence entre chaque note et la
moyenne comme l’illustre le segment d’algorithme ci-dessous :
……
Différence  note1 – moyenne ;
Écrire(Différence) ;
Différence  note2 – moyenne ;
Écrire(Différence) ;
.
.
.
Différence  note35 – moyenne ;
Écrire(Différence) ;
…...

Cette solution sera bien entendu impraticable si on doit effectuer les mêmes calculs pour 100
ou 1000 notes.

Le fait que les variables note1, note2, …, note35 soient distinctes nous force à utiliser des
instructions d’affectation individuelles pour calculer chaque différence bien que les calculs
effectués pour chaque variable soient exactement les mêmes. Ainsi, la possibilité d’utiliser la

Dr Ndi Nyoungui André


133 Algorithmique et structures de données

répétition dans ce qui apparaît être une situation répétitive est empêchée par le fait que les
variables impliquées ne sont pas liées les unes aux autres.

Une meilleure approche consiste à considérer toutes ces variables comme étant les
composantes d’une variable unique appelée un vecteur et utiliser un indice pour distinguer les
composantes individuelles. Ceci est illustré par la figure ci-dessous où on a une seule variable
vecteur appelée note ayant trente cinq composantes ou éléments.

Figure 6.1. Représentation d’un vecteur

note[1] note[2] note[3] … note[35]

78 95 100 … 65

note

La variable note est associée à 35 emplacements mémoire consécutifs, un emplacement pour


chaque composante ou élément du vecteur. On peut sélectionner une composante donnée en
utilisant un indice entre crochets. Ainsi, note[1] désigne le premier élément du vecteur, à
savoir 78. De même, note[3] désigne le troisième élément de note ou 100. Les nombres 78,
95, 100, …, 65 sont les valeurs des éléments du vecteur. Les entiers entre crochets sont les
indices. La variable note dont la valeur est le vecteur entier, est appelée une variable vecteur
ou tout simplement un vecteur. Les variables note[1], … note[35], dont les valeurs sont les
composantes individuelles du vecteur sont appelées des variables indicées. On peut utiliser
les variables indicées de la manière que les autres variables que nous avons étudiées jusque
là : c’est-à-dire, elles peuvent être utilisées dans des instructions d’affectation, des instructions
de lecture, dans des instructions d’écriture et ainsi de suite.

6.1.1 Définitions formelles

Soient :
 un ensemble quelconque de valeurs E,
 un entier positif n (n  0),
 un intervalle I de N tel que I = [1..n].

Un vecteur est une application V de I dans E qui à chaque élément i de I fait correspondre un
élément V[i] de E ; I est appelé l’ensemble des indices du vecteur.

5.1.2 Déclaration des variables vecteurs

Pour déclarer un vecteur note de 35 éléments pouvant contenir chacun un entier, on utilise la
déclaration suivante :

variable
note : vecteur[1..35] de entier ;

Dr Ndi Nyoungui André


134 Algorithmique et structures de données

Cette déclaration mentionne deux types de données : le type de données des composantes du
vecteur, entier dans notre exemple, et le type de données des indices, le type intervalle 1..35
dans notre exemple.

La forme générale d’une déclaration de vecteur est :

variable
liste : vecteur[typeindice] de télément ;

où télément est le type des éléments du vecteur et typeindice le type des indices du vecteur.

Type des éléments : Le type des éléments décrit chaque élément du vecteur. Çà peut être
n’importe lequel des types scalaires que nous avons étudiés jusque là (entier, réel, caractère,
booléen) ou même un type structuré comme un article ou un vecteur. On notera que tous les
éléments d’un vecteur sont de même type.

Le type des indices : Le type de l’indice peut être soit l’un des types standards booléen ou
caractère, un type scalaire énuméré défini par l’utilisateur, ou un type intervalle. Aucun des
types scalaires entier ou réel ne peut être utilisé comme ensemble des indices d’un vecteur car
ceci créera un vecteur ayant un nombre infini de composantes. Toutefois, un intervalle type
entier peut être utilisé.

Pour des raisons de simplicité, l’ensemble des indices sera toujours noté [1..n] si on a un
vecteur de n éléments. Il peut être vide (si n = 0, [1..n] est vide). Le vecteur est alors dit vide.
L’indice le plus petit (ici 1) est appelé la borne inférieure, le plus grand (ici n) est appelé la
borne supérieure.

Taille du vecteur : La taille d’un vecteur est le nombre d’éléments de ce vecteur ou le


nombre d’éléments de l’ensemble des indices. Si l’ensemble des indices est [1..n], la taille du
vecteur est n. Par contre si l’ensemble des indices est [0..n], la taille du vecteur est n+1.

Notations : Un vecteur liste pourra être noté liste[1..n] et même simplement liste lorsqu’il n’y
a pas d’ambiguïté.

Un élément d’un vecteur liste[1..n] est noté liste[i] avec i  [1..n].

Un vecteur liste peut être noté par l’ensemble de ses éléments :

Figure 6.2. Représentation d’un vecteur en mémoire

1 i n
liste[1] … liste[i] … liste[n]

liste

Dr Ndi Nyoungui André


135 Algorithmique et structures de données

Indiçage : Les indices sont utilisés pour distinguer les composantes individuelles d’un
vecteur. On référence une composante particulière d’un vecteur en donnant le nom du vecteur,
suivi par un indice entre des crochets carrés. L’indice peut être une constante, une variable ou
même une expression. La seule restriction est que le type de l’expression doit correspondre au
type de l’indice spécifié dans la déclaration du vecteur.

Soit un vecteur liste[1..n] :


 pour tout indice i  [1..n], l’opération d’indiçage notée liste[i] délivre la valeur de la
composante d’indice i du vecteur ; cette valeur peut être affectée à une variable de
même type que celui des éléments du vecteur, par exemple A  liste[i]. De même on
peut ranger une valeur A dans le ième élément par l’affectation liste[i]  A.
 le résultat de liste[i] est indéfini si i  [1..n].

6.1.3 Définition d’un type vecteur

Comme avec les autres types de données, il est possible de définir un type de données vecteur
en utilisant une définition de type de la forme :

constante
taillemax = 100 ;
type
vélément = vecteur[1..taillemax] de télément ;

où télément est un identificateur de type simple (entier, réel, caractère, booléen) ou structuré
(article, vecteur). La constante taillemax représente le nombre maximum d’éléments possibles
que le vecteur peut contenir. En effet, il faut prévoir le nombre maximum d’éléments du
vecteur afin de réserver la place mémoire nécessaire.

Avec cette définition de type, on peut déclarer n’importe quel vecteur liste[1..n] tel que la
borne supérieure n du vecteur vérifie la relation 0 < n  taillemax. Par exemple,

variable
liste : vélément ;

Avec cette déclaration, le vecteur liste peut contenir jusqu’à 100 valeurs de type ELEMENT.
Cent emplacements mémoire sont donc réservés pour contenir les éléments du vecteur liste.
On dit qu’on a une gestion statique de la mémoire. Mais il peut arriver dans la pratique que
l’on utilise que n emplacements seulement, avec 0  n < taillemax. Ce qui peut résulter en un
gâchis de la mémoire si on utilise effectivement très peu de place par rapport à la place
réservée lors de la définition du vecteur.

Figure 6.3. Allocation statique de la mémoire

1 n taillemax
… …

Sous-vecteur

Dr Ndi Nyoungui André


136 Algorithmique et structures de données

On appelle sous-vecteur du vecteur liste[1..n], toute restriction de liste à un sous-intervalle


[i..j] de [1..n]. Un sous-vecteur est noté comme un vecteur. liste[i..j] est le sous-vecteur de
liste[1..n] formé des éléments de liste dont les indices sont compris entre i et j.

Relation d’ordre

On est parfois amené à définir une relation d’ordre sur l’ensemble E des valeurs d’un vecteur.
Cette relation est noté, pour des raisons de simplification d’écriture, par le symbole d’inégalité
stricte (<) quel que soit l’ensemble E. Il est évident que dans chaque cas particulier, on est
conduit à définir le prédicat associé à cette relation d’ordre.

6.2 Algorithmes traitant un seul vecteur

Avec un seul vecteur, nous traitons soit les problèmes où l’on est amené à parcourir
l’ensemble du vecteur, soit au contraire ceux où l’on devra s’arrêter dès qu’une certaine
condition est vérifiée : il s’agira alors plus spécialement des problèmes d’accès à une valeur
donnée dans un vecteur.

6.2.1 Problème du parcours d’un vecteur

Soit un vecteur liste de n éléments. L’approche la plus simple consiste à parcourir le vecteur
dans l’ordre croissant des indices. On obtient alors un schéma d’énumération séquentiel de la
gauche vers la droite des éléments du vecteur :

procédure parcoursgd(liste : vélément ; n : entier) ;


variable
i : entier ;
début
i  1 ;
tantque i  n faire
début
traiter(liste[i]) ;
i  i + 1 ;
fin ;
fin;

où traiter() est une opération quelconque sur l’élément liste[i].

ou encore en utilisant l’instruction pour croissante :

procédure parcoursgd(liste : vélément ; n : entier) ;


variable
i : entier ;
début
pour i  1 haut n faire
traiter(liste[i]) ;
fin ;

Dr Ndi Nyoungui André


137 Algorithmique et structures de données

On peut également définir un parcours séquentiel dans l’ordre décroissant des indices. On
obtient alors un schéma d’énumération séquentiel de la droite vers la gauche des éléments du
vecteur :

procédure parcoursdg(liste : vélément ; n : entier) ;


variable
i : entier ;
début
i  n ;
tantque i  1 faire
début
traiter(liste[i]) ;
i  i - 1 ;
fin ;
fin;

ou encore en utilisant l’instruction pour décroissante :

procédure parcoursdg(liste : vélément ; n : entier) ;


variable
i : entier ;
début
pour i  n bas 1 faire
traiter(liste[i]) ;
fin ;

On peut aussi écrire le parcours d’un vecteur liste[i..n] sous forme récursive.

procédure parcoursrecgd(liste : vélément ; i, n : entier) ;


début
si i  n alors
début
traiter(liste[i]) ;
parcoursrecgd(liste, i+1, n) ;
fin ;
fin ;

Cet algorithme est équivalent à :

procédure parcoursgd(liste : vélément ; n : entier) ;


variable
i : entier ;
début
i  1 ;
tantque (i  n) faire
début
traiter(liste[i]) ;
i  i + 1 ;
fin ;

Dr Ndi Nyoungui André


138 Algorithmique et structures de données

fin;

ou encore à :

procédure parcoursgd(liste : vélément ; n : entier) ;


variable
i : entier ;
début
pour i  1 haut n faire
traiter(liste[i]) ;
fin ;

L’emploi de méthodes récursives pour traiter les parcours simples de vecteurs est souvent
inutilement lourd et lent à l’exécution. C’est pourquoi les algorithmes sur les vecteurs sont le
plus souvent traités de manière itérative, en réservant la récursivité aux cas où elle s’impose.

Exemples d’application

Exemple 6.1
Écrire une procédure pour lire un vecteur de n éléments.

procédure lirevecteur(var liste : vélément ; var n : entier) ;


variable
i : entier ;
début
écrire(‘Quelle est la valeur de n ?’) ;
lire(n) ;
i  1 ;
tantque (i  n) faire
début
lire(liste[i]) ;
i  i + 1 ;
fin ;
fin;

Exemple 6.2
Écrire une procédure qui affiche les éléments d’un vecteur dans l’ordre des indices croissants.

Première version, utilisation de la boucle tantque

procédure afficherliste(liste : vélément ; n : entier) ;


variable
i : entier ;
début
i  1 ;
tantque i  n faire
début
écrire(liste[i]) ;
i  i + 1 ;

Dr Ndi Nyoungui André


139 Algorithmique et structures de données

fin ;
fin;

Deuxième version, utilisation de la boucle pour croissante

procédure afficherliste(liste : vélément ; n : entier) ;


variable
i : entier ;
début
pour i  1 haut n faire
écrire(liste[i]) ;
fin ;

Exemple 6.3
Écrire une fonction qui calcule la somme des éléments d’un vecteur de n nombres réels. On
admet que la somme des éléments est égale à zéro si le vecteur est vide.

Version itérative

On applique un parcours de la gauche vers la droite en remplaçant l’instruction traiter(liste[i])


par l’instruction total  total + liste[i] ; la variable total étant initialisée à zéro.

L’algorithme est alors le suivant :


fonction sommeliste(liste : vélément ; n : entier) : réel ;
variable
i : entier ;
total : réal ;
début
total  0 ;
pour i  1 haut n faire
total  total + liste[i] ;

sommeliste  total ;
fin ;

Version récursive

Le raisonnement est le suivant : à l’appel de la fonction, la valeur du paramètre n est


comparée à 0. Si elle est égale à 0, alors on sait que le vecteur est vide et sommeliste peut
alors recevoir la valeur zéro. Dans le cas contraire, on regarde si la valeur de n est égale à 1.
Si elle est en effet égale à 1, alors on sait que l’on a une seule valeur à sommer, on peut donc
affecter la valeur de cet unique élément à sommeliste puis sortir de la fonction. Si par contre n
est supérieur à 1, on invoque encore la fonction, mais cette fois avec n – 1 comme deuxième
paramètre. La somme des éléments du vecteur est alors fixée à la somme des n –1 premiers
éléments plus la valeur de liste[n].

L’algorithme est alors le suivant :


fonction sommeliste(liste : vélément ; n : entier) : réel ;
début

Dr Ndi Nyoungui André


140 Algorithmique et structures de données

si n = 0 alors
sommeliste  0
sinon
si n = 1 alors
sommeliste  liste[n]
sinon
sommeliste  liste[n] + sommeliste(liste, n - 1) ;
fin ;

Exemple 6.4
Écrire une fonction qui calcule la moyenne des éléments d’un vecteur de n nombres réels.

fonction moyenneliste(liste : vélément ; n : entier) : réel ;


variable
i : entier ;
total : réal ;
début
si n = 0 alors
moyenneliste  0
sinon
début
total  0 ;
pour i  1 haut n faire
total  total + liste[i] ;

moyenneliste  total/n ;
fin ;
fin ;

Exemple 6.5
Écrire une fonction qui calcule le maximum des éléments d’un vecteur de n nombres réels.

fonction maximum(liste : vélément ; n : entier) : réel ;


variable
i : entier ;
max : réel ;
début
max  liste[1] ;
pour i  2 haut n faire
si max < liste[i] alors
max  liste[i] ;

maximum  max ;
fin ;

Exemple 6.6
Écrire une fonction qui calcule la position du plus grand élément d’un vecteur de n nombres
réels. On suppose que la plus grande valeur est unique.

Dr Ndi Nyoungui André


141 Algorithmique et structures de données

fonction indicemax(liste : vélément ; n : entier) : entier ;


variable
i, indice : entier ;
max : réel ;
début
max  liste[1] ;
indice  1 ;
pour i  2 haut n faire
si max < liste[i] alors
début
indice  i ;
max  liste[i] ;
fin;
indicemax  indice ;
fin ;

Exemple 6.7
Écrire une procédure qui calcule le plus grand élément d’un vecteur de n nombres réels ainsi
que sa position dans le vecteur. On suppose que la plus grande valeur est unique.

procédure maxpos(liste : vélément ; n : entier ; var grand : réel ; var k : entier) ;
variable
i : entier ;
début
grand  liste[1] ;
k  1 ;
pour i  2 haut n faire
si grand < liste[i] alors
début
grand  liste[i] ;
k  i ;
fin ;
fin ;

Exercices d’apprentissage

Exercice 6.1
Écrire une procédure qui affiche les éléments d’un vecteur dans l’ordre décroissant des
indices.

Exercice 6.2
Écrire la même procédure sous forme récursive.

Exercice 6.3
Écrire une fonction qui calcule le nombre d’occurrences d’une valeur val dans un vecteur de n
éléments.

Exercice 6.4

Dr Ndi Nyoungui André


142 Algorithmique et structures de données

On considère un vecteur de n entiers. Écrire une procédure qui imprime uniquement les
éléments de rang impaire.

Exercice 6.5
Écrire une procédure qui reçoit en entrée un vecteur de n entiers et remplace chaque élément
du vecteur par la somme des éléments qui le précèdent y compris lui-même.

Exercice 6.6
On à demandé à n étudiants d’évaluer la qualité de la nourriture offerte à la cafétéria sur une
échelle de 1 à 10 (1 signifiant immangeable et 10, excellent). Écrire un algorithme pour lire
les n réponses dans un vecteur d’entiers et résumer le résultat du sondage.

Exercice 6.7
Une des techniques utilisées par les cryptographes pour déchiffrer les codes est d’utiliser la
fréquence avec laquelle les différents caractères apparaissent dans un texte encodé. Écrire une
procédure qui lit une chaîne de caractères se terminant par un point et calcule la fréquence
d’apparition de chaque caractère dans le texte. On suppose que seules les lettres de l’alphabet
anglais sont concernées. La ponctuation est ignorée et aucune différence n’est faite entre les
lettres minuscules et majuscules.

Exercice 6.8
Écrire une procédure qui reçoit en entrée un vecteur de n nombres réels et détermine le plus
grand et le second plus grand de ces nombres.

Exercice 6.9
On suppose que l’on a les définition suivantes :

type
tdate = article
jour, mois, année : entier
fin ;
toption = (GBIO, MIP, GTE, GIN) ;
tétudiant = article
nom, chaîne ;
prénom : chaîne ;
sexe : (féminin, masculin) ;
matricule : chaîne ;
datenais : tdate ;
lieunais : chaîne ;
option : toption ;
fin ;
vétudiant = vecteur[1..100] de tétudiant ;

1. Écrire une algorithme pour lire un vecteur de n étudiants.


2. Écrire une algorithme pour écrire un vecteur de n éléments.
3. Écrire un algorithme qui délivre le nombre de filles dans un vecteur de n étudiants.
4. Écrire un algorithme qui délivre le nombre de garçons et le nombres de filles dans un
vecteur de n étudiants.
5. Écrire une fonction qui délivre le nombre d’étudiants inscrits dans une option donnée.

Dr Ndi Nyoungui André


143 Algorithmique et structures de données

6.2.2 Algorithmes d’accès à un élément dans un vecteur

Une des applications les plus courantes implique la recherche dans une liste pour déterminer
si un élément particulier est présent dans la liste. Il y a plusieurs raisons pour lesquelles on a
souvent besoin de faire la recherche dans les listes. On peut simplement vouloir déterminer si
une valeur particulière apparaît comme une composante (recherche associative), on peut
vouloir déterminer là où elle apparaît (recherche par position) ou on peut vouloir localiser
une composante pour la modifier ou la remplacer. On a aussi très souvent besoin de faire la
recherche dans un vecteur pour retrouver la composante correspondante dans un autre vecteur.
L’accès par position est défini par la primitive d’indiçage liste[i]. L’accès associatif est le plus
courant. Soit elem une variable contenant une valeur du même type que celui des éléments de
la liste, on veut déterminer s’il existe un indice i  [1..n] tel que élément = liste[i].

La recherche dans les listes peut se faire de différentes manières. Ici, nous présentons le sujet
en examinant les deux algorithmes les plus courants, la recherche linaire ou recherche
séquentielle et la recherche dichotomique ou recherche binaire.

6.2.2.1 Recherche linéaire ou séquentielle dans un vecteur non trié

Dans une recherche linéaire ou séquentielle, on cherche une valeur particulière dans une liste
en examinant chaque élément dans l’ordre, en commençant par le début de la liste. Quand on
trouve l’élément que l’on cherche, on arrête. Bien entendu, il est possible que l’élément
recherché ne soit pas présent dans la liste. On détermine cette situation en atteignant la fin de
la liste sans trouver la valeur recherchée.

Une première implémentation naïve de cette méthode conduit à l’algorithme suivant :

fonction rechséquentielle(liste : vélément ; n : entier; elem : télément) : booléen ;


variable
i : entier ;
début
i  1 ;
tantque (i  n) et (liste[i]  elem) faire
i  i + 1 ;

rechséquentielle  i  n ;
fin ;

Une analyse attentive de la boucle de cette fonction montre que si elem  liste[1..n], i atteint à
la dernière itération la valeur n + 1, et on est alors amené à effectuer un test sur liste[n + 1] qui
est une valeur indéfinie. On introduit donc l’opérateur booléen et alors qui est en fait une
évaluation optimisée du et classique. Il s’agit simplement de constater que si le premier terme
du et est faux, il est inutile d’examiner le second terme car le résultat de la conjonction sera
faux.

La table de vérité de l’opérateur et alors qui se déduit de celui du et est le suivant :

Tableau 6.1. Table de vérité de l’opérateur « et alors »

Dr Ndi Nyoungui André


144 Algorithmique et structures de données

A B A et alors B
vrai vrai vrai
vrai faux faux
faux non examiné faux

La valeur de l’opération C  A et alors B est équivalent à celle que donnerait l’instruction

si A alors
CB
sinon
C  faux ;

On définit de même l’opérateur ou sinon qui correspond à l’évaluation optimisée du ou. Si le


premier terme du ou sinon est vrai, il est inutile d’examiner le second car le résultat de la
disjonction sera vrai.

Tableau 6.2. Table de vérité de l’opérateur ou sinon

A B A ou sinon B
vrai non examiné vrai
faux vrai vrai
faux faux faux

La valeur de l’opération C  A ou sinon B est équivalent à celle que donnerait l’instruction

si A alors
C  vrai
sinon
C  B ;

Remarques

 La loi de De Morgan peut s’écrire pour ces opérateurs :


non (a et alors b) = (non a) ou sinon (non b)
non (a ou sinon b) = (non a) et alors (non b)

 De nombreux langages de programmation implémentent ces opérateurs soit


directement (and then et or else de ADA, && et || de C) soit sous forme d’option
d’évaluation accélérée (certains compilateurs de Pascal). S’ils n’existent pas, on est
amené à les simuler au moyen de variables booléennes.

Une expression correcte de l’algorithme ci-dessus est alors :

fonction rechséquentielle(liste : vélément ; n : entier; elem : télément) : booléen ;


variable
i : entier
début

Dr Ndi Nyoungui André


145 Algorithmique et structures de données

i  1 ;
tantque (i  n) et alors (liste[i]  elem) faire
i  i + 1 ;

rechséquentielle  i  n ;
fin ;

Pour éviter l’utilisation du connecteur « et alors », on introduit une variable booléenne
trouvé. L’algorithme devient alors :

fonction rechséquentielle(liste : vélément ; n : entier; elem : télément) : booléen ;


variable
i : entier
trouvé : booléen ;
début
i  1 ;
trouvé  faux ;
tantque (i  n) et (non trouvé) faire
si liste[i] = elem alors
trouvé  vrai
sinon
i  i + 1 ;

rechséquentielle  trouvé ;
fin ;

On peut aussi donner une version récursive de cet algorithme. Dans ce cas, il faut introduire
une variable i dans la liste des paramètres en tant que borne inférieure du sous-vecteur que
l’on étudie. On obtient alors l’algorithme suivant :

fonction rechrécursive(liste : vélément ; i, n : entier; elem : télément) : booléen ;


début
si i > n alors
rechrécursive  faux
sinon
si liste[i] = elem alors
rechrécursive  vrai
sinon
rechrécursive  rechrécursive(liste, i + 1, n, elem) ;
fin ;

On peut également donner une version récursive de cet algorithme pour effectuer un parcours
de la droite vers la gauche. Dans ce cas, on recherche élément dans liste[1..n] et on n’a plus
besoin de la variable auxiliaire i, puisque la borne inférieure du vecteur est toujours égale à 1.

L’algorithme est alors le suivant :


fonction rechrécursive(liste : vélément ; n : entier; elem : télément) : booléen ;
début
si n = 0 alors

Dr Ndi Nyoungui André


146 Algorithmique et structures de données

rechrécursive  faux
sinon
si liste[i] = elem alors
rechrécursive  vrai
sinon
rechrécursive  rechrécursive(liste, n - 1, elem) ;
fin ;

Avec le connecteur et alors, on pourrait éviter de vérifier dans le tantque si liste[i] est égal à
elem lorsque i = n, mais nous préférons présenter une version de l’algorithme qui évite
l’emploi du et alors même au prix d’un test supplémentaire lorsque liste[n] = elem.

L’algorithme est alors le suivant :


fonction rechséquentielle(liste : vélément ; n : entier ; elem : télément) : booléen ;
variable
i : entier
début
i  1 ;
tantque (i < n) et (liste[i]  elem) faire
i  i + 1 ;

rechséquentielle  liste[i]= elem ;


fin ;

Exercices d’apprentissage

Exercice 6.11
Écrire une fonction qui calcule le produit des éléments d’un vecteur de n nombres réels
(penser au cas où un élément serait nul)

Exercice 6.12
Écrire de plusieurs manières différentes une fonction qui délivre l’indice de la valeur val dans
un vecteur liste[1..n] si val appartient au vecteur et 0 si val n’appartient pas au vecteur.
Préciser dans chaque cas si c’est la première ou la dernière occurrence que l’on a trouvée.

6.2.2.2 Vecteur trié ou ordonné

Si tous les couples d’éléments consécutifs d’un vecteur vérifient la relation liste[i-1]  liste[i],
on dit que le vecteur est trié ou ordonné par ordre croissant. Si par contre tous les couples
d’éléments consécutifs vérifient la relation liste[i]  liste[i-1], on dira que le vecteur est trié ou
ordonné par ordre décroissant.

Dans toute la suite du cours, sauf avis contraire, on appellera vecteur trié un vecteur trié par
ordre croissant. On admettra que le vecteur vide et que le vecteur ne contenant qu’un seul
élément sont triés.

D’où la définition :

 un vecteur vide (n = 0) est ordonné,

Dr Ndi Nyoungui André


147 Algorithmique et structures de données

 un vecteur contenant un seul élément (n = 1) est ordonné,


 un vecteur liste[1..n] est ordonné si,  i  [2..n, ], liste[i-1]  liste[i].

On peut également donner une définition récursive :

 un vecteur vide (n = 0) est ordonné,


 un vecteur contenant un seul élément (n = 1) est ordonné,
 (liste[1..i-1] ordonné, liste[i-1]  liste[i])  liste[1..i] ordonné pour tout i  [2..n].

Cette définition récursive est à rapprocher des définitions récursives que nous connaissons en
mathématiques , telles que la définition de n!

Une application immédiate de cette définition est la fonction qui vérifie qu’un vecteur est trié.
La méthode consiste à comparer tous les couples de deux éléments consécutifs en s’arrêtant
dès qu’un couple ne vérifie pas la relation d’ordre liste[i-1]  liste[i]. Le vecteur est trié si on
termine la parcours avec i = n + 1.

L’algorithme est alors le suivant :


fonction vecteurtrié(liste : vélément ; n : entier) : booléen ;
variable
i : entier ;
début
i  2 ;
tantque (i  n) et alors (liste[i-1]  liste[i]) faire
i  i + 1 ;
vecteurtrié  i > n
fin;

On peut également réécrire cette fonction sans utiliser le « et alors ». L’algorithme devient
alors :

fonction vecteurtrié(liste : vélément ; n : entier) : booléen ;


variable
i : entier ;
stop : booléen ;
début
i  2 ;
stop  faux ;
tantque (i  n) et non stop faire
si (liste[i-1]  liste[i]) alors
ii+1
sinon
stop  vrai ;

vecteurtrié  i > n
fin;

Dr Ndi Nyoungui André


148 Algorithmique et structures de données

6.2.2.3 Accès à un élément dans un vecteur ordonné

La méthode consiste à parcourir le vecteur à la recherche d’un indice i  [1..n] tel que la
relation liste[1..i-1] < elem  liste[i...n] soit vérifiée.
Ensuite, il ne reste plus qu’à tester l’égalité elem = liste[i] pour savoir si elem est présent ou
non dans le vecteur trié.

L’algorithme est alors le suivant :


fonction accèstrié1(liste : vélément ; n : entier ; elem : télément): booléen ;
variable
i : entier ;
début
i  1 ;
tantque (i  n) et alors (liste[i] < elem) faire
i  i + 1 ;

accèstrié1  (i  n) et alors (liste[i] = elem) ;


fin ;

D’autres solutions sont possibles, comme nous l’avons vu précédemment afin d’éviter
l’emploi du « et alors ». L’algorithme devient alors :

fonction accèstrié2(liste : vélément ; n : entier ; elem : télément) : booléen ;


variable
i : entier ;
stop : booléen ;
début
i  1 ;
stop  faux ;
tantque (i  n) et (non stop) faire
si liste[i] < elem alors
stop  vrai
sinon
i  i + 1 ;

si i  n alors
accèstrié2  liste[i] = elem
sinon
accèstrié2  faux ;
fin ;

D’autres solutions sont possibles, comme nous l’avons vu précédemment pour les vecteurs
non triés.

 Si le vecteur n’est pas vide, on peut s’arrêter sur l’avant-dernier élément, afin d’éviter
l’emploi de l’opérateur « et alors » :

L’algorithme devient alors :

Dr Ndi Nyoungui André


149 Algorithmique et structures de données

fonction accèstrié3(liste : vélément ; n : entier ; elem : télément) : booléen ;


variable
i : entier ;
début
i  1 ;
tantque (i < n) et (liste[i] < elem) faire
i  i + 1 ;

accèstrié3  liste[i] = elem ;


fin ;

 Si le vecteur n’est pas vide, on peut aussi comparer l’élément que l’on cherche avec
celui du dernier élément de la liste. Dans ce cas, si valeur > liste[n], l’algorithme est
terminé, l’élément n’est pas présent dans la liste. Si valeur  liste[n], on peut effectuer
un parcours de gauche à droite avec une seule condition (liste[i] < valeur) au niveau
du tantque car on est certain que i sera borné par n (puisque liste[n]  valeur).

L’algorithme devient alors :


fonction accèstrié4(liste : vélément ; n : entier ; elem : télément) : booléen ;
variable
i : entier ;
début
si elem > liste[n] alors
accèstrié4  faux
sinon
début
i  1 ;
tantque (liste[i] < elem) faire
i  i + 1 ;

accèstrié4  liste[i] = elem ;


fin ;
fin ;

Exercices d’apprentissage

Exercice 6.13
Écrire une fonction entière qui retourne la place de la première occurrence de la valeur val
dans un vecteur trié liste[1..n] si val appartient au vecteur liste, et 0 si val n’appartient à liste.

Exercice 6.14
Même question pour la dernière occurrence.

6.2.2.4 Recherche dichotomique

La recherche dichotomique (on dit aussi recherche binaire ou recherche par bissection) est une
méthode classique qui permet de faire une recherche associative dans un vecteur ordonné. Le
problème est le suivant : on dispose d’un vecteur liste[1..n] trié par ordre croissant. On veut

Dr Ndi Nyoungui André


150 Algorithmique et structures de données

déterminer si on peut trouver dans ce vecteur un élément ayant une valeur donnée elem. La
recherche dichotomique s’exécute de la manière suivante :

On divise le vecteur liste[1..n] en trois sous-vecteurs liste[1..m-1], liste[m] et liste[m+1..n]


ordonnés où m est un indice tel que la taille des sous-vecteurs liste[1..m-1] et liste[m+1..n]
soit la même à un élément près. On compare alors la valeur recherchée à la valeur du milieu
liste[m]. Si les deux éléments sont égaux, alors on a trouvé et la recherche est terminée. Sinon
si elem est supérieur à la valeur du milieu alors on continue la recherche de la même manière
dans la moitié droite du vecteur, sinon on continue la recherche dans la moitié gauche, et ainsi
de suite. Cette recherche peut bien entendu se solder par un échec si le vecteur ne contient pas
un élément égal à elem.

L’algorithme est alors le suivant :


procédure dichotomie(liste : vélément; n : entier ; elem : télément) : booléen ;
variable
inf, sup, m : entier ;
trouvé : booléen ;
début
trouvé  faux ;
inf  1 ;
sup  n ;
tantque (inf  sup) et non trouvé faire
début
m  (inf + sup) div 2 ;
si liste[m] = elem alors
trouvé  vrai
sinon
si liste[m] < elem alors
inf  m + 1
sinon
sup  m – 1
fin ;
dichotomie trouvé ;
fin ;

On peut notablement accélérer l’exécution de cet algorithme en ajoutant un test initial pour
savoir si elem est bien compris entre les deux éléments extrêmes du vecteur.

L’algorithme est alors le suivant :


procédure dichotomie(liste : vélément ; n : entier ; elem : télément) : booléen ;
variable
inf, sup, m : entier ;
trouvé : booléen ;
début
trouvé  faux ;
si (élément  liste[1] et (elem  liste[n]) alors
début
inf  1 ;
sup  n ;

Dr Ndi Nyoungui André


151 Algorithmique et structures de données

tantque (inf  sup) et (non trouvé) faire


début
m  (inf + sup) div 2 ;
si liste[m] = elem alors
trouvé  vrai
sinon
si liste[m] < elem alors
inf  m + 1
sinon
sup  m – 1
fin ;
fin ;
dichotomie trouvé ;
fin ;

Sous forme récursive, on cherche elem dans liste[inf..sup]. L’algorithme devient alors :

procédure dichotomie(liste : vélément ; inf, sup : entier ; elem : télément) : booléen ;


variable
m : entier ;
début
si inf > sup alors
dichotomie  faux
sinon
début
m  (inf + sup) div 2 ;
si liste[m] = elem alors
dichotomie  vrai
sinon
si liste[m] < elem alors
dichotomie  dichotomie(liste, m + 1, sup, elem)
sinon
dichotomie  dichotomie(liste, inf, m - 1, elem)
fin ;
fin ;

Exercices d’apprentissage

Exercice 6.15
Modifier les fonctions dichotomie ci-dessus afin qu’elles retournent la place de élément dans
le vecteur V si élément est présent dans le vecteur et 0 sinon (on supposera que le vecteur est
trié sans répétition)

Autre version

Il existe de nombreuses versions de l’algorithme de recherche dichotomique. Nous proposons


ici une version qui consiste à ne pas tester l’égalité (liste[m] = elem) mais à sortir de
l’itération avec l’assertion liste[1..sup-1] < elem  liste[sup..n]. Il ne reste donc plus qu’à
tester l’égalité liste[sup] = elem pour savoir si l’élément est présent ou non. Au lieu d’écrire

Dr Ndi Nyoungui André


152 Algorithmique et structures de données

une fonction booléenne, nous écrivons une fonction qui délivre la place de elem dans le
vecteur. On admettra que cette place est égale à zéro dans le cas où elem n’est pas présent
dans la liste.

Cette version présente l’avantage que si le vecteur comporte plusieurs occurrences de


élément, elle délivre la place de la première occurrence alors que l’algorithme précédent ne
peut déterminer de quelle occurrence il s’agit.

L’algorithme est alors le suivant :


fonction dichotomie(liste : vélément ; n : entier ; elem : télément) : entier ;
variable
m, inf, sup : entier ;
début
si elem > liste[n] alors
dichotomie  0
sinon
début
inf  1 ;
sup  n ;
tantque inf < sup faire
début
m  (inf +sup) div 2 ;
si elem  liste[m] alors
sup  m ;
sinon
inf  m + 1 ;
fin ;
si elem = liste[sup] alors
dichotomie  sup
sinon
dichotomie  0 ;
fin ;
fin ;

6.3 Algorithmes de mise à jour d’un vecteur

Ces algorithmes mettent en œuvre les techniques décrites précédemment : accès par position,
accès associatif par une méthode séquentielle ou dichotomique si le vecteur est trié. Nous
nous limiterons à des algorithmes d’insertion ou de suppression d’un seul élément dans un
vecteur.

6.3.1 Insertion d’un élément dans un vecteur non trié

S’il n’y a aucun critère d’insertion, il suffit d’ajouter le nouvel élément à la fin du vecteur et
de ne pas oublier de mettre à jour la taille du vecteur.

L’algorithme est alors le suivant :


procédure insertfin (var liste : vélément; var n : entier ; valeur : télément) ;
début

Dr Ndi Nyoungui André


153 Algorithmique et structures de données

n  n + 1 ;
liste[n]  valeur ;
fin ;

Si au contraire, on veut ajouter un élément à la kème place, ou après (ou avant) l’occurrence
d’une valeur, alors les algorithmes sont du même type que celui de l’insertion d’une valeur
dans un vecteur trié, que nous présentons ci-dessous.

6.3.2 Insertion d’un élément dans un vecteur trié

On considère le problème qui consiste à insérer un nouvel élément dans un vecteur trié de n
éléments de manière à obtenir un vecteur trié de n+1 éléments contenant tous les éléments du
vecteur initial plus l’élément à insérer.

Nous proposons une méthode qui consiste à :

 chercher la place de l’élément à insérer,


 effectuer l’insertion de cet élément.

On va donc définir :

 une fonction entière qui prend en entrée un vecteur trié liste de n éléments, une valeur
elem de type télément et retourne la place de elem dans la liste.

fonction position(liste : vélément ; n : entier ; elem : télément) : entier

 une procédure qui prend en entrée un vecteur trié liste de n éléments, un entier p, une
valeur de type ELEMENT et insère cette valeur à la position p de manière à obtenir un
vecteur trié de n + 1 éléments.

procédure insertplace(var liste : vélément ; var n : entier ; p : entier ; elem : télément) ;

L’algorithme d’insertion sera donc le suivant :


procédure insertion(var liste : vélément ; var n : entier ; elem : télément) ;
variable
p : entier ;
début
si n = 0 alors
début
n  1 ;
liste[n]  elem ;
fin
sinon
début
p  position(liste, n, elem) ;
insertplace(liste, n, p, elem);
fin;
fin;

Dr Ndi Nyoungui André


154 Algorithmique et structures de données

Recherche de la place de l’élément à insérer

On cherche un indice p  [1..n+1] tel que la relation suivante soit vérifiée :

liste[p-1]  elem < liste[p]

En effet, on cherche une place le plus à droite possible afin d’avoir à effectuer le moins
possible de décalages pour l’insertion.

Cette recherche peut être effectuée par une méthode séquentielle ou par une méthode
dichotomique.

Méthode séquentielle

On commence par regarder si p = 1 ; sinon il reste à chercher p dans l’intervalle [2..n+1]. Pour
cela on effectue un parcours de droite à gauche.

L’algorithme est alors le suivant :


fonction position(liste : vélément ; n : entier ; elem : télément) : entier ;
variable
i : entier ;
début
si liste [1] > elem alors
position  1
sinon
début
i  n ;
tantque liste[i] > elem faire
i  i - 1 ;

position  i + 1 ;
fin ;
fin ;

Méthode dichotomique

On traite d’abord le cas p = n + 1. Ensuite il reste à chercher p  [1..n] tel que


liste[1..p-1]  elem < liste[p..n].

L’algorithme est alors le suivant:


fonction position(liste : vélément ; n : entier ; elem : télément) : entier ;
variable
inf, sup, m : entier ;
début
si liste [n]  elem alors
position  n + 1
sinon
début
inf 1 ;

Dr Ndi Nyoungui André


155 Algorithmique et structures de données

sup n ;
tantque inf < sup faire
début
m  (inf + sup) div 2 ;
si liste [m]  elem alors
inf  m + 1
sinon
sup  m ;
fin ;
position  sup ;
fin ;
fin ;

Insertion de l’élément à sa place

Connaissant la place p de l’élément à insérer, il suffit de créer un élément supplémentaire à la


fin du vecteur, de décaler d’une position vers la droite tous les éléments de liste[p] à liste[n] et
d’affecter ensuite elem à liste[p].

L’algorithme est alors le suivant :


procédure insertplace (var liste : vélément ; var n : entier ; p : entier ; elem : télément) ;
variable
i : entier ;
début
n  n+1 ;
i  n ;
tantque i > p faire
liste[i]  liste[i - 1] ;

liste[p]  elem ;
fin ;

6.3.3 Suppression d’un élément dans un vecteur trié

Le problème est analogue à celui de l’insertion d’un nouvel élement. Toutefois, la valeur à
supprimer n’appartient pas forcément au vecteur liste, la suppression est donc impossible dans
ce cas. On définira alors un paramètre booléen permettant de savoir si la suppression a été
possible ou non. Nous nous intéresserons à la suppression d’un élément dans un vecteur trié.

L’algorithme peut se décomposer en deux étapes :

 rechercher une occurrence de la valeur elem à supprimer,


 si on a trouvé une occurrence, retasser les éléments du vecteur afin d’obtenir un
vecteur qui ne contienne plus que n-1 éléments.

Pour la recherche d’une occurrence, on adoptera la convention suivante : on cherche l’indice p


le plus grand possible (afin de minimiser le nombre de décalages) tel que p  [1..n] et liste[p]
= elem. Si la valeur elem est inexistante dans le vecteur, on donne par convention à la variable
p la valeur zéro.

Dr Ndi Nyoungui André


156 Algorithmique et structures de données

L’algorithme de suppression est alors le suivant :


procédure suppression(var liste : vélément ; n : entier ; elem : télément ;
var possible : booléen) ;
variable
p : entier ;
début
possible  faux ;
p  positionsup(liste, n, elem) ;
si p > 0 alors
début
tasser(liste, n, p) ;
possible  vrai ;
fin ;
fin ;

Recherche de la place de l’élément

On cherche un indice p tel que : d’une part p [0..n] et d’autre part elem  liste[1..n], p = 0
ou bien liste[p] = elem, elem < liste[p+1..n], p [1..n].

Méthode séquentielle

fonction positionsup(liste : vélément ; n : entier ; elem : télément) : entier ;


variable
i, p : entier ;
début
p  0;
si liste[1]  elem alors
début
i  n ;
tantque liste[i] > elem faire
i  i – 1 ;
si liste[i] = elem alors
p  i ;
fin ;
positionsup  p ;
fin ;

Méthode dichotomique

En utilisant les mêmes principes que précédemment, on obtient :

fonction positionsup(liste : vélément ; n : entier ; elem : télément) : entier ;


variable
p, inf, sup, m : entier ;
début
p  0;
si liste[n] = elem alors

Dr Ndi Nyoungui André


157 Algorithmique et structures de données

p  n ;
sinon
si liste[n] > elem alors
début
inf  1 ;
sup  n;
tantque inf < sup faire
début
m  (inf + sup) div 2 ;
si liste[m]  elem alors
inf  m + 1
sinon
sup  m ;
fin ;

si (sup > 1) et alors (liste[sup-1] = elem) alors


p  sup – 1 ;
fin ;
positionsup  p ;
fin ;

Écriture de la procédure tasser

Pour supprimer l’élément d’indice p, il suffit de décaler d’une position vers la gauche tous les
éléments ayant un indice supérieur à p. A la fin du décalage, on diminue la taille du vecteur de
un. On obtient alors l’algorithme :

procédure tasser(var liste : vélément ; var n : entier ; p : entier) ;


variable
i : entier ;
début
pour i  p + 1 haut n faire
liste[i-1]  liste[i] ;

n  n – 1;
fin;

6.3.4 Élimination de valeurs non significatives

Au lieu de supprimer directement un élément dans un vecteur, on peut remplacer la valeur que
l’on veut supprimer par une valeur non significative appelée bidon. Au bout d’un certain
nombre de suppressions, le vecteur contient un certain nombre de valeurs non significatives
que l’on supprimera par un retassement unique.
Dans la pratique, on parlera de suppression logique : marquage des éléments par un booléen
mis à faux par exemple, suivie da la suppression physique des éléments marqués : retassement
avec suppression de tous les éléments marqués.

L’algorithme se divise en deux étapes :


 Recherche du premier élément non significatif, au moyen de la fonction premierbidon,

Dr Ndi Nyoungui André


158 Algorithmique et structures de données

 Retassement en un seul passage tous les éléments non significatifs.

fonction premierbidon(liste : vélément ; n : entier ; bidon : télément) : entier ;


variable
i : entier ;
début
i  1 ;
tantque liste[i]  bidon faire
i  i + 1 ;
premierbidon  i ;
fin ;

procédure supprimebidon(var liste: vélément; var n: entier; bidon: télément);


variable
i, j : entier ;
début
i  premierbidon(liste, n, bidon);
j  i + 1 ;
tantque j  n faire
début
si liste[j]  bidon alors
début
liste[i]  liste[j]
i  i + 1 ;
fin ;
j  j + 1 ;
fin ;
n  i –1 ;
fin ;

On peut remarquer que l’algorithme ci-dessus respecte l’ordre des éléments significatifs du
vecteur d’origine. Si cet ordre n’est pas d’importance, nous pouvons donner une autre version
un peu plus performant.

procédure supprimebidon(var liste: vélément; var n: entier; bidon: télément);


variable
i, j : entier ;
début
i  1;
j  n;
tantque i  j faire
si liste[i] = bidon alors
début
si liste[j]  bidon alors
début
liste[i]  liste[j]
i  i + 1 ;
fin ;

Dr Ndi Nyoungui André


159 Algorithmique et structures de données

j  j – 1;
fin
sinon
i  i + 1 ;
n  i –1 ;
fin ;

Exercices d’apprentissage

Exercice 6.16
Écrire un algorithme pour supprimer un sous-vecteur donnée dans un vecteur.

Exercice 6.17
Écrire un algorithme pour remplacer un sous-vecteur donné dans un vecteur par un autre.

Exercice 6.18
Écrire un algorithme pour remplacer toutes les occurrences d’un sous-vecteur donnée dans un
vecteur par un autre.

Exercice 6.19
Écrire un algorithme pour nettoyer un texte (vecteur de caractères), en éliminant les espaces
superflus : chaque fois que l’on trouve une suites d’espaces, on les supprime tous sauf un.

6.3.5 Partition d’un vecteur en trois zones

Il s’agit de diviser un vecteur en trois zones, selon un critère qui peut prendre trois valeurs
différentes. Dans chaque zone, tous les éléments présenteront la même valeur du critère. On
demande d’effectuer cette partition en un seul parcours du vecteur, chaque élément ne devant
être examiné qu’une seule fois. On s’efforcera également de minimiser le nombre de
permutations.

Le problème peut être exprimé de la manière suivante : un vecteur contient n boules qui
peuvent être bleues, blanches ou rouges. On demande de trier le vecteur de telle sorte qu’il
contienne d’abord les boules bleues, ensuite les boules blanches et enfin les boules rouges.

On dispose de trois prédicats bleu(liste[i]), blanc(liste[i]) et rouge(liste[i]) qui ont la valeur


vrai si et seulement si la boule numéro i est respectivement bleue, blanche ou rouge.

Une première version de l’algorithme est la suivante :


procédure partition(var liste : vélément ; n : entier) ;
variable
i, b, r : entier ;
début
i  1;
b 1;
r  n;
tantque i  r faire
si blanc(liste[i]) alors
ii+1

Dr Ndi Nyoungui André


160 Algorithmique et structures de données

sinon
si bleu(liste[i]) alors
début
permute(liste[b], liste[i]) ;
b  b + 1;
i  i + 1;
fin
sinon
début
si i < r alors
permute(liste[r], liste[i]) ;
r  r – 1 ;
fin ;
fin ;

On constate que le nombre de permutations n’est pas optimal dans cette version. En effet, lors
de la permutation (liste[r], liste[i]), si liste[r] est une boule rouge, on permutera de nouveau
cette boule au pas suivant. On peut donc, afin de minimiser le nombre de permutations,
s’assurer que lorsque liste[i] est une boule rouge, qu’on ne permute pas avec une autre boule
rouge liste[r].

L’algorithme devient alors :

procédure partition(var liste : vélément ; n : entier) ;


variable
i, b, r : entier ;
début
i  1;
b 1;
r  n;
tantque i  r faire
si blanc(liste[i]) alors
ii+1
sinon
si bleu(liste[i]) alors
début
permute(liste[b], liste[i]) ;
b  b + 1;
i  i + 1;
fin
sinon
si rouge(liste[r]) alors
rr–1
sinon
début
si i < r alors
permute(liste[r], liste[i]) ;
r  r – 1 ;
fin ;

Dr Ndi Nyoungui André


161 Algorithmique et structures de données

fin ;

6.4 Les algorithmes de tri

Un problème classique dans le traitement des données est celui du tri des vecteurs. Étant
donné un vecteur liste[1..n] de n éléments d’un ensemble totalement ordonné E, trier ces n
éléments revient à les réorganiser de telle sorte qu’ils soient classés soit par ordre croissant
soit par ordre décroissant.

Il existe de nombreux algorithmes de tri. Dans cette section, nous allons nous intéresser
uniquement aux méthodes de tri les plus classiques qui sont :

 le tri par bulles ou par échanges


 le tri par insertion linéaire ou par sélection
 le tri par insertion directe
 le tri par insertion par bissection
 le tri par segmentation (Quicksort)

6.4.1 Le tri par échanges ou par bulles

Méthode : Le tri par bulles est une méthode de tri très simple mais très peu efficace. Pour
trier par ordre croissant un vecteur liste[1..n], le tri par bulles s’exécute en (n - 1) étapes de la
manière suivante :

Étape 1 : Parcourir le vecteur de liste[1] à liste[n] en faisant des comparaisons échanges pour
amener dans liste(n) le plus grand élément du vecteur liste[1..n] ;
Étape 2 : Parcourir le vecteur de liste[1] à liste[n – 1] en faisant des comparaisons échanges
pour amener dans liste[n – 1] le plus grand élément du sous-vecteur liste[1..n - 1] ;
.
.
.
Étape k : Parcourir le vecteur de liste[1] à liste[n-k+1] en faisant des comparaisons échanges
pour amener dans liste[n – k + 1] le plus grand élément du sous-vecteur liste[1..n – k + 1] ;
.
.
.
A la dernière étape, les deux premiers sont comparés et échangés si nécessaire, et le tri est
terminé.

On a donc besoin d’une procédure qui effectue la permutation de deux éléments :

procédure permute(var a, b : télément) ;


variable
temp : télément ;
début
temp  a ;
a  b ;
b  temp ;
fin ;

Dr Ndi Nyoungui André


162 Algorithmique et structures de données

procédure tribulles(var liste : vélément ; n : entier) ;


variable
i, k : entier ;
début
k  1 ;
tantque k  n - 1 faire
début
i  1 ;
tantque i  n - k faire
début
si liste[i] > liste[i + 1] alors
permute(liste[i], liste[i + 1]) ;
i  i + 1 ;
fin;
k  k + 1 ;
fin;
fin;

Optimisation de l’algorithme

Quand on utilise le tri par bulles, on n’a pas souvent besoin de faire toutes les n-1 passes. Il y
a un moyen simple de déterminer si un vecteur est déjà complètement trié. Si une passe trouve
chaque couple d’éléments consécutifs dans un bon ordre et n’effectue donc aucune
permutation, alors le vecteur est déjà trié. On peut donc modifier l’algorithme ci-dessus pour
prendre avantage de cette information. Pour cela, on introduit un indicateur booléen que l’on
met à vrai au début de chaque passe et on le met à faux lorsqu’une permutation est effectuée.,
Si cet indicateur est toujours à vrai à la sortie de la passe, alors on considère que le vecteur est
déjà trié et on arrête.

L’algorithme devient alors :


procédure tribullesoptimal(var liste : vélément ; n : entier) ;
variable
i, k : entier ;
stop : booléen;
début
stop  faux ;
k  1 ;
tantque (k  n – 1) et (non stop) faire
début
i  1 ;
stop  vrai ;
tantque i  n - k faire
début
si liste[i] > liste[i + 1] alors
début
permute(liste[i], liste[i + 1]) ;
stop  faux ;
fin ;

Dr Ndi Nyoungui André


163 Algorithmique et structures de données

i  i + 1 ;
fin;
k  k + 1 ;
fin;
fin;

6.4.2 Le tri par insertion linéaire ou par sélection

Méthode : Le tri par sélection est une méthode de tri très simple mais très peu efficace. Pour
trier par ordre croissant un vecteur liste[1..n] de n éléments, le tri par sélection s’exécute en (n
- 1) étapes de la façon suivante :

Étape 1 : Déterminer et ranger dans liste[1] le plus petit élément du sous-vecteur liste[1..n] ;
Étape 2 : Déterminer et ranger dans liste[2] le plus petit élément du sous-vecteur liste[2..n] ;
.
.
.
Étape k : Déterminer et ranger dans liste[k] le plus petit élément du sous-vecteur liste[k..n] ;
.
.
.
À la dernière étape (n - 1) les deux derniers éléments sont comparés et échangés si nécessaire
et le tri est terminé.

On a donc besoin d’une fonction qui délivre à chaque étape k, l’indice du plus petit élément
du sous-vecteur liste[k..n].

fonction indicemin(liste : vélément ; k, n : entier) : entier ;


variable
i, indice : entier ;
début
indice  k ;
pour i  k + 1 haut n faire
si liste[indice] > liste[i] alors
indice  i ;
indicemin  indice ;
fin ;

L’algorithme de tri par sélection est alors :


procédure trisélection(var liste : vélément ; n : entier) ;
variable
i, k : entier ;
début
k  1 ;
tantque k < n faire
début
i  indicemin(liste, k, n) ;
si i  k alors
permute(liste[i], liste[k]);

Dr Ndi Nyoungui André


164 Algorithmique et structures de données

k  k + 1 ;
fin;
fin;

6.4.3 Le tri par insertion directe

Méthode : Le tri par insertion directe est une méthode de tri très efficace pour trier des
vecteurs de taille moyenne. Pour trier par ordre croissant un vecteur liste[1..n], le tri par
insertion directe procède de la manière suivante :

Étape 1 : Initialement, le premier élément liste[1] est placé ;


Étape 2 : Insérer liste[2] dans le sous-vecteur liste[1..1] de façon à former un vecteur trié de
deux éléments ;
Étape 3 : Insérer liste[3] dans le sous-vecteur liste[1..2] pour former un vecteur trié de trois
éléments
.
.
.
Étape k : Insérer liste[k] dans le sous-vecteur liste[1..k-1] pour former un vecteur trié de k
éléments ;
.
.
.

L’algorithme du tri par insertion directe est donc le suivant :

procédure trinsertion(var liste: vélément ; n : entier) ;


variable
i, j , k : entier ;
elem : télément;
début
i  2 ;
tantque i  n faire
début
j  1 ;
tantque (liste[j]  liste[i]) et (j < i) faire
j  j + 1 ;

si (j < i) alors
début
elem  liste[i];
pour k  i bas j + 1 faire
liste[k]  liste[k - 1];

liste[j]  elem;
fin;
i  i + 1 ;
fin;
fin;

Dr Ndi Nyoungui André


165 Algorithmique et structures de données

En observant qu’à chaque étape i, le sous-vecteur liste[1..i] est déjà trié, on peut utiliser la
procédure d’insertion dans un vecteur trié que nous avons présentée précédemment pour
placer l’élément liste[i + 1]. La procédure de tri par insertion directe devient alors :

procédure trinsertion(var liste : vélément ; n : entier) ;


variable
i : entier ;
début
i  1 ;
tantque i < n faire
insertion(liste, i, liste[i + 1]);
fin;

6.4.4 Le tri par insertion par bissection

C’est une version du tri par insertion directe dans laquelle on utilise la recherche binaire pour
trouver la place de l’élément liste[i] dans le sous-vecteur liste[1..i-1] à l’étape i. On a donc
besoin d’une version de l’algorithme de recherche dichotomique qui délivre la place de
l’élément dans le vecteur.

fonction indichotomie(liste : vélément ; n : entier ; elem : télément) : entier ;


variable
inf, sup, m : entier ;
début
si liste[n] elem alors
indichoctomie  n + 1
sinon
début
inf  1 ;
sup  n ;
tantque (inf < sup) faire
début
m  (inf + sup) div 2 ;
si liste[m]  elem alors
inf  m + 1
sinon
sup  m ;
fin ;
indichotomie  sup ;
fin ;
fin ;

On peut maintenant écrire l’algorithme de tri par insertion par bissection de la manière
suivante :

procédure tribissection(var liste: vélément ; n : entier) ;


variable
i, j : entier ;

Dr Ndi Nyoungui André


166 Algorithmique et structures de données

elem : télément;
début
pour i  2 haut n faire
début
j  indichotomie(liste, i – 1, liste[i]) ;
si j < i alors
début
elem  liste[i];
pour k  i bas j + 1 faire
liste[k]  liste[k - 1];

liste[j]  elem ;
fin;
fin;
fin;

6.4.5 Le tri rapide (Quicksort)

Méthode : Le Quicksort fonctionne en sélectionnant un élément du vecteur à trier, appelé


pivot, et en plaçant le pivot dans sa position finale dans le vecteur. Au fur et mesure que la
position du pivot est recherchée dans le vecteur, les autres éléments sont déplacés de telle
sorte que tous les éléments à gauche du pivot lui soient inférieurs et tous les éléments à droite
du pivot lui soient supérieurs. Lorsque le pivot est finalement mis à sa place, le vecteur est
peut être divisé en deux sous-vecteurs : le sous-vecteur des éléments au pivot et le sous-
vecteur des éléments supérieurs ou égaux au pivot. Ces deux sous-vecteurs sont ensuite triés
en faisant des appels récursifs à la procédure Quicksort. Le processus de récursion prend fin
lorsque le sous-vecteur à trier ne contient plus qu’un seul élément.

Cet algorithme peut être décrit de la manière suivante :

début
si le vecteur à trier a encore plus de un élément alors
début
Sélectionner le pivot
Réorganiser le vecteur pour placer le pivot à sa position finale
Trier le sous-vecteur à gauche du pivot
Trier le sous-vecteur à droite du pivot
fin ;
fin ;

Nous allons donc commencer par écrire une fonction qui permet de sélectionner le pivot avant
de voir comment on procède pour amener le pivot dans sa position finale. Le pivot peut être
choisi de différentes manières. On peut par exemple, prendre comme pivot le premier élément
du vecteur à trier. Une autre approche consiste à choisir le plus grand entre les deux premiers
éléments comme pivot. Lorsque le vecteur est presque trié, il peut paraître plus logique de
prendre comme pivot l’élément milieu du vecteur (pivot central).

La fonction ci-dessus retourne comme position du pivot l’indice du premier élément du


vecteur à trier. Elle retourne 0 si le vecteur a un seul élément.

Dr Ndi Nyoungui André


167 Algorithmique et structures de données

fonction Pivot(i, j : entier) : entier ;


début
si i < j alors
pivot  j
sinon
pivot  0;
fin ;

Une fois que la valeur du pivot a été choisi, on a besoin d’un algorithme pour placer le pivot
dans sa position finale. Pour faire cela on utilise deux indices dans le vecteur : un indice L se
déplaçant de la gauche vers la droite et un autre indice R se déplaçant de la droite vers la
gauche. À tout moment les valeurs se trouvant à gauche de L sont plus petites que le pivot
tandis que celles qui se trouvent à droite de R sont supérieures ou égales au pivot. Pour
obtenir cela, L se déplace en dépassant tout élément qui est inférieur au pivot. Il arrête son
mouvement quand il rencontre une valeur supérieure ou égale au pivot. Si L croise R, alors on
a fini et le pivot est positionné. Si L arrête son mouvement à gauche de R, R commence à se
déplacer de la droite vers la gauche. R se déplace en passant toute valeur supérieure ou égale
au pivot. Lorsque R arrête à son tour son mouvement, les valeurs se trouvant en position L et
R sont permutées et L reprend son mouvement vers la droite. Pour trier le reste du vecteur, on
répète le processus avec les sous-vecteurs à gauche et à droite du pivot.

Algorithmes

Compte tenu de l’importance du tri rapide, nous donnons ci-dessous plusieurs versions de cet
algorithme de tri qui a, dans le cas général, de meilleures performances que les algorithmes
précédents.

Première version

procédure Quicksort(var liste : vélément ; inf, sup : entier) ;


variable
L, R, place : entier ;
début
place  Pivot(inf, sup) ;
si place  0 alors
début
L  place + 1 ;
R  sup ;
tantque L  R faire
début
tantque liste[L] < liste[place] faire
L  L + 1 ;

tantque liste[R]  liste[place] faire


R  R – 1 ;

permute(liste[L], liste[R])
fin ;

Dr Ndi Nyoungui André


168 Algorithmique et structures de données

permute(liste[place], liste[R]) ;
Quicksort(liste, inf, R – 1) ;
Quicksort(liste, R + 1, sup)
fin;
fin;

Deuxième version

Elle est basée sur le principe de « diviser pour régner » que nous avons déjà utilisé dans
l’algorithme de recherche dichotomique. Si on appelle partition la procédure qui permet de
trouver et de placer le pivot dans sa place finale, le Quicksort s’écrit récursivement :

procédure Quicksort(var liste : vélément ; inf, sup : entier) ;


variable
place : entier ;
début
si inf < sup alors
début
partition(liste, inf, sup, place) ;
Quicksort(liste, inf ; place - 1) ;
Quicksort(liste, place + 1, sup) ;
fin ;
fin ;

Écriture de la procédure de partition

On choisit une valeur appartenant au vecteur liste[inf..sup] que l’on appelle pivot. Ensuite on
effectue des permutations afin de classer les éléments du vecteur par rapport à ce pivot)

procédure partition(var liste : vélément ; inf, sup : entier ; var place : entier) ;
variable
L, R : entier ;
Pivot : télément;
début
pivot  liste[inf];
L  inf + 1;
R  sup;
tantque L  R faire
si liste[L]  pivot alors
LL+1
sinon
début
permute(liste[L], liste[R]) ;
R  R – 1 ;
fin ;
permute(liste[inf], liste[R])
place  R;
fin ;

Dr Ndi Nyoungui André


169 Algorithmique et structures de données

En analysant cet algorithme, on constate que l’on effectue des permutations inutiles. En effet,
en permutant liste[L] et liste[R], si liste[R] est supérieur au pivot, on permutera de nouveau
cet élément liste[R] qui est en position i pour le remettre en position R – 1.

Version optimisée

Afin d’éviter ces permutations, nous donnons ci-dessous une nouvelle version qui ne permute
liste[L] et liste[R] que dans la cas où liste[L] > pivot et liste[R]  pivot. L’algorithme est alors
le suivant :

procédure partition(var liste : vélément ; inf, sup : entier ; var place : entier) ;
variable
L, R : entier ;
pivot : télément;
début
pivot  liste[inf];
L  inf + 1;
R  sup;
tantque L  R faire
si liste[L]  pivot alors
LL+1
sinon
si liste[R] > pivot alors
R  R – 1 ;
sinon
si L < R alors
début
permute(liste[L], liste[R]) ;
L  L + 1 
R  R – 1 ;
fin ;

permute(liste[inf], liste[R])
place  R ;
fin ;

Tri par sélection avec pivot central

Il peut paraître plus logique, lorsque le vecteur est presque trié, de prendre le pivot au milieu
du vecteur (pivot central). Nous donnons ci-dessous une version du Quicksort qui inclut les
deux appels récursifs dans une procédure unique.

procédure Quicksort(var liste : vélément ; inf, sup : entier) ;


variable
L, R : entier ;
pivot: télément;
début
L  inf;
L  sup;

Dr Ndi Nyoungui André


170 Algorithmique et structures de données

pivot  liste[(inf + sup) div 2];


tantque L  R faire
début
tantque liste[L] < pivot faire
LL+1

tantque liste[L] < pivot faire


R  R – 1 ;

si L  R alors
début
si L < R alors
permute(liste[L], liste[R]) ;
L  L + 1 
R  R – 1 ;
fin ;
fin ;

si inf < R alors


Quicksort(liste, inf, R) ;

si L < sup alors


Quicksort(liste, L, sup) ;
fin ;

Exercices d’apprentissage

Exercice 6.20
Modifier le tri par bulles pour obtenir un vecteur trié par ordre décroissant.

Exercice 6.21
Modifier le tri par sélection pour obtenir un vecteur trié par ordre décroissant.

Exercice 6.22
Modifier le tri par insertion pour obtenir un vecteur trié par ordre décroissant.

Exercice 6.23
Modifier le tri par insertion par bissection pour obtenir un vecteur trié par ordres décroissant.

Exercice 6.24
Modifier le Quicksort pour obtenir un vecteur trié par ordre décroissant.

6.5 Interclassement de vecteurs

Ce problème consiste à construire, à partir de plusieurs vecteurs triés, un seul vecteur suivant
un certain critère de fusion. Nous prenons comme exemple celui de l’interclassement de deux
vecteurs triés.

Dr Ndi Nyoungui André


171 Algorithmique et structures de données

Soient deux vecteurs triés liste1[1..n1] et liste[1..n2] à valeurs dans un même ensemble E et
munis de la même relation d’ordre. On veut construire un vecteur liste3[1..n3] trié et
contenant tous les éléments de liste1 et liste2. Il est évident qu’on aura n3 = n1 + n2.

L’algorithme se divise en deux parties distinctes :


 Une première partie consistant à parcourir les deux vecteurs liste1 et liste2 et à prendre
à chaque fois le plus petit élément. Dès qu’un vecteur est terminé, on aborde la
deuxième partie.
 Une deuxième partie consistant à compléter le vecteur résultat par tous les éléments
restants du vecteurs non terminé. Pour cette deuxième partie, on peut compléter avec
les éléments restants des deux vecteurs, l’un d’entre eux étant nécessairement terminé.

procédure interclassement(liste1, liste2 : vélément ; n1, n2 : entier ;


var liste3 : vélément ; var n3 : entier) ;
variable
i, j, k : entier ;
début
i  1;
j  1;
k  0;
n3  n1 + n2;
tantque (i  n1) et (j  n2) faire
début
k  k + 1 ;
si liste1[i]  liste2[j] alors
début
liste3[k]  liste1[i] ;
i  i + 1 ;
fin
sinon
début
liste3[k]  liste2[j] ;
j  j + 1 ;
fin ;
fin ;
tantque i  n1 faire
début
k  k + 1 ;
liste3[k]  liste1[i] ;
i  i + 1 ;
fin ;
tantque j  n2 faire
début
k  k + 1 ;
liste3[k]  liste2[j] ;
j  j + 1 ;
fin ;

Dr Ndi Nyoungui André


172 Algorithmique et structures de données

Application : tri par interclassement

L’algorithme d’interclassement peut être utilisé dans un algorithme de tri qui s’exprime
récursivement de manière assez semblable au Quicksort. Il paraît ressembler aussi à une
recherche dichotomique, mais oblige à parcourir chaque fois l’ensemble du vecteur non
seulement une moitié.
À chaque étape, on trie la première moitié du vecteur et la seconde moitié, puis on interclasse
les deux sous-vecteurs triés.

procédure triinterclass(var liste : vélément ; inf, sup : entier) ;


variable
m : entier ;
début
si sup > inf alors
début
m  (inf + sup) div 2 ;
triinterclass(liste, inf, m) ;
triinterclass(liste, m + 1, sup) ;
interclassement(liste, inf, m, liste, m + 1, sup, liste, inf, sup) ;
fin ;
fin ;

Exercice 6.25
Écrire un algorithme pour calculer la réunion de deux ensembles représentés par des vecteurs.

Exercice 6.26
Écrire un algorithme pour calculer l’intersection de deux ensembles représentés par des
vecteurs.

Exercice 6.27
Écrire un algorithme pour calculer la différence ensembliste de deux ensembles représentés
par des vecteurs.

Exercice 6.28
Écrire un algorithme pour calculer la différence symétrique de deux ensembles représentés
par des vecteurs.

6.6 Les chaînes de caractères

Dans les exemples précédents, nous avons utilisé les vecteurs pour traiter des valeurs
numériques. Cependant, comme nous l’avons indiqué au début du chapitre, les éléments d’un
vecteur ne sont pas nécessairement de type numérique. Nous pouvons déclarer et utiliser des
vecteurs dont les composantes sont des caractères. Les instructions suivantes déclarent les
vecteurs de caractères nom et prénom.

variable
nom : vecteur[1..20] de caractère ;
prénom : vecteur[1..12] de caractère ;

Dr Ndi Nyoungui André


173 Algorithmique et structures de données

Comme avec les composantes des autres vecteurs, on peut utiliser des boucles pour manipuler
les caractères stockés dans un vecteur. Par exemple, le segment d’algorithme ci-dessous lit 20
caractères et les stocke dans le vecteur de caractères nom. Il lit ensuite 12 caractères et les
stocke dans le vecteur de caractères prénom.

pour i  1 haut 20 faire


lire(nom[i]) ;
pour i  1 haut 12 faire
lire(prénom[i]) ;

Cependant, le traitement des données caractères un caractère à la fois est très encombrant car
les boucles doivent être utilisées pour toutes les manipulations. Heureusement, le langage
algorithmique offre un type spécial de vecteur de caractères pour faciliter la manipulation de
plusieurs caractères à la fois. Dans les chapitres précédents, nous avons introduit des objets de
la forme ‘Bonjour’. Ces objets sont des chaînes de caractères. Bien que nous ayons utilisé
plusieurs fois les chaînes de caractères, nous n’avons pas indiqué leur type de donnée. Toutes
les chaînes de caractères sont des constantes du type :

chaîne[1..n] de caractère

où n est la longueur de la chaîne. Ainsi, ‘Bonjour’ est une constante de type :

chaîne[1..7] de caractère ou encore chaîne7 pour simplifier.

L‘avantage d’utiliser le type chaîne de caractères est qu’il offre la possibilité de manipuler
l’ensemble des caractères d’une chaîne comme un tout. Par exemple, si on déclare les
variables nom et prénom par :

variable
nom : chaîne[1.20] de caractère
prénom : chaîne[1.12] de caractère

les instructions suivantes lisent puis impriment les variables nom et prénom :

lire(nom) ;
lire(prénom) ;
écrire(‘nom :’, nom) ;
écrire(‘prénom :’, prénom) ;

Exercice 6.29
Un palindrome est une chaîne de caractères qui se lit de la manière de la gauche vers la droite
et de la droite vers la gauche. Écrire une fonction qui prend en entrée une chaîne de caractères
et détermine si c’est un palindrome ou non.

6.7 Les tableaux à plusieurs dimensions

On a souvent besoin de tableaux qui correspondent aux entrées d’une table plutôt qu’à une
liste. Par exemple, les données de la table suivante

Dr Ndi Nyoungui André


174 Algorithmique et structures de données

Test
Étudiant 1 2 3 4
1 68 75 78 65
2 100 91 87 95
3 85 87 78 96

peuvent être représentées par un tableau à deux dimensions de la manière suivante :


Numéros de ligne

Numéros de colonne
1 2 3 4
1 68 75 78 65

2 100 91 87 95
Comme avec les tableaux à3 une85 dimension
87 ou78 vecteurs,
96 un tableau à deux dimensions est
dénoté par un identificateur qui référence le tableau entier. Pour localiser une composante
dans un tableau à deux dimensions, deux indices doivent être utilisés, un pour le numéro de
ligne et l’autre pour le numéro de colonne. Par exemple, supposons que le tableau ci-dessus
soit appelé notes. La composante notes[1, 3] référence la valeur se trouvant dans la ligne 1,
colonne 3. C’est-à-dire que la valeur de notes[1, 3] est 78. De même, la valeur de notes[3, 2]
est 87.

Tout tableau à deux dimensions utilisé dans un algorithme doit être déclaré. La déclaration
d’un tel tableau est semblable à celle d’un vecteur. Par exemple, pour déclarer le tableau notes
correspondant à la figure ci-dessus, on peut utiliser la déclaration :

variable
notes : tableau[1..3, 1..4] de entier ;

La définition d’un type tableau à deux dimensions doit spécifier :

 l’identificateur du type,
 l’ensemble des indices de ligne et l’ensemble des indices de colonne,
 le type des éléments du tableau.

type
identificateur = tableau[typeindiceligne, typeindicecolonne] de télément ;

où télément est le type des éléments du tableau, typeindiceligne le type des indices de ligne et
typeindicecolonne le type des indices de colonne.

Par exemple, pour définir un type tableau à deux dimensions de m lignes et de n colonnes, on
utilise une définition de type de la forme  :

Dr Ndi Nyoungui André


175 Algorithmique et structures de données

type
tmatrice = tableau[1..m, 1..n] de télément ;

Chaque élément X[i, j] d’un tableau à deux dimensions est caractérisé par son indice de ligne i
et par son indice de colonne j. La manipulation d’un tableau à deux dimensions nécessite donc
habituellement l’utilisation des boucles imbriquées. Par exemple, la procédure ci-dessus lit
une matrice ligne par ligne.

procédure lirematrcice(var notes : tmatrice) ;


variable
i, j : entier ;
début
pour i  1 haut m faire
pour j  1 haut n faire
lire(notes[i, j]) ;
fin ;

De même, la procédure ci-dessous imprime les élément d’une une matrice ligne par ligne.

procédure lirematrcice(var notes : tmatrice) ;


variable
i, j : entier ;
début
pour i  1 haut m faire
début
pour j  1 haut n faire
lire(notes[i, j]) ;
écrireln ;
fin ;
fin ;

Il est bien entendu possible de déclarer et d’utiliser des tableaux à trois dimensions ou plus.

6.7.1 Tableaux de tableaux

Le langage algorithmique nous permet d’avoir des tableaux de n’importe quel type. Jusque
maintenant nous avons travaillé avec des tableaux dont le type de base est un type scalaire
(entier, réel, caractère, booléen) ou un type défini par l’utilisateur. Étant donné que nous
pouvons définir un type de tableau, nous pouvons aussi avoir des tableaux dont les éléments
sont eux-mêmes des tableaux. Par exemple, considérons les déclarations suivantes :

type
tétudiant = tableau[1..5] de entier ;
variable
Classe : tableau[1..35] de tétudiant ;

Le tableau Classe a 35 éléments de type tétudiant. Donc Classe[3] référence le troisième


élément de Classe. En d’autres termes, il référence le troisième étudiant. Le type tétudiant, à
son tour, est un tableau (vecteur) pouvant contenir cinq notes. La note du deuxième test du

Dr Ndi Nyoungui André


176 Algorithmique et structures de données

troisième étudiant est stockée dans Classe[3][2]. Le premier indice [3], est utilisé pour
sélectionner l’étudiant qui dans ce cas est un tableau contenant 5 entiers représentant les notes
de cet étudiant. Le deuxième indice [2] est utilisé pour sélectionner la deuxième note de cet
étudiant.
La déclaration ci-dessus, bien entendu, est équivalente à :

variable
Notes : tableau[1..35, 1..5] de entier ;

La composante Notes[3, 2] du tableau Notes est équivalente à la composante Classe[3][2] du


tableau Classe. Le seul avantage du tableau Classe sur le tableau Notes est qu’il permet
l’affectation de toutes les notes d’un étudiant sans utiliser une boucle. Ainsi, pour affecter les
notes de l’étudiant i à l’étudiant j on peut utiliser l’instruction Classe[j]  Classe[i]. Avec
Notes on aurait écrit une boucle pour affecter les cinq notes une à la fois.

6.7.2 Tableaux de chaînes de caractères

Une application importante des tableaux dont les éléments sont des tableaux implique les
chaînes de caractères. Un tableau dont les éléments sont des chaînes de caractères peut être
déclaré comme suit :

type
tnom = chaîne[1..15] de caractère ;
variable
Clients : tableau[1..100] de tnom ;

Le tableau Clients a 100 composantes, chacune d’elle est une chaîne de caractères de longueur
15. Comme avec un tableau dont les éléments sont des tableaux, nous pouvons référencer le
nom d’un client particulier en utilisant un simple indice. La composante Clients[i] référence le
nom du client numéro i. Toutes les opérations qui peuvent être utilisées avec les chaînes
peuvent aussi être utilisées avec les composantes du tableau Clients. L’instruction

Clients[j]  Clients[i]

affecte le nom du client numéro i au client numéro j. L’instruction

Écrire(Clients[i])

imprime la chaîne de caractères qui constitue le nom du client numéro i. Enfin, l’expression
logique

Clients[i] = Clients[j]

teste l’égalité entre les noms des clients numéros i et j.

Exercices d’apprentissage

Exercice 6.30

Dr Ndi Nyoungui André


177 Algorithmique et structures de données

Écrire une procédure qui reçoit en entrée deux matrices réelles de m lignes et n colonnes et
retourne la somme des deux matrices.

Exercice 6.31
Écrire une procédure qui reçoit en entrée une matrice réelle A de m lignes et n colonnes et une
matrice B réelle de n lignes de p colonnes retourne le produit AB de ces deux matrices.

Exercice 6.32
Une matrice de N lignes et N colonnes est dite symétrique si A[i, j] = [j, i] pour couple (i, j).
Écrire une fonction qui prend en une matrice réelle de N lignes et N colonnes et détermine si
la matrice est symétrique.

Exercice 6.33
Écrire une fonction qui prend en entrée une matrice réelle de m lignes et n colonnes et
retourne le plus grand élément de cette matrice.

Exercice 6.34
Écrire une procédure qui prend en entrée une matrice réelle de m lignes et n colonnes et
retourne le plus grand élément de cette matrice ainsi sa position ligne/colonne.

Exercice 6.35
Écrire une procédure qui calcule la transposée d’une matrice réelle.

Exercice 6.36
Un carré magique est un tableau de N lignes et N colonnes d’entiers de l’intervalle [1.. N*N]
tel que les sommes des termes de chaque ligne, chaque colonne et de chaque diagonale
principale sont égales. Écrire une fonction qui prend en entrée une matrice de N lignes et N
colonnes d’entiers de l’intervalle [1.. N*N] et détermine si la matrice est un carré magique.

Exercice 6.37
Écrire une fonction qui prend en entrée une matrice réelle de m lignes et n colonnes et
retourne le numéro de la ligne dont la somme des coefficients est maximum.

Exercice 6.38
Écrire une procédure qui prend en entrée un entier n et retourne la matrice des n premières
lignes du triangle de Pascal.

6.8 Études de cas

Dans ces études de cas, nous allons appliquer les algorithmes sur les vecteurs (création,
parcours, recherche, insertion, suppression, tri) à des structures de données plus ou moins
complexes.

Gestion des étudiants

On souhaite gérer une liste d’étudiants à l’aide de quelques algorithmes simples sur les
vecteurs (parcours, recherche, insertion, suppression, tri). Les informations retenues pour
chaque étudiant sont le numéro matricule, le nom (nom et prénom), le sexe , la date de

Dr Ndi Nyoungui André


178 Algorithmique et structures de données

naissance, le lieu de naissance, la filière, le nombre et la liste des cours de la filière. Bien
entendu, le numéro matricule sera différent pour chaque étudiant.

On suppose que l’on a les définitions suivantes :

constante
taillemax = 100 ;
nbmax = 20 ;
type
tdate = article
jour, mois, année : entier ;
fin ;
tcours = article
code : entier ;
intitulé : chaîne ;
examenpartiel : réel ;
examenfinal : réel ;
fin ;
vcours = vecteur[1..nbmax] de tcours ;
tétudiant = article
matricule : chaîne ;
nom : chaîne ;
sexe : (Féminin, Masculin) ;
annéenais : tdate ;
lieunaiss : chaîne ;
filière : chaîne ;
nbcours : entier ;
courspris : vcours ;
fin ;
vétudiant = vecteur[1..taillemax] de tétudiant ;

On suppose que le vecteur est trié par ordre alphabétique et que tous étudiants de la même
filière ont les mêmes cours.

1°/ Écrire un algorithme pour créer un vecteur de n étudiants.

2°/ Écrire un algorithme qui imprime le nom, la date de naissance, le lieu de naissance et la
spécialité de chacun des étudiants enregistrés dans un vecteur de n étudiants.

3°/ Écrire une fonction qui retourne le nombre d’étudiants d’une filière donnée contenus dans
un vecteur de n étudiants.

4°/ Écrire une procédure qui retourne le nombre de filles et le nombre de garçons présents
dans un vecteur de n étudiants.

5°/ Écrire un algorithme pour afficher le matricule, le nom, le lieu de naissance et la spécialité
de chacun des étudiants dont l’année de naissance est comprise entre aninf et ansup dans un
vecteur de n étudiants.

Dr Ndi Nyoungui André


179 Algorithmique et structures de données

6°/ Écrire une fonction qui retourne la position d’un étudiant de matricule donné dans un
vecteur de n étudiants. La fonction retourne zéro si l’étudiant concerné n’est pas présent dans
la liste.

7°/ Écrire une procédure pour insérer un nouvel étudiant dans un vecteur de n étudiants. La
procédure doit d’abord vérifier qu’il n’existe pas encore un étudiant ayant le même matricule.

8°/ Écrire une procédure qui supprime un étudiant de matricule donné dans un vecteur de n
étudiants.

9°/ Écrire un algorithme qui calcule le nombre d’unités de valeur validées par un étudiant de
matricule donné.

10°/ Écrire une fonction qui retourne la moyenne des notes d’un étudiant de matricule donné
dans une unité de valeur de code donné.

11°/ Écrire une fonction qui retourne la moyenne générale des notes d’un étudiant de
matricule donné.

12°/ Écrire un algorithme qui classe par ordre de mérite, par rapport à la moyenne générale
des notes, les étudiants d’une filière donnée. À moyenne égale, les étudiants seront classés par
ordre alphabétique.

Stock de voitures

Un marchand de véhicules d’occasion souhaite gérer son stock à l’aide de procédures simples
sur les vecteurs. On suppose que le stock de véhicules, de 100 voitures au plus, peut être
représenté à l’aide d’un tableau en mémoire.

Les seules informations retenues pour chaque voiture seront le numéro d’immatriculation et
son année de mise en service, sa marque et son modèle, ainsi que le prix. Bien entendu, le
numéro d’immatriculation sera différent pour chaque voiture.

Les déclarations de types utilisées seront les suivantes :

type
tvoiture = article
numéro : chaîne ;
année : entier ;
marque, modèle : chaîne ;
prix : réel ;
fin ;
vvoiture = vecteur[1..100] de tvoiture ;

On suppose que le vecteur est trié par ordre croissant sur les numéros des véhicules.

1°/ Écrire une procédure qui prend en entrée un vecteur de n voitures, une marque et modèle
et affiche l’année et le prix de chacune des voitures de le marque et du modèle demandés

Dr Ndi Nyoungui André


180 Algorithmique et structures de données

2°/ Écrire une procédure qui prend en entrée un vecteur de n voitures, un prix inférieur et un
prix supérieur et affiche l’année, la marque et le modèle de chacune des voitures dont le prix
est compris entre les deux prix.

3°/ Écrire une procédure qui prend en entrée un vecteur de n voitures et un numéro et retourne
position du vecteur dans le vecteur. La procédure retourne également un indicateur booléen
permettant de déterminer si la voiture est présente dans la liste.

4°/ Écrire une procédure qui prend entrée un vecteur de n voitures et une nouvelle voiture et
procède à l’insertion de la nouvelle voiture dans la liste ; le vecteur de sortie devant être trié
sur les numéros. La procédure retourne également un indicateur booléen permettant de
déterminer si l’insertion a effectivement eu lieu.

5°/ Écrire une procédure qui prend entrée un vecteur de n voitures et un numéro et procède à
la suppression de la voiture de numéro demandé. La procédure retourne également un
indicateur booléen permettant de déterminer si la suppression a effectivement eu lieu.

6°/ Écrire une procédure qui reçoit en entrée un vecteur de n voitures et procède au tri du
vecteur par ordre alphabétique sur les marques, et à marque égale, sur les prix décroissants.

Librairie

On souhaite gérer les ouvrages vendus dans une librairie à l’aide d’une structure composée
d’un vecteur liste[1..taillemax]. Chaque composante du vecteur est une variable article
composé des champs suivants : le nom de l’auteur, le titre de l’ouvrage, la discipline, le nom
de l’éditeur, l’année d’édition et le prix du livre.

Le vecteur des ouvrages est supposé trié par ordre alphabétique sur les noms des auteurs.

Les déclarations de types utilisées seront les suivantes :

constante
taillemax = 100 ;
type
touvrage = article
auteur : chaîne ;
titre : chaîne ;
discipline : chaîne ;
éditeur : chaîne ;
prix : réel ;
quantité : entier ;
fin ;
vouvrage = vecteur[1..taillemax] de touvrage ;

On supposera que les titres de tous les livres écrits par un même auteur sont différents.

1°/ Écrire une procédure qui crée un vecteur de n livres et le trie par ordre alphabétique sur les
noms des auteurs.

Dr Ndi Nyoungui André


181 Algorithmique et structures de données

2°/ Écrire une procédure qui imprime un vecteur de n livres.

3°/ Écrire une fonction qui déterminer si un auteur de nom donné a écrit au moins un livre.

4°/ Écrire une fonction qui délivre l’indice d’un ouvrage de titre et d’auteur donnés dans la
liste si cet ouvrage se trouve dans la liste et 0 sinon.

5°/ Écrire une fonction qui délivre la quantité en stock d’un ouvrage de titre et d’auteur
donnés.

6°/ Écrire une procédure qui imprime la liste des ouvrages dont la quantité en stock est égale à
zéro.

7°/ Écrire une procédure qui imprime la liste des ouvrages édités par un éditeur donné.

8°/ Écrire une procédure qui imprime la liste des ouvrages disponibles dans une discipline
donnée.

9°/ Écrire une procédure qui ajoute un nouvel ouvrage dans la liste.

10°/ Écrire une procédure qui supprime un ouvrage de titre et d’auteur donnés.

12°/ Écrire une procédure qui supprime tous les livres dont la quantité en stock est nulle.

Dr Ndi Nyoungui André