Académique Documents
Professionnel Documents
Culture Documents
PLAN DU COURS
Chapitre Page
4. Structures de Contrôle 12
4.1. Structures Conditionnelles ou Alternatives
4.2. Structures Itératives ou Répétitives
5. Procédures et Fonctions 18
5.1. Procédures et Passage de paramètres
5.2. Fonctions, Appel de fonction et Récursivité
5.3. Sous-programmes et Portée des variables
Pour donner des ordres à l'ordinateur, il est nécessaire de pouvoir communiquer avec lui.
Cette communication passe par un langage de programmation, dans lequel est écrit le
programme.
Etymologiquement, le mot algorithme est dérivé du nom d'un mathématicien perse qui a
vécu au IXème siècle, Mohammed al-Khwârizmî (en latin Algorismus). Il a proposé un ensemble
d'opérations élémentaires à exécuter séquentiellement, pour additionner, soustraire, multiplier et
diviser des nombres décimaux.
Un algorithme s'écrit le plus souvent en pseudo-langage de programmation appelé langage
algorithmique. Un algorithme n'est donc exécutable directement par aucune machine. Mais il a
l'avantage d'être facilement traduisible dans tous les langages de programmation.
L'algorithmique, qui est l'art d'écrire des algorithmes, permet de se focaliser sur la
procédure de résolution du problème sans se soucier des spécificités d'un langage particulier.
Pour résoudre un problème, il est vivement conseillé de réfléchir d'abord à l'algorithme
avant de programmer, c'est à dire d'écrire le code en langage de programmation sur un ordinateur
car en fait, un programme est la traduction d’un algorithme dans un langage de programmation.
IMPORTANT : Avant de se mettre devant la machine, on doit écrire les algorithmes !!!
o La conception d’un programme informatique doit passer par les 3 phases suivantes :
1. 1 Problème => Enoncé : La spécification
2. 2 Enoncé => Algorithme : La résolution
3. Algorithme => Programme : Le codage
o Si le problème est bien posé, l’énoncé doit permettre de définir sans ambiguïté :
¾ L’entrée (données du problème)
¾ La sortie (résultats recherchés)
¾ Les relations (éventuelles) entre les données et les résultats
o Voici les principes pour écrire un algorithme de qualité :
¾ Abstraire (Repousser le plus loin possible l’écriture de l’algorithme)
¾ Décomposer (Décomposer la résolution en une suite de “sous-problèmes” que l’on
considère comme résolus)
¾ Combiner (Résoudre le problème par combinaison des abstractions des “sous-
problèmes”)
o Pour fournir une solution au problème, il faut donc un moyen d’exprimer cette solution
(l’algorithme) à l’aide d’un langage qui doit être :
¾ formel (pas d’ambiguïtés),
¾ lisible et concis,
¾ indépendant de tous langages informatiques
¾ coordonné selon les principes suivants :
9 les entités manipulées sont désignées et typées
9 suite ordonnée d’actions modifiant l’´etat d’un programme, pour passer :
de l’´etat initial -le problème -
à l’´etat final - la solution au problème-
On classe souvent les langages de programmation en trois "générations": la première regroupe les
langages machine, la seconde les langages d'assemblage et la troisième les langages évolués.
o Le Langage Machine
Cependant, dans le cadre de l'écriture d'un programme important et complexe, elle est
quasi inutilisable et surtout inutile. On programmera donc dans des langages de programmation
plus commodes à employer : les langages d’assemblage et les langages évolués.
Les langages d’assemblage ont permis l’écriture des instructions du langage machine sous
forme symbolique; la traduction en binaire étant assurée par un programme, fourni par le
constructeur, appelé ASSEMBLEUR.
Les langages d'assemblage sont des langages de programmation les plus proches du
langage machine, où chaque instruction correspond à une instruction machine unique. Bien qu'ils
introduisent certains allégements (codes opératoires mnémoniques, adresses en base 10 ou 16,
utilisation d'étiquettes pour les ruptures de séquence...), ils ne solutionnent pas les autres
inconvénients du langage machine (extrême pauvreté des instructions, liaison étroite en le langage
et le type d'ordinateur). En effet, le jeu d’instructions d’un tel langage est associé à un certain type
de processeur. Ainsi, les programmes écrits en langage assembleur pour un processeur particulier
doivent être réécrits pour tourner sur un ordinateur équipé d’un processeur différent. Après
écriture d’un programme en langage d'assemblage, le programmeur fait alors appel à l’assembleur
spécifique du processeur, qui traduit ce programme en instructions machine.
Cependant, les langages d'assemblage sont encore utilisés aujourd'hui dans quelques cas :
¾ -Quand la vitesse d'exécution est primordiale (par exemple, les jeux vidéo ou
l'informatique en temps réel).
¾ Pour accéder directement à certains périphériques (programmation d'une carte
vidéo, par exemple).
¾ Quand la taille du programme est vitale (par exemple, l'informatique embarquée ou
les virus).
Pour pallier aux inconvénients des langages d'assemblage, on a imaginé de créer des
langages dits évolués, écrits avec l’alphabet usuel, comportant des sigles ayant une valeur
mnémotechnique pour l’utilisateur. Pour chaque langage et chaque type de machine, on a rédigé
un programme (baptisé compilateur) qui assure la traduction de tout texte écrit avec le langage
évolué en un texte en langage machine, en garantissant la fidélité de la traduction : les actions
exécutées par la machine sont effectivement celles qui sont décrites par le langage évolué.
Les langages impératifs ou procéduraux ne s’éloignent des langages machines que par leur
richesse d’expression ; ils en gardent la structure de calcul. Ces langages sont basés sur :
¾ Des variables avec des types de données définis
¾ Le contrôle de l’enchainement à travers des structures de contrôle
¾ Des procédures et des fonctions qui sont des subdivisions du programme ou des sous-
programmes
Exemples de langages procéduraux : Pascal, C, Delphi, …
Pour faire résoudre un problème par un ordinateur, il faut en fournir une méthode explicite
de calcul si on utilise la programmation impérative, une méthode implicite par la programmation
récursive. Dans les deux cas, il faut trouver une relation de récurrence exploitable. La
programmation logique, inventée par le Français Alain Colmerauer, fait sortir de cet univers. Elle
vise à donner à l’ordinateur non un algorithme de résolution, mais des informations sur les
données et les relations qui les lient aux résultats. Nous sortons donc du domaine où programmer
veut dire trouver une méthode de résolution. Il n’y a plus création d’algorithme. On fixe les
relations entre des éléments. On donne certains éléments, le système (dit expert) trouve tous ceux
qui leur sont liés par ces relations. Pour y parvenir, l’ordinateur doit être muni d’un programme
complexe appelé moteur d’inférence qui cherche quelles règles appliquer et, au besoin, quelles
variables supplémentaires mettre en jeu. Il relève des théories de la démonstration automatique.
Exemple de langage logique : Prolog, …
¾ les booléens (qui n'ont que deux valeurs possibles, 0 ou 1 ou encore VRAI ou FAUX)
¾ les dates (dates : jour, mois, année et heures sous plusieurs formats)
NB : On trouve quelques fois un autre type : le type monétaire pour les valeurs monétaires.
De plus, il est également possible pour le Programmeur de créer des types de données
personnalisés.
Les données d'un programme doivent être récupérées en mémoire centrale, à partir du
clavier ou d'un fichier par exemple, pour pouvoir être traitées par le processeur qui exécute le
programme. Ainsi, toutes les données d'un programme sont mémorisées en mémoire centrale, dans
des sortes de cases que l'on appelle variables.
Une variable peut être représentée par une case mémoire, qui contient la valeur d'une
donnée. Chaque variable possède un nom unique appelé identificateur par lequel on
peut accéder à son contenu.
Par exemple, on peut avoir en mémoire une variable Prix et une variable Quantité qui
contiennent respectivement les valeurs 1000 et 15 :
1000 15
Prix Quantité
Attention à ne pas confondre la variable et son contenu : une variable est un contenant,
c'est à dire une sorte de boîte, alors que le contenu d'une variable est une valeur correspond à un
type de données (numérique, alphanumérique, booléenne, …)
Deux variables peuvent avoir la même valeur, mais une variable ne peut pas avoir plusieurs
valeurs en même temps. En revanche, la valeur d'une variable peut varier au cours du programme.
L'ancienne valeur est tout simplement écrasée et remplacée par la nouvelle.
Pour qu'un programme puisse utiliser une variable, il faut au préalable que cette variable
ait été déclarée, c'est-à-dire que le programme lui ait réservé une place en mémoire et ait attribué
l'identificateur à cette place. Mais toutes les variables n'ont pas besoin de la même place en
mémoire. Un grand nombre prend plus de place qu'un caractère. Selon le type de l'objet, il faudra
lui réserver plus ou moins de place: c'est pourquoi il faut déclarer le type des variables et pas
seulement leur nom. Par ailleurs, selon le type des variables, les opérations possibles seront
différentes.
Ainsi, pour déclarer une variable, on utilise le mot clé « Variable » ou « VAR » et on indique :
¾ son identificateur (son nom)
¾ son type (sa taille)
La syntaxe de déclaration de variables en algorithmique est donc :
Variable <liste d’identificateurs séparés par des virgules> : type
Exemples :
Variables Nombre1, nombre2: entiers
Sexe : caractère
Nom, prénoms : chaînes de caractères
L’instruction qui permet d’attribuer une valeur à une variable ou modifier cette valeur, est
l’affectation. En algorithmique, cette instruction se note avec le signe Å. Par exemple : nÅ2.
Remarques : Un identificateur peut être composé de lettres et de chiffres mais il ne peut pas
commencer par un chiffre et ne peut comporter d'espaces. L'identificateur des variables doit être
suffisamment signifiant pour qu'on reconnaisse leur fonction aisément. Par exemple pour des
variables représentant un prix et une quantité, évitez a et b mais utilisez plutôt Prix et Quantité.
Les variables dont la valeur ne change pas au cours de l'exécution du programme sont
appelées variables constantes ou plus simplement constantes. Les constances sont donc des
entités dont la valeur est figée. Une constante a également un type et une valeur.
On distingue deux types de constantes :
¾ Constante implicite : ce sont les valeurs de faite. Par exemple : 10, ”toto”, ’a’
¾ Constante explicite : ce sont les constantes définies explicitement. Par exemple :
LIMITEVAL (une valeur limite donnée), DATEANNIV (une date d’anniversaire)
o Déclaration de constantes :
Pour déclarer une constante, on utilise le mot clé « Constante » ou « CONST » et on indique :
¾ son identificateur (son nom)
¾ son type (facultatif)
¾ et sa valeur
La syntaxe de déclaration de constantes en algorithmique est donc :
Constante (<identificateur : type>) Å valeur
Exemples :
Constantes (TVA : entier) Å 18%
(DateNoel : date) Å ’25-12-2006’
Un opérateur est un symbole / mot qui permet d’associer deux ou plusieurs opérandes (variables
ou constantes) pour former une expression. Une expression a un donc un type et une valeur.
Voici une liste non-exhaustive des opérateurs utilisés en algorithmique, par type de données :
Un algorithme est donc un ensemble fini d’instructions séparées généralement par des
points-virgules ou des retours-chariots (retours à la ligne).
Les informations (données) manipulées par les instructions peuvent prendre plusieurs formes :
¾ des variables proprement dites
¾ des constantes explicites
¾ des constantes implicites (valeurs littérales écrites telles qu'elles dans le programme)
¾ des messages (libellés) destinés à l'utilisateur (quelles sont les données à entrer, quels
résultats sont affichés…), qui sont des valeurs littérales particulières
¾ des expressions complexes (combinaisons de variables, constantes et valeurs littérales)
avec des opérateurs. Exemple : 2 * rayon * 3.14
ALGORITHME NomAlgo
// Déclarations de variables et constantes
Au début d’un programme, les variables n’ont pas encore reçues de valeur; on dit qu’elles
sont indéfinies. Toutes les variables doivent être initialisées, c’est-à-dire recevoir une valeur
initiale, avant leur utilisation. Sinon, lorsqu’on utilise une variable indéfinie (qui n’a pas encore
reçue de valeur), le comportement du programme peut être aléatoire.
De là vient la nécessité de toujours bien initialiser les variables avant d’utiliser leur valeur.
L’initialisation s’effectue généralement au début du programme, juste après les déclarations. Elle
prend la forme d'une simple affectation ou d’une saisie au clavier. Pendant ou à la fin de
l’exécution du programme il s’avère souvent nécessaire d’afficher à l’écran le contenu des
variables ou expressions dérivées.
La saisie d'une variable au clavier permet, comme l'initialisation d'affecter une valeur
initiale à une variable : donc les variables saisies n'ont pas à être initialisées mais elles doivent
être déclarées au préalable pour préciser à quel type correspond la donnée saisie.
¾ Syntaxe :
Saisir variable1 [, variables2, …]
¾ Exemples :
Saisir x
Saisir x, y
(Lit la 1ère valeur saisie au clavier et l'affecte à x, puis lit la 2ème saisie et l'affecte à y)
¾ Utilité de la saisie :
On pourrait être tenté de dire que l’instruction de saisie est inutile car on dispose déjà un
moyen d’attribuer une valeur aux variables, par l’instruction d’affectation. Mais en fait,
l’instruction de saisie (ou de lecture sur un périphérique autre que le clavier) est indispensable
pour permettre d’utiliser le même programme sur des données différentes sans avoir à changer les
valeurs du programme à chaque fois.
L’un des avantages de l’instruction de saisie, c’est que le choix de la valeur se fait en cours
d’exécution du programme. On peut donc utiliser le programme autant de fois que l’on veut avec
des données différentes sans avoir à modifier le programme.
¾ Syntaxe :
Afficher expression1, [expression2, …]
¾ Exemples :
Afficher Nom
(Permet d'afficher la valeur de la variable Nom à l'écran. Si Nom est une chaîne qui
vaut "toto", cette instruction affichera toto à l'écran)
Afficher "Bonjour!"
Afficher A, B
(Quand on veut afficher deux objets à la suite, on les sépare d'une virgule)
¾ Exemple :
Afficher "Voici les résultats : x vaut ", x, " et y vaut", y
Nous aurons donc à l’écran (en supposant que les valeurs de x et y sont
respectivement 5 et 10) : Voici les résultats : x vaut 5 et y vaut 10
Si condition Alors
Séquence d'instructions 1
Sinon
Séquence d'instructions 2
Finsi
Où condition est une expression logique ou booléenne dont la valeur conditionne le choix
d'un des deux ensembles d'instructions. Cette condition peut être soit vraie soit fausse. Si
l'expression est vraie, la première séquence d'instruction sera exécutée et la seconde sera ignorée;
si l'expression est fausse, seule la seconde séquence d'instructions sera exécutée.
Dans certains cas, lorsque la condition est fausse, aucune instruction ne doit être exécutée.
La structure alternative s'exprime alors plus simplement sous la forme:
Si condition Alors
Séquence d'instructions
Finsi
Remarques :
o Quelle que soit la séquence choisie et exécutée, les instructions qui suivent Finsi seront
exécutées.
o Chacune des séquences d'instructions d'un Si ... Finsi peut contenir des Si...Finsi à son tour.
On dit alors que les structures sont imbriquées.
o Pour faire apparaître plus clairement la structure, on écrit les séquences d'instructions
légèrement en retrait des mots-clefs (Si, Alors, Sinon, Finsi). On dit qu'on indente le texte de
l'algorithme.
SelonQue identificateur
valeur1 : séquence d'instructions 1
valeur2 : séquence d'instructions 2
…
intervallevaleuri : séquence d'instructions i
…
valeurn : séquence d'instructions n
autre : séquence d'instructions par défaut
Finselon
¾ Exemple1 :
SelonQue mois
1 : Afficher "Janvier"
2 : Afficher "Février"
3 : Afficher "Mars"
4 : Afficher "Avril"
5: Afficher "Mai"
6 : Afficher "Juin"
7 : Afficher "Juillet"
8 : Afficher "Aout"
9 : Afficher "Septembre"
10 : Afficher "Octobre"
11: Afficher "Novembre"
12: Afficher "Décembre"
autre : Afficher "Un numéro de mois doit être compris entre 1 et 12"
Finselon
¾ Exemple2:
SelonQue montant
<1000 : TauxTVAÅ1
≥1000 et < 3000: TauxTVAÅ2
≥3000 et < 10000: TauxTVAÅ3
≥ 10000: TauxTVAÅ4
autre : TauxTVAÅ5
FinSelon
1. La boucle « Pour »
Les mots Faire et Finpour encadrent les instructions qui doivent être exécutées plusieurs
fois. On précise entre Pour et Faire comment seront contrôlées les répétitions. On y définit une
variable appelée variable de contrôle ou compteur et les valeurs que prendra cette variable :
une première valeur ou valeur initiale indiquée après le symbole d’affectation, une dernière valeur
ou valeur finale indiquée après le mot à. Ces valeurs doivent être de type numérique.
SommeÅ0
Pour iÅ0 à 20 pasde 2 Faire
SommeÅsomme+i
Finpour
Afficher(‘’la somme vaut’’,Somme)
En premier lieu, la condition, exprimée sous la forme d’une expression logique, est évaluée
(il faut donc veiller à initialiser sa valeur avant l'entrée dans la boucle): si sa valeur est vrai, le
corps de la boucle est exécuté puis la condition est réévaluée (il faut donc qu'elle puisse changer de
valeur pour sortir de la boucle) et si elle a la valeur faux, on sort de la boucle et on exécute
l'instruction qui suit Fintantque. Il peut arriver que la boucle ne soit jamais du tout exécutée.
Il est à noter que parfois, il est plus simple de déterminer les raisons d'arrêter le processus
répétitif que celles de continuer. Dans ce cas, il est préférable d'exprimer la condition sous la forme
NOT (négation de la condition d'arrêt). La forme de ce type de boucle devient alors :
SommeÅ0
iÅ0
TantQue ( i <= 20) Faire
SommeÅsomme+i
iÅi+2
Fintantque
Afficher(‘’la somme vaut’’,Somme)
Comme la boucle TantQue, la boucle Répéter est une boucle répétitive utilisée lorsque le
nombre de fois que la séquence d'instructions doit être exécutée est inconnu au moment où cette
séquence est abordée pour la première fois mais le corps de la boucle est toujours exécuté au
moins une fois. La syntaxe de cette boucle est :
Répéter
Séquence d’instructions
Jusqua condition
Où condition est une expression logique qui exprime les raisons d'arrêter la boucle. Cette
expression est évaluée après l'exécution du corps de la boucle : si sa valeur est faux, le corps de la
boucle est exécuté à nouveau puis la condition est réévaluée (il faut donc qu'elle puisse changer de
valeur pour sortir de la boucle) et si elle a la valeur vrai, on exécute l'instruction qui suit Jusqua.
¾ ATTENTION : Si ceci correspond tout à fait à l'usage familier que nous faisons de
l'expression Répéter ... jusqu'à, le test effectué ici est la négation de celui utilisé dans la
boucle TantQue et ceci peut parfois prêter à confusion.
SommeÅ0
iÅ0
Répéter
SommeÅsomme+i
iÅi+2
Jusqua (i > 20)
Afficher(‘’la somme vaut’’,Somme)
¾ REMARQUES : La boucle Pour est dite « déterministe » car le nombre d’itérations est
connu à l’avance, tandis que les boucles TantQue et Répéter sont dites « non
déterministes » car leur nombre d’itérations n’est pas connu à l’avance.
o Enoncé :
Etant donnés deux nombres entiers m et n positifs ou nuls, on veut en calculer le PGCD.
L'algorithme d'Euclide permet de résoudre ce problème en prenant d'abord le reste de la division
de m par n, puis le reste de la division de n par ce premier reste, etc jusqu'à ce qu'on trouve un
reste nul. Le dernier diviseur utilisé est le PGCD de m et n.
o Résolution pratique :
Pour m=1386 et n=140, on a successivement:
1386 = 140 * 9 + 126
140 = 126 * 1 + 14
126 = 14 * 9 + 0
Et le PGCD de 1386 et 126 est bien 14.
Remarquons que par définition, si un des nombres est nul, l'autre nombre est le PGCD.
Si nous avions pris m=140 et n=1386, nous aurions obtenu la suite de calculs suivants:
140 = 1386 * 0 + 140
1386 = 140 * 9 + 126
140 = 126 * 1 + 14
126 = 14 * 9 + 0
Et le PGCD est le même. L'ordre de m et n n'a donc pas d'importance.
o Ecriture de l’Algorithme :
Notre algorithme doit répéter le calcul du reste de la division d'un nombre par un autre.
Pour cela, appelons a le dividende, b le diviseur et r le reste. Le calcul du reste de la division de a
par b se fait simplement au moyen des instructions de l'algorithme suivant :
Algorithme EuclidePGCD:
Variables m,n,a,b,r,PGCD : entier
DEBUT
Saisir m, n
aÅm
bÅn
TantQue NOT(b= 0) Faire
r Å a mod b
a Åb
bÅr
Fintantque
PGCD Åa
Afficher "Le PGCD de",m,"et",n,"est",PGCD
FIN
En fait, on décompose l’algorithme en plusieurs parties plus petites qu'on assemble ensuite
pour former l'application finale. Les procédures et les fonctions permettent donc de découper un
gros algorithme en morceaux plus petits et relativement plus simples à coder et à comprendre, et
permettent surtout d'éviter de répéter plusieurs morceaux de code identiques. On parle de
programmation modulaire.
Comme un algorithme, une procédure ou une fonction possède un nom, des variables, des
instructions, un début et une fin. Mais contrairement à un algorithme, une procédure ou une
fonction n’est pas destinée à s'exécuter indépendamment d'un autre algorithme.
En effet, l'exécution d’une procédure ou une fonction sera demandée par un algorithme
principal ou par une autre procédure ou fonction grâce à une instruction spécifique qu'on nomme
APPEL. Cet algorithme principal contient l'ensemble des instructions prévues pour s'exécuter au
lancement de l'application et c’est lui qui se chargera de demander l'exécution des instructions se
trouvant dans les procédures et fonctions.
La déclaration d’une procédure se fait par le mot clé « procédure » suivi du nom de la
procédure suivi de la déclaration des paramètres formels de la procédure mis entre parenthèses et
séparés par des virgules. Ensuite, suit on déclare les variables «locales» de la procédure puis on
place les instructions de la procédure entre début et fin.
Les paramètres sont des variables déclarées dans l'en-tête d’une procédure ou une fonction,
avec leur type (entier, chaîne,…) et leur nature (donnée en entrée / résultat en sortie), dont les
valeurs sont fournies soit en entrée soit en sortie. On parle de paramètres formels.
Les procédures admettent trois types de paramètre formels. On préfixe donc le paramètre
formel dans la déclaration par :
o Entrée ou E pour un passage de paramètre en entrée : dans ce cas, le paramètre constitue
une donnée pour la procédure et toutes les modifications effectuées sur ce paramètre dans
la procédure ne seront pas effectives pour le reste de l’algorithme
o Sortie ou S pour un passage de paramètre en sortie : dans ce cas, le paramètre constitue un
résultat pour la procédure et ne possède pas de valeur initiale. La première instruction de la
procédure qui utilise ce paramètre doit donc l'initialiser. Toutes les modifications effectuées
ensuite sur ce paramètre dans la procédure perdurent après l'appel de la procédure.
o Entrée/Sortie ou E/S pour un passage de paramètre en entrée/sortie : dans ce cas, le
paramètre constitue à la fois une donnée et un résultat. Il possède une valeur initiale et
toutes les modifications effectuées sur lui perdurent après l'appel de la procédure.
Une procédure peut aussi ne pas nécessiter de paramètre. Dans ce cas, la liste des paramètres
reste vide. Par défaut, les paramètres d'une procédure sont en Entrée.
Lorsqu’on appelle une procédure dans un algorithme, on remplace chaque paramètre formel
par une valeur nommée paramètre effectif ou argument en suivant l’ordre de déclaration des
paramètres formels. On nomme passage de paramètre, l’association de valeur qui est utilisée
entre le paramètre effectif et le paramètre formel.
L'appel d'une procédure représente une instruction à part entière dont la syntaxe est :
NomProcedure(valeur_argument1,valeur_argument2,…,valeur_argumentn)
Où NomProcedure représente le nom de la procédure appelée et valeur_argumenti
correspond au paramètre effectif associé au ième paramètre formel.
¾ REMARQUES :
o Le nombre d'arguments doit correspondre au nombre de paramètres formels.
o Le type du ième argument doit correspondre au type du ième paramètre.
o Le ième argument a, de préférence, un nom différent de celui du ième paramètre.
o Les arguments qui correspondent à des paramètres en entrée peuvent être des
valeurs littérales ou des variables ayant des valeurs au moment de l’appel tandis
que ceux qui correspondent à des paramètres en sortie ou en entrée/sortie doivent
obligatoirement être des variables.
o Un argument omis sera remplacé par sa valeur par défaut dans la procédure.
Algorithme Exemple1
Variables
X, Y : Entier
Début Lors de l’appel d’une procédure, il faut veiller
Afficher ''Entrez le premier nombre'' à fournir des arguments du même type et dans
Saisir X le même ordre que les paramètres de la
Afficher'' Entrez le deuxième nombre '' procédure. Ici, les arguments X et Y de type
Saisir Y entiers remplaceront les paramètres formels A
Somme(X,Y) et B de même type dans la procédure.
Fin
Une fonction est une suite d’instructions considérée comme un sous-algorithme et destinée
à réaliser une ou plusieurs actions, qui retourne obligatoirement et explicitement une valeur
(résultat) à l’algorithme appelant. Elle peut aussi prendre en arguments des paramètres pour les
utiliser dans ses calculs.
Une fonction est une procédure particulière, qui ne génère qu’un et un seul résultat
généralement de type simple : Entier, réel, booléen, caractère.
La déclaration d’une fonction se fait par le mot clé « fonction » suivi du nom de la fonction,
suivi de la déclaration des paramètres formels de la fonction mis entre parenthèses et séparés par
des virgules, suivi du type de la valeur retournée. Ensuite, suit on déclare les variables «locales» de
la fonction puis on place les instructions de la fonction entre début et fin.
¾ Remarques :
o L’instruction RETOURNER permet de renvoyer comme résultat de la fonction la
valeur ou le résultat de l’expression placée à la suite.
o Cette instruction peut aussi être remplacée par une instruction d’affectation de la
valeur du résultat au nom de la fonction : NomFonction Å Expression ou Valeur
o Une fonction retourne obligatoirement une et une seule valeur résultat à l’appelant.
o Ne jamais oublier de préciser le type du résultat dans l’entête de la fonction.
Seul le passage de paramètres en Entrée est autorisé pour les fonctions. Ces paramètres
constituent les données pour la fonction et toutes les modifications effectuées sur ces paramètres
dans la fonction ne seront pas effectives pour le reste de l’algorithme.
NomVariable=NomFonction(valeur_argument1,…,valeur_argumentn)
En effet, l'appel d'une fonction est remplacé à l'exécution par la valeur qu’elle retourne.
¾ REMARQUES :
Algorithme AppelFonction
Variables
A, C : Reel
B : Entier L’appel d’une fonction intervient dans
Début
une instruction dans laquelle le résultat
Afficher ''Entrez le premier nombre''
de la fonction peut être directement
Saisir A
utilisé. Ici, la fonction PUISSANCE est
Afficher '' Entrez la puissance voulue "
Saisir B
appelée dans une instruction d’affectation
C Å PUISSANCE(A,B) et son résultat est affecté à la variable C.
Afficher(A,‘’a la puissance’’,B,’’vaut’’,C)
Fin
¾ Remarque :
Il existe dans tous les langages de programmation des fonctions standard appelées
fonctions prédéfinies du langage. Ces fonctions peuvent être utilisées directement en
programmation sans que l’on ait besoin de les recoder.
Une fonction récursive est une fonction qui s'appelle elle-même. Ce genre de fonction est
utile pour résoudre des problèmes faisant intervenir une suite finie de cas.
Une fonction récursive découpe le problème en morceaux plus petits et s'appelle elle-même
pour résoudre chacun des plus petits morceaux du problème. Les fonctions récursives permettent
de résoudre des problèmes généralement complexes à résoudre avec des fonctions ordinaires.
Les fonctions récursives sont souvent plus gourmandes en ressources. C’est pourquoi
certaines règles sont à respecter pour mettre en place de telles fonctions.
o 3- Le nombre d'appel récursif ne doit pas être très grand : sinon, il pourrait
également survenir un dépassement de la capacité mémoire ('StackOverflow').
o 5- Il faut limiter les appels à une seule fonction récursive en même temps
afin d’éviter le phénomène des fonctions récursives imbriquées.
On voit bien que la fonction Factorielle s’appelle elle-même : c'est une fonction récursive.
Rappelez-vous de l’Algorithme d’Euclide que nous avons étudié au chapitre 4 (cf. Page 17).
On se rend compte avec la méthode utilisée que, pour deux nombres entiers positifs et non
nuls M et N, on obtient :
Il s’agit donc d’un cas de récursivité. La fonction récursive peut s’écrire ainsi :
o Si une procédure ou une fonction écrite n'est jamais appelée, elle ne sera jamais
exécutée. Il faut donc l’appeler explicitement au moins une fois.
o Utilisez une fonction pour retourner un résultat si le résultat correspond à une seule
valeur qui doit être communiquée à l’appelant au lieu d'être affiché.
o Utilisez une procédure avec des paramètres en sortie pour retourner un résultat si
plusieurs variables forment le résultat.
L'endroit où est déclarée une variable est très important car il détermine dans quelles
procédures et fonctions cette variable pourra être utilisée.
Une variable déclarée à l'intérieur d'une procédure ou d’une fonction est appelée variable locale.
Elle n'est utilisable que dans la procédure ou fonction où elle a été déclarée. Ceci est aussi valable
pour l’algorithme principal : une variable déclarée dans l’algorithme principal n'est utilisable que
dans l’algorithme principal et pas dans les autres procédures et fonctions.
Une variable déclarée à l'extérieur de l’algorithme principal et des procédures et fonctions est
appelée variable globale. Elle est commune à l'ensemble des procédures et fonctions et
l’algorithme principal. Elle est donc utilisable partout.
Le même nom de variable peut désigner plusieurs variables locales différentes. En effet,
rien n'interdit que plusieurs procédures et fonctions utilisent le même nom de variable en local. La
valeur d’une variable locale X dans une procédure donnée n'aura pas d'influence sur la valeur de X
dans une autre procédure, car X correspond à deux emplacements de la mémoire distincts.
En revanche, une variable globale n'existe qu'en un seul exemplaire pour toutes les
procédures et fonctions. Ainsi, chaque fois qu'une procédure ou fonction modifie une variable
globale, sa modification sera visible par toutes les autres procédures ou fonctions. Toutes les
procédures et fonctions peuvent utiliser et éventuellement modifier les variables globales.
Mais ne jamais appeler une variable locale par le même nom qu'une variable globale.
¾ Exemple :
Algorithme Principal
Début
// Utilisation des variables globales
Afficher "Saisir deux nombres A et B"
Saisir A, B
Moyenne Å CalculMoyenne(A,B)
Afficher ‘’la moyenne Arithmétique de’’, A, ‘’et’’, B, ‘’vaut’’, Moyenne
Fin
Un tableau est une structure de données permettant de mémoriser des valeurs de même
type ou de même nature, chacune étant repérée par un ou plusieurs indices indiquant sa position.
Les indices sont des entiers variant entre une valeur minimale et une valeur maximale.
Généralement, un tableau est représenté par un ensemble de cases contenant chacune une valeur.
Ces cases, encore appelées éléments du tableau, sont qualifiées de variables indicées.
A la différence des variables classiques, qui sont déclarées individuellement avec un nom distinct,
les variables indicées constituant le tableau sont implicitement déclarées lors de la déclaration du
tableau. Un tableau constitue donc un ensemble de variables indicées, toutes du même type,
chaque composante étant repérée par ses indices. Chacune de ces variables indicées se comporte
comme une variable classique (celles que nous avons manipulées jusqu'à présent) : on peut saisir
ou afficher sa valeur, utiliser sa valeur dans une expression ou lui affecter une valeur.
En général, on peut déclarer ou utiliser les tableaux de deux manières, en tant que :
o Variables Tableau, pour les tableaux dont les valeurs peuvent changer.
o Constantes Tableau, pour les tableaux dont les valeurs ne peuvent pas changer.
Pour déclarer un tableau, on déclare une variable ou constante unique, dite composite,
possédant un nom et un nombre d'éléments qui représentent la taille du tableau. Ainsi, chaque
élément d’un tableau sera identifié par le nom du tableau suivi des indices entre crochets.
On distingue des tableaux à taille fixe appelés tableaux statiques ainsi que des tableaux
à taille variables appelés tableaux dynamiques.
Un tableau à une dimension est un tableau dont chaque valeur peut être identifiée par un
seul indice. Un tel tableau est appelé Vecteur et correspond à un ensemble de cases rangées en
ligne et contenant une liste d’éléments du même type.
Remarque : On peut aussi utiliser un autre indice minimum (par exemple, 0 comme dans
certains langages). Dans ce cas, l'indice maximum sera égal au nombre d'éléments –1.
18 12,5 16 10 08 11 17 14,5 12 15
indices 1(minimum) …. 10(maximum)
Pour désigner un élément, on indique le nom du tableau suivi de l’indice entre crochets.
Ainsi, Notes[3] représente ici le 3ième élément du tableau Notes et vaut 16.
Il est parfois utile de déclarer un tableau en tant que constante, c’est-à-dire un tableau dont
les valeurs ne changent pas. Dans ce cas, comme pour les autres constantes, il faut initialiser les
valeurs du tableau dès la déclaration. Pour cela, on utilise une liste d’initialisation entre accolades.
La syntaxe de déclaration d'une constante tableau à une dimension est donc la suivante :
Où Identificateur est le nom du tableau, indice_min et indice_max sont les valeurs minimales
et maximales des indices du tableau, et valeur1, valeurn les valeurs des éléments.
De même, si on considère que l'indice minimum vaut 1 (par défaut en Algo), il n’est
pas nécessaire d’indiquer cette valeur et la déclaration du tableau, peut alors être simplifiée ainsi :
TablMois:tableau[1..12]Å{"janvier","février","mars","avril","mai","juin","juillet","aôut",
"septembre","octobre","novembre","décembre"}
Un tableau multidimensionnel est un tableau dont chaque valeur doit être identifiée par
plusieurs indices. Pour le cas de 2 indices, on parle de tableaux à deux dimensions ou
Matrices. On les représente par une grille dont les cases sont rangées en ligne et en colonne.
En règle générale, les indices minimum valent 1 (par défaut en Algo), et la taille du
tableau est égale au produit des valeurs des indices maximum. Dans ce cas, il n’est pas
nécessaire d’indiquer les indices minimum et la déclaration peut alors être simplifiée ainsi :
o Pour créer un tableau devant contenir 5 notes par matière pour 3 matières:
18 12,5 16 10 08
15 13 17 12 18
09 12 14,5 15 13,5
Pour désigner un élément, on indique le nom du tableau suivi des indices entre crochets.
Ainsi, Notes2[3,4] représente ici la 4ème note de la 3ème matière et vaut 15.
En règle générale, les indices minimum valent 1 (par défaut en Algo), et la taille du
tableau est égale au produit des valeurs des indices maximum. Dans ce cas, il n’est pas
nécessaire d’indiquer les indices minimum et la déclaration peut alors être simplifiée ainsi :
¾ Tableaux dynamiques :
Les tableaux que nous venons d’étudier ont une taille prédéfinie à l’avance à travers les
bornes du tableau que sont les indices minimales et maximales : ce sont des tableaux statiques.
Toutefois, il peut arriver que l’on ne connaisse pas à l’avance le nombre d’éléments que
devra comporter un tableau. Certes, une solution consisterait à déclarer un tableau gigantesque.
Mais d’une part, on n’en sera jamais parfaitement sûr, d’autre part, en raison de l’immensité de la
place mémoire réservée et la plupart du temps non utilisée, ce serait un gâchis préjudiciable à la
rapidité, voire à la viabilité de notre algorithme.
Aussi, pour parer à ce genre de situation, a-t-on la possibilité de déclarer un tableau sans
préciser au départ son nombre d’éléments. Ce qui revient à ne pas figer les bornes des indices du
tableau. On parle alors de tableaux dynamiques.
Pour ce genre de tableaux, la taille n’est pas prédéfinie et peut donc varier en cours
d’utilisation. Ainsi, pour modifier dynamiquement la taille d’un tableau après l’avoir déclaré, cela
est possible en Algorithmique grâce à l’instruction Redimensionner notée généralement Redim,
qui permet de redimensionner un tableau afin d’augmenter sa taille.
Pour créer un tableau dynamique, il suffit simplement de ne pas préciser lors de sa
déclaration les bornes minimales et maximales de ses indices. Ainsi, on fur et à mesure qu’on
ajoutera des éléments au tableau dynamique, l’indice correspondant au dernier élément ajouté
sera considéré comme l’indice maximum.
L’accès aux éléments d’un tableau dynamique est identique à celui des tableaux statiques.
Les tableaux dynamiques sont la solution minimisant la place occupée en mémoire pour de
gros ensembles de données car seule la place nécessaire est réservée.
La plupart du temps, les valeurs contenues dans un tableau sont rangées dans un ordre
quelconque qui ne correspond pas à l’ordre de grandeur exact de ces valeurs. On dit que le tableau
est non trié. Le tri d’un tableau consiste donc à ranger les valeurs du tableau dans le bon ordre de
grandeur, soit de la plus petite à la plus grande valeur (tri dans l’ordre croissant), soit de la plus
grande à plus petite valeur (tri dans l’ordre décroissant).
Il existe plusieurs méthodes classiques de tri des tableaux. A chacune de ces méthodes
correspond généralement un ou plusieurs algorithmes.
Pour décrire plus précisément les différents algorithmes de tri, leur procédure et leur
complexité, nous supposerons dans tout ce qui suit, que l'on dispose d'un tableau à une
dimension de N entiers numérotés de 1 à N et qu'on cherche à trier les entiers dans
l'ordre croissant, c’est-à-dire « de gauche à droite ». Mais, ces algorithmes pourraient
éventuellement aussi s’appliquer à d’autres types de tableaux et vous pourrez aussi les transcrire
aisément dans n’importe quel langage de programmation.
Ces algorithmes sont basés sur des méthodes qui trient les éléments du tableau deux à
deux, de manière plus ou moins efficace, mais qui nécessitent toujours de comparer chacun des
N éléments avec chacun des N-1 autres éléments. Le nombre de comparaisons sera donc de
l'ordre de N2 - on note cet ordre de grandeur O(N2). Par exemple, pour N=1000, N2=106, pour
N=106, N2=1012.
L'algorithme se déroule ainsi : les deux premiers éléments du tableau sont comparés, si le
premier élément est supérieur au second, une permutation est effectuée. Ensuite, sont comparés et
éventuellement permutés les valeurs 2 et 3, 3 et 4 jusque (n-1) et n. Une fois cette étape achevée, il
est certain après le premier parcours, que le dernier élément du tableau T[N] est le plus grand. Il
reste donc à appliquer la même procédure sur le tableau composé des éléments T[1] à tab[N-1].
Ainsi, l'algorithme reprend donc pour classer les (n-1) éléments qui précédent. L'algorithme se
termine quand il n'y a plus de permutations possibles.
Pour classer les n valeurs du tableau, il faut effectuer au maximum n itérations. La
complexité de cet algorithme est donc en O(N2), c'est-à-dire du même ordre de grandeur que le
carré du nombre d'éléments.
Cet algorithme porte le nom de tri bulle car, petit à petit, les plus grands éléments du
tableau remontent, par le jeu des permutations, en fin de tableau. Idem dans un aquarium : les
plus grosses bulles remontent plus rapidement à la surface que les petites.
L'algorithme du Tri par Sélection ou Tri par minimum successif consiste à trouver
dans le tableau l'élément le plus petit, puis à l’échanger avec l’élément à la première place. On
réitère ensuite avec le tableau à partir de la deuxième position, puis avec le tableau à partir de la
troisième position, et ainsi de suite, selon l’illustration suivante :
Le principe de cet algorithme est donc le suivant : Il faut rechercher l’indice (noté min) de
l’élément tel que, quelque soit k, T[k] >= T[min]. Une fois cet indice trouvé, les éléments T[1] et
T[min] sont permutés (on utilisera une variable temporaire de type entier) puis la même procédure
est appliquée sur la suite d'éléments tab[2], ..., tab[N]. Ainsi, chaque élément du tableau sera à sa
place définitive au bout de n-1 itérations. C'est la méthode la plus facile à retenir et à programmer.
L’Algorithme de Tri par Insertion consiste à piocher une à une les valeurs du tableau et à
les insérer, au bon endroit, dans le tableau trié constitué des valeurs précédemment piochées et
triées. Les valeurs sont piochées dans l'ordre où elles apparaissent dans le tableau.
Le principe de cet algorithme est le suivant : soit p l'indice de la valeur piochée, les (p-1)
premières valeurs du tableau constituent le tableau trié dans lequel va être inséré la pième valeur.
Au début de l'algorithme, on considère que la liste constituée du seul premier élément est trié :
c'est vrai puisque cette liste ne comporte qu'un seul élément. Ensuite, on insère le second élément
(p=2), puis le troisième (p=3)... Ainsi, p varie de 2 à n, où n est le nombre d'éléments du tableau.
Cette méthode de tri est très différente de la méthode de tri par sélection et s'apparente à
celle utilisée pour trier ses cartes dans un jeu : on prend une carte, T[1], puis la deuxième, T[2],
que l'on place en fonction de la première, ensuite la troisième T[3] que l'on insère à sa place en
fonction des deux premières et ainsi de suite. Pour placer T[i], on va utiliser une variable
intermédiaire temp pour conserver sa valeur qu'on compare successivement à chaque élément T[i-
1],T[i-2],... qu'on déplace vers la droite tant que sa valeur est supérieure à celle de temp. On affecte
alors à l'emplacement dans le tableau laissé libre par ce décalage la valeur de temp.
Le problème de cet algorithme est qu'il faut parcourir le tableau trié pour savoir à quel
endroit insérer le nouvel élément, puis décaler d'une case toutes les valeurs supérieures à
l'élément à insérer. En pratique, les éléments sont décalés vers la droite tant que l'élément à
insérer est plus petit qu'eux. Dés que l'élément à insérer est plus grand qu'un des éléments du
tableau trié, il n'y a plus de décalage, et l'élément est inséré dans la case laissée vacante par les
éléments qui ont été décalés. Contrairement aux tris par sélection et bulle qui nécessitent un
nombre constant de comparaisons, la complexité du tri par insertion est plus fortement
dépendante de l'ordre du tableau initial le tri par insertion ne conduit qu'à très peu de
comparaisons lorsque le tableau initial est presque en ordre. Ce tri a donc de meilleures propriétés.
Les algorithmes de tris récursifs sont basés sur le principe de la dichotomie (du grec dicho =
deux - tomie = couper) également appelé "diviser pour régner" :
Ce sont des méthodes de tri plus rapides, car elles trient des sous-ensembles d’éléments
puis regroupent les éléments triés. Le nombre de comparaisons est alors de l'ordre de N (log(N)).
Par exemple, pour N=1000, N(log(N))=10000 environ, pour N=106, N(log(N))=20x106 environ.
Cette méthode de tri est probablement la plus utilisée actuellement. Elle a été inventée par
Sir Charles Antony Richard Hoare en 1960. Elle illustre le principe dit « diviser pour régner », qui
consiste à appliquer récursivement une méthode destinée à un problème de taille donnée à des
sous-problèmes similaires, mais de taille inférieure. Ce principe général produit des algorithmes
qui permettent souvent d'importantes réductions de complexité.
On considère un élément au hasard dans le tableau appelé le pivot, dont on affecte la
valeur à une variable, disons pivot. On procède alors à une partition du tableau en 2 zones : les
éléments inférieurs ou égaux à pivot et les éléments supérieurs ou égaux à pivot. Si on parvient à
mettre les éléments plus petits en tête du tableau et les éléments plus grands en queue de tableau,
alors on peut placer la valeur de pivot à sa place définitive, entre les deux zones. On répète ensuite
récursivement la procédure sur chacune des partitions créées jusqu'à ce qu'elle soit réduite à un
ensemble à un seul élément.
La partie du tri la plus sensible reste le choix du pivot. Dans la plupart du temps, il est
choisi au hasard parmi les éléments du tableau, mais ce choix peut se révéler catastrophique : si le
pivot est à chaque choix le plus petit élément du tableau, alors le tri rapide dégénère en tri par
sélection. On montre que la complexité de ce tri est :
• dans le meilleur des cas, en O (N log N) ;
• en moyenne, en O (N log N) ;
• dans le pire des cas, en O (N2).
Il existe bon nombre d'astuces pour optimiser le tri rapide, ce qui rend finalement cette
méthode la plus rapide en moyenne parmi toutes celles utilisées.
Cette méthode de Tri est également basée sur le principe du « diviser pour régner ». En
effet, étant données deux suites d'éléments triés, de longueurs respectives L1 et L2, il est très
facile d'obtenir une troisième suite d'éléments triés de longueur L1 + L2, par fusion des deux
précédentes suites.
On constate que la procédure de fusion nécessite un tableau intermédiaire aussi grand que
le nombre d'éléments à interclasser. C'est là où réside le principal inconvénient du tri fusion, car si
sa complexité dans tous les cas est en O (N log N), c'est au prix d'un tableau annexe aussi grand
que le tableau initial, ce qui peut se révéler handicapant dans les situations où la place mémoire
est restreinte.