Vous êtes sur la page 1sur 112

ALGORITHMIQUE

FONDAMENTALE
et TEST
AFGT

INTRODUCTION

Valérie-Anne Nicolas Licence Informatique S4


1
MOTIVATION :
RESOUDRE UN PROBLEME
Problème particulier Problème générique
Modèles de
+ + données
règles de résolution ad-hoc règles de résolution génériques
= Algorithmes
=
solution ad-hoc solution générique

Abstraction / Modélisation

Trier un ensemble
Trier les valeurs de n valeurs dans
51, 48, 6 dans Trier 10 valeurs l'ordre croissant
l'ordre croissant dans l'ordre
croissant

2
ALGORITHMIQUE :
COEUR DE L’INFORMATIQUE
C’est une boîte à outils pour résoudre des problèmes (complexes) en
proposant :
 Des moyens de décrire précisément (modéliser) les étapes de
calcul et les données (structures de contrôle et de données)
 Différentes méthodes de résolution : récursivité, itération, "diviser
pour résoudre", algorithmes d'approximation (programmation
dynamique, algorithmes gloutons, utilisation d’heuristiques, IA),
programmation parallèle ou distribuée…
 Une classification de ces méthodes / problèmes
 Une classification des problèmes traités : P, NP, NP-complet
 Des bases théoriques posant les limites de l’algorithmique :
décidabilité, calculabilité
 Des métriques associées (complexité en temps, complexité en
espace…) et des techniques d’optimisation
 Des techniques pour vérifier la correction (ou sûreté), la
terminaison (vivacité) et la complétude des algorithmes construits
3
PLAN DU COURS

I - Modélisation et conception d’algorithmes/programmes


II - Rappels sur les éléments constitutifs des programmes
impératifs (contrôle), langage de description algorithmique
III - Schéma itératif, schéma récursif de programme : conception,
éléments d’analyse (optimisation, correction)
IV – Test de programme, éléments de typage
V - Rappels sur les structures de données pour les programmes
impératifs
VI - Abstraction (extraction + représentation) de données et
conception de programmes. Types Abstraits de Données (TAD)
VII - Représentation objet d’un TAD
VIII - Les TAD génériques associés aux structures de données
liste, pile, file et arbre 4
I – MODELISATION ET CONCEPTION
D’ALGORITHMES / PROGRAMMES
QU’EST CE QU’UN ALGORITHME ?

Un algorithme est un ensemble de règles ou de procédures bien


défini qu'il faut suivre pour obtenir la solution d'un problème ou
accomplir une tâche en un nombre fini d'étapes.

 L’algorithme agit sur une représentation des données du problème


(ex : modifier un élément de donnée associé à une personne)

 En informatique, l’algorithme doit pouvoir s’exprimer (être implanté)


dans un langage de programmation

5
DEMARCHE POUR RESOUDRE
UN PROBLEME
(OBTENTION D’UN ALGORITHME)

Analyse Problème à résoudre


données que l’on
(fonctionnelle) entités distingue ou/et leur
représentation dans
méthodes un modèle
Conception / liens entre entités
Modélisation *(modèle) et entre méthodes
Conception algorithmique algorithme
*(Schéma de résolution) -> solution
Programmation programme
*Définition de types (de leurs liens), de méthodes (de leurs liens) 6
DEMARCHE POUR RESOUDRE
UN PROBLEME
(ETUDE DE L’ALGORITHME)

Algorithme ou Programme
Analyse (pour la qualité)

Constituants
Preuve
Métriques Complexité
Test

Correction Qualité Efficacité


Assurance qualité
7
1 – ANALYSE (FONCTIONNELLE) :
BIEN POSER UN PROBLEME

 Préciser les données du problème (hypothèses,


informations contextuelles…)
 Préciser les résultats attendus
 Préciser les outils de résolution et de mise en
œuvre dont on dispose

8
EXEMPLES

 Exemple 1 :
A partir d’informations stockées dans le système
d’information d’une entreprise, élaborer des données
statistiques comme l’évolution de la masse salariale et des
dépenses de fonctionnement sur les dix dernières années.

L’outil de résolution choisi étant un gestionnaire de base de


données relationnel.

9
EXEMPLES

 Exemple 2 :
Une entreprise conçoit et commercialise deux produits.
Le premier est vendu avec un bénéfice de 10 euros l’unité pour les
40 premières unités, et de 20 euros pour les suivantes,
Le second est vendu avec un bénéfice de 14 euros l’unité pour les
25 premières unités, et de 18 euros pour les suivantes.
La production est soumise à la contrainte suivante : la quantité
journalière fabriquée ne peut dépasser 100 unités tous produits
confondus.

Quel est le plan de production quotidien qui optimise les bénéfices ?

L’outil pouvant être utilisé est un outil de programmation en C.


10
2 – CONCEPTION

Modéliser le problème (ses données)


+
Modéliser les opérations autorisées sur ces données
(méthodes)
+
Modéliser la résolution du problème
(algorithme, schéma de résolution)

Remarque importante : Toute représentation possible dans un


modèle doit pouvoir être « implantée » (implémentée, avoir un support)
dans un langage informatique.
11
2-1 – CONCEPTION :
MODELE DE DONNEES
 D’un point de vue logique :

MODELE DE DONNEES Mod_A


=
ENSEMBLE D’ELEMENTS A
+
ENSEMBLE DE RELATIONS SUR A
+
ENSEMBLE D’ELEMENTS PARTICULIERS DANS A

12
MODELISER LES DONNEES

Représenter formellement les données, avec :


. Des constantes ou des symboles (éléments de A) : Dans l’exemple
2, la constante entière 100 représente la quantité globale de produit à ne
pas dépasser.
. Des variables (éléments de A) : Dans l’exemple 1, les variables
salaire (resp. année), où salaire pourra prendre comme valeur la charge
salariale annuelle de l’entreprise correspondant à l’année dont la valeur
sera prise par la variable année.
Dans l’exemple 2, qp1 sera la quantité quotidienne de produit 1 et qp2 la
quantité quotidienne de produit 2.

. Des expressions paramétrées (arithmétiques, booléennes,


fonctionnelles… (éléments de A)) : Dans l’exemple 2, l’expression
arithmétique qp1 + qp2 représentera la quantité quotidienne fabriquée
tous produits confondus (cas d’une programmation en C).
13
MODELISER LES DONNEES

Mais aussi :

. Utiliser des collections (ensembles, tables, graphes…)

. Utiliser des relations (relations sur A) :


Pour relier des représentations de données dépendantes.

Dans l’exemple 1 (cas d’un SGBDR), on pourrait avoir la relation


« statistique » telle que :
statistique(salaire, fonctionnement, année)
met en relation le total des salaires, le total des dépenses de
fonctionnement dans une année donnée.

14
MODELISER LES OPERATIONS SUR
LES DONNEES (METHODES)

. Avec des fonctions ou des procédures (relations sur A) :


Pour réaliser des « actions » sur des données (ou leurs
valeurs).

Dans l’exemple 1, la fonction somme des totaux précédents


sur les dix dernières années.

15
EXEMPLE DE MODELE
(Pour l’exemple 2)

. Les constantes sont : 10, 20, 14, 18, 40, 25, 100 ;
. Les variables sont : qp1, qp11, qp12, z12 (quantité quotidienne de produit 1),
qp2, qp21, qp22, z22 (quantité quotidienne de produit 2) : entiers naturels ;
. Une fonction f(bénéfice quotidien) telle que :
f(qp11,qp12,qp21,qp22) = 10*qp11 + 20*qp12 + 14*qp21 + 18*qp22
. Des relations d’égalité = :
qp1 = qp11+qp12 (quantité de produit 1), qp2 = qp21+qp22 (quantité de produit 2)
. Des relations d’inégalité ≤ :
qp1+qp2 ≤ 100 (limite de production quotidienne)
0 ≤ qp11, 0 ≤ qp12, 0 ≤ qp21, 0 ≤ qp22 (toute quantité produite est positive)
qp11 ≤ 40, qp11≥ 40 * z12, qp12 ≤ 60 * z12, z12 ϵ {0,1}
qp21 ≤ 25, qp21 ≥ 25 * z22, qp22 ≤ 75 * z22, z22 ϵ {0,1}
vers CM2
16
EXEMPLE DE MODELE
(Exemple 2 récapitulatif)

En plus de la représentation des données (qp, z), décrire le calcul :

Maximiser
f(qp11,qp12,qp21,qp22) = 10*qp11 + 20*qp12 + 14*qp21 + 18*qp22
telle que qp11+qp12+qp21+qp22 ≤ 100
0 ≤ qp11, 0 ≤ qp12, 0 ≤ qp21, 0 ≤ qp22
qp11 ≤ 40, qp11 ≥ 40 * z12,
qp12 ≤ 60 * z12, z12 ϵ {0,1}
qp21 ≤ 25, qp21 ≥ 25 * z22,
qp22 ≤ 75 * z22, z22 ϵ {0,1}

Noter tout l’intérêt du travail permettant de passer de l’énoncé d’un


problème (p.10) à un modèle à la base de sa résolution.
Si on ne sait pas faire cela, on ne peut pas résoudre le problème (tout
algorithme de résolution et/ou programme informatique sont quasiment
impossibles à réaliser) ! 17
2-2 – CONCEPTION ALGORITHMIQUE :
LE SCHEMA DE RESOLUTION

:- lib(fd). Exemple 2 :
:- lib(fd_search).
 Résolution possible par
opt(P11,P12,P21,P22) :- l’algorithme du simplexe
P11 :: [0..40],
P12 :: [0..60], (programmable) dans le cadre
P21 :: [0..25], de la programmation linéaire en
P22 :: [0..75], nombres entiers (Recherche
[Z12,Z22] :: [0..1], Opérationnelle, cf. UE Algo
P11+P12+P21+P22 #=< 100,
FC #= 10*P11+20*P12+14*P21+18*P22, avancée en L3).
FCNeg #= -FC,
P11 #>= 40*Z12,
P12 #=< 60*Z12,  Autre solution via la
P21 #>= 25*Z22, programmation par contraintes :
P22 #=< 75*Z22, résolution de systèmes de
minimize(labeling([P11,P12,P21,P22]), FCNeg). contraintes (cf. UE PLIA en L3).
18
3 – ANALYSE DE L’ALGORITHME
(POUR LA QUALITE)

Exemple : un même problème


mais
plusieurs algorithmes de résolution
 Soit les algorithmes algo1, algo2, algo3.
Dans ce qui suit, n est un entier.
1- algo1(0)=0, algo1(n)=n+algo1(n-1)
2- Pour calculer algo2(n)
. Affecter la valeur initiale 0 à une variable entière x puis
. Pour i= 1, …, n (i étant une variable entière) ajouter
respectivement chacune des valeurs de i à la valeur de x.
. La valeur de algo2(n) est la valeur finale de x.
3- algo3(n)=n*(n+1)/2
19
UN MÊME PROBLÈME
PLUSIEURS ALGORITHMES DE RESOLUTION
(analyse des algorithmes)

 Une lecture de code permet de repérer des éléments caractéristiques


des algorithmes (bien utilisés) : algo1 est récursif, algo2 est itératif,
algo3 utilise un résultat des mathématiques sur la somme des n+1
premiers termes d’une progression arithmétique.

 La lecture de code permet aussi de se convaincre que les algorithmes


algo1, algo2, algo3 calculent bien la somme des n+1 premiers entiers (0
compris).

 Une analyse (statique) des programmes associés permettrait de


montrer des propriétés (éléments de correction) telles que : les valeurs
calculées sont positives, elles forment une suite croissante, etc.

 Métriques : pour quantifier la qualité. Par exemple, nombre de


variables, nombre de lignes de code, etc.
Sur ces 2 métriques, algo3 est meilleur que algo1, lui-même meilleur que algo2. 20
UN MÊME PROBLÈME
PLUSIEURS ALGORITHMES DE RESOLUTION
(une preuve du résultat des algorithmes)

La preuve est une technique statique, basée entièrement


sur le code source du programme : elle ne nécessite pas
l’exécution du programme

A titre d’exemple, nous prouvons par récurrence (induction)


que algo2 permet le calcul de la somme des n+1 premiers
entiers.

 Cas de base : Pour n=0, algo2(n)=0 (valeur initiale de x)


0 est bien la somme des 0+1 premier entier (0 inclus).
21
UN MÊME PROBLÈME
PLUSIEURS ALGORITHMES DE RESOLUTION
(une preuve : suite et fin)

 Cas général (récursif) :


- Soit v la valeur calculée par algo2 appliqué à l’entier k.
v est la valeur de x (initialisée à 0) lorsque i prend les
valeurs respectives 1, …, k dans l’algorithme.
Par hypothèse de récurrence, supposons que v est la
somme des k+1 premiers entiers (0 compris).
- Soit v’ la valeur calculée par algo2 appliqué à k+1.
v’ est la valeur de x (initialisée à 0) lorsque i prend les
valeurs respectives 1, …, k, k+1 dans l’algorithme : on aura
donc v’ = v+k+1 et donc v’ est la somme des k+2 premiers
entiers (0 compris).
22
Exercice

 Prouver la correction de algo3.


Il n’y a rien à prouver !

algo3(n) = n*(n+1)/2

utilise une formule prouvée en mathématiques, et qui vaut


la somme des n+1 premiers entiers (0 inclus).

23
Exercice

 Prouver par récurrence la correction de algo1.

- Cas de base : on prouve le résultat pour n=0.


algo1(0) = 0

- Cas général/récursif : supposons que algo1(k) = k*(k+1)/2 et


prouvons que algo1(k+1) = (k+1)*(k+2)/2.

algo1(k+1) = (k+1) + algo1(k)


= (k+1) + (k*(k+1)/2)
= (k+2)*(k+1)/2

24
UN MÊME PROBLÈME
PLUSIEURS ALGORITHMES DE RESOLUTION
(complexité)

 Un programme étant associé à chacun des


algorithmes, la mesure de temps d’exécution de
ces programmes sur des valeurs de plus en
plus grandes de n montrera que algo3 est plus
performant.

 Une mesure théorique de la complexité de ces


algorithmes confirmera et généralisera la mesure
pratique et ponctuelle des temps d’exécution.
25
EXPRIMER UN ALGORITHME DANS
LE PARADIGME IMPERATIF

II - Eléments constitutifs des programmes impératifs,


langage de description algorithmique

26
ECRIRE UN ALGORITHME :
S’EXPRIMER A UN HAUT NIVEAU EN
INFORMATIQUE

LANGAGE NATUREL (Français, Anglais…)


OU SYMBOLIQUE (mathématiques…)
Mécanismes de traduction éventuels

LANGAGE ALGORITHMIQUE (voir manuel)


Mécanismes de traduction éventuels

LANGAGE DE HAUT NIVEAU (C, LISP, Java…)


Mécanismes de traduction existants

LANGAGE MACHINE
27
CARACTERISTIQUES D’UN
LANGAGE DE HAUT NIVEAU
IMPERATIF

LANGAGE IMPERATIF
=
LANGAGE À BASE D’INSTRUCTIONS
modifiant des EMPLACEMENTS MEMOIRE

• Les emplacements mémoires : partie données -> flot de données


• Les instructions : partie contrôle -> flot de contrôle

28
CONSTITUANTS SYNTAXIQUES
D’UN LANGAGE IMPERATIF

 Au niveau le plus élevé : le programme (nom, arguments). Un « pas de


traitement » correspond à l’exécution d’une instruction.
 Puis les sous-programmes (procédures, fonctions) pour la modularité,
réutilisation.Si non primitif, le sous-programme appelé doit être déclaré
(chargé) avant utilisation : inclure(nom_sous_programme).
 Représentation des données basiques : constantes, variables (avec adresses),
expressions numériques ou booléennes construites à partir de variables, de
constantes, d’opérateurs, de symboles particuliers et d’expressions. Attention
aux priorités lors de l’évaluation :
 Expressions numériques : () puis */ puis +- puis expressions les plus à gauche
(associativité gauche)
 Expressions booléennes : () puis <>=≠ puis NON puis ET puis OU puis expressions
les plus à gauche (associativité gauche)
 Instructions de base : affectation (n ← 3, consistance de type), lecture (lire),
écriture (écrire, écrireln)
 Structures de contrôle : séquence directe (instruction ou bloc entre {}),
conditionnelles (si... sinon), boucles. 29
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les structures de contrôle

 Selon les langages, d’autres schémas conditionnels que le


si… sinon peuvent exister mais ils sont toujours
exprimables avec les schémas conditionnels de base du
langage algorithmique
 Par exemple, le switch/case en C :
switch (var) {
case val1 : séq1; break; si (var = val1) séq1
case val2 : séq2; break; sinon si (var = val2) séq2
…. sinon …
case valn : séqn; break; sinon si (var = valn) séqn
default : séq; sinon séq
}
30
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les structures de contrôle

 Attention au break à expliciter dans le switch/case en C


 Si on l’oublie :
Equivalent en langage algorithmique :

si (var = val1)
switch (var) { { séq1; séq2; … séqn; séq }
case val1 : séq1; sinon si (var = val2)
case val2 : séq2; { séq2; … séqn; séq }
…. sinon …
case valn : séqn; sinon si (var = valn)
default : séq; { séqn; séq }
} sinon séq 31
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les structures de contrôle

Itérations (boucles) en langage algorithmique

En général, le corps de la boucle modifie au-moins une


variable utilisée dans la condition pour assurer la terminaison
On peut avoir les trois possibilités :
Ordre dépend du pas p (négatif ou positif)

tant que condition faire pour i de init à final [pas p]


séquence séquence séquence
tant que condition

Condition de continuation ( = négation de la condition d’arrêt) 32


Exercice – question 1

 Soit p le nombre de passages dans la boucle suivante lors de son


exécution (on sort de la boucle après la pème itération)

tant que n > 0 // n est un entier naturel


{ traitement1 // peut dépendre de n, mais ne modifie pas n
n ← n/2 } // division entière

 Montrer que p  1 + log2 n (i.e. que lors de l’exécution, on


passera au maximum 1 + log2 n fois dans la boucle : élément clé
pour évaluer la complexité de l’algorithme et sa terminaison)

* On pourra utiliser les résultats suivants des mathématiques :


log2 est une fonction croissante et log2 2n = n
33
Question 2

 En supposant que l’on ait une structure itérative pour


réalisant le même calcul que la structure tant que
précédente :

pour i de 1 à n // n est un entier naturel


{ traitement2 } // peut dépendre de i sans le modifier

Laquelle de ces deux structures choisiriez-vous ?


Pourquoi ?

34
Solution
On note n:2 la valeur du résultat de la division (réelle) de n par 2.
On note n/2 la partie entière de la valeur n:2.
- Avant la première itération, la variable n contient la valeur initiale de n.
- Après une itération, la nouvelle valeur de n est n/2 et on a :
n/2  n:2
- Après deux itérations la nouvelle valeur de n est (n/2)/2 et on a :
(n/2)/2  (n:2):2 = n:22

- Après (p-1) itérations la valeur de n a été divisée (p-1) fois par 2 et on a :
(…((n/2)/2)……)/2  n:2(p-1)
p-1 fois
- Au début de la pème itération, la valeur de n doit être 1 (car on sortira de la
boucle après une dernière mise à jour de n à sa valeur finale 0) et on a :
(…((n/2)/2)……)/2 = 1
On a ainsi 1  n:2(p-1) d’où 2(p-1)  n donc log2 2(p-1)  log2 n d’où p-1  log2 n
et donc p  1 + log2 n 35
III - Schéma itératif,
schéma récursif de programme

Conception, éléments d’analyse des


algorithmes itératifs ou récursifs
(optimisation, correction)

36
PROGRAMMES RECURSIFS

 Un programme est récursif lorsque pour décrire


son comportement sur des valeurs de données
d’une certaine taille (ex : valeurs numériques,
ensembles d’éléments…), on utilise soit
directement soit indirectement le même
programme appliqué à des valeurs différentes
(ex : plus petites).
 Les valeurs des données ne peuvent pas être
prises en-deçà d’un certain seuil.
 On dit que l’on fait un appel récursif au
programme.
37
PROGRAMMES RECURSIFS

 Attention !!
 Les appels récursifs doivent être effectués un nombre
fini de fois.

Lors du dernier appel (lorsque le seuil des valeurs est


atteint) le programme retourne en général directement un
résultat.

Cela implique les propriétés de vivacité et de terminaison :


décroissance stricte des paramètres vers le cas d’arrêt.
La complexité du programme doit ainsi être finie.

38
PROGRAMMES RECURSIFS

 La situation dans laquelle s’effectue le dernier


appel est la base de récursivité.
 Cette situation est atteinte lorsque les valeurs
des données en entrée vérifient certaines
conditions.

 C’est pour cela que ….

39
PROGRAMMES RECURSIFS

 Les structures conditionnelles sont bien adaptées à la


construction d’un schéma récursif de programme :
si condition // condition d’arrêt
retourner(résultat)
sinon
faire appel récursif

OU ENCORE :
si condition // condition de continuation, cas récursif
faire appel récursif
[sinon] retourner(résultat) // retour du résultat associé au cas d’arrêt
40
SCHEMA GENERAL DE
PROGRAMME RECURSIF

si cond_arrêt1 retourner res_cas_arrêt1


sinon si cond_arrêt2 retourner res_cas_arrêt2
sinon …
sinon si cond_arrêt_n retourner res_cas_arrêt_n
sinon traitement_récursif

- Dans traitement_récursif, l’algorithme se rappelle lui-même


- traitement_récursif peut aussi être composé de différents cas récursifs
- L’ensemble des cas doit couvrir le domaine d’entrée en totalité : c’est la
propriété de complétude.
41
PROGRAMMES RECURSIFS

 Un programme est récursif terminal lorsqu’après


chaque appel récursif il n’y a plus d’instructions à
traiter.
 Un programme possède une récursivité croisée
(ou indirecte) lorsque ses appels récursifs
utilisent des appels intermédiaires à d’autres
programmes.
 Un programme possède une récursivité multiple
lorsqu’il contient plusieurs appels récursifs à lui-
même, ou que la récursivité se fait sur plusieurs
paramètres.
42
PROGRAMMES RECURSIFS
Exemple de récursivité terminale

 Le calcul du pgcd de deux nombres par l’algorithme


d’Euclide :

(entier) pgcd(entier x, y) // x, y entiers naturels


{
si y = 0 // condition du cas d’arrêt
retourner (x)
sinon retourner (pgcd(y, x modulo y))
}
43
PROGRAMMES RECURSIFS
Exemples (factorielle)

 Programme permettant le calcul de la factorielle n!


d’un nombre n :

(entier) facto(entier n) // n entier naturel


{
si n = 0 // condition du cas d’arrêt
retourner (1)
sinon retourner (n * facto(n-1)) Récursivité
} non terminale
44
PROGRAMMES RECURSIFS
Exemples (factorielle)

 Illustration de l’usage d’un accumulateur pour passer


d’une version non récursive terminale à une version
récursive terminale

(entier) facto2(entier n)
{ retourner (facto-acc(n,1) }
(entier) facto-acc(entier n, entier a) {
si n = 0
retourner (a)
sinon retourner (facto-acc(n-1, n * a))
Récursivité }
terminale
45
PROGRAMMES RECURSIFS
Exemple de récursivité croisée

(booléen) est-pair(entier n) { // n entier naturel


si n = 0 retourner (vrai)
sinon si n = 1 retourner (faux) // optimisation
sinon retourner (est-impair(n-1) }

(booléen) est-impair(entier n) {
si n = 0 retourner (faux)
sinon si n = 1 retourner (vrai) // optimisation
sinon retourner (est-pair(n-1) }

46
PROGRAMMES RECURSIFS
Exemples de récursivité directe

 Les fonctions est-pair et est-impair précédentes peuvent


aussi s’écrire plus simplement et indépendamment avec
de la récursivité directe :

(booléen) est-pair(entier n) { // n entier naturel


si n = 0 retourner (vrai)
sinon si n = 1 retourner (faux)
sinon retourner (est-pair(n-2) }

(booléen) est-impair(entier n) {
si n = 0 retourner (faux)
sinon si n = 1 retourner (vrai)
sinon retourner (est-impair(n-2) }
47
PROGRAMMES RECURSIFS
Exemple de récursivité multiple

 Calcul des termes de la suite de Fibonacci :


F0 = 1, F1 = 1, Fn = Fn-1 + Fn-2

Complexité
(entier) fibo(entier n) exponentielle !
{
si n= 0 OU n = 1
retourner (1)
sinon retourner (fibo(n-1) + fibo(n-2)) // appel multiple
}
48
PROGRAMMES RECURSIFS
Exemple de récursivité multiple

 Optimisation de Fibonacci via une fonction à mémoire


linéaire et un tableau F (indicé de 0 à n) :

Complexité
Tableau[entier][n+1] F ← {1,1,0,…0}
(entier) fibo2(entier n) { linéaire !
si F[n] 0 retourner (F[n])
sinon { F[n] ← fibo2(n-1) + fibo2(n-2) // appel multiple
retourner (F[n]) }
}

49
PROGRAMMES RECURSIFS
Intérêts

 Un schéma récursif traduit une démarche définissant le général à


partir du particulier en un nombre fini d’étapes :
On définit par exemple facto(n) à partir de la situation particulière
facto(0).
Cette démarche « naturelle » est notamment utilisée dans de
nombreuses définitions en mathématiques.

 Une « version » récursive de programme est en général moins


longue en nombre de lignes de code.

 Une « version » récursive de programme s’adapte à des supports


de données dynamiques (dont la taille peut évoluer, par exemple
en « libérant de l’espace mémoire occupé par des constituants de
ce support » à chaque nouvel appel).
50
PROGRAMMES RECURSIFS
Récursif vs Itératif ?
Avantages/Inconvénients

 Il n’est pas évident de décrire certains problèmes en itératif (par


exemple des énoncés mathématiques, ou si on utilise des types
de données complexes structurellement inductifs).

 A contrario, d’autres problèmes sont difficiles à décrire de


manière récursive (boucles d’E/S, ordonnancement de tâches…).

 L’utilisation de supports de données statiques (par exemple des


tableaux) peut être pénalisante dans les performances du
programme récursif en temps de calcul ou en occupation
mémoire lors de son traitement.

 On peut transformer toute version récursive terminale (resp. non


terminale) de programme en une version itérative sans pile (resp.
avec pile explicite).
51
PROGRAMMES RECURSIFS
Passage du récursif à l’itératif
frec(n) :
Plusieurs cas d’arrêt
-> plusieurs cas
si cond-arrêt d’initialisation
retourner res-arrêt Récursivité multiple
-> plusieurs t-param
sinon retourner (t-res(frec(t-param(n)))
et/ou variables

fiter(n) : fiter(n) :

type_r r ← res-arrêt ou type_r r ← res-arrêt


tant que non(cond-arrêt) tant que non(cond-arrêt)
{ r ← t-res(r,n) { n ← t-param(n)
n ← t-param(n) } r ← t-res(r,n) }
retourner (r) retourner (r) 52
PROGRAMMES RECURSIFS
Exemple de transformation d’une
récursivité terminale en itératif
sans pile explicite

 Ci-dessous la version itérative du calcul du pgcd par


l’algorithme d’Euclide (vu en récursif p. 43) :

(entier) pgcd-iter(entier x, y) { // x, y entiers naturels


entier t
tant que y  0 // condition de continuation
{ t ← x modulo y
x←y
y←t }
retourner (x)
}
53
EXERCICE

Question 1 :
 Formuler en langage ALGO la méthode de calcul
alternative du pgcd suivante (exprimée en langage
naturel) :
« Le pgcd de 2 nombres x, y se calcule comme suit :
lorsque ces 2 nombres ont des valeurs différentes, on
calcule leur pgcd comme le pgcd du plus petit des 2
nombres avec la différence du plus grand et du plus
petit.
Ce procédé s’arrête lorsque le pgcd se calcule sur 2
nombres égaux, et dans ce cas le pgcd est la valeur
commune de ces 2 nombres »
54
SOLUTION A LA QUESTION 1

(entier) pgcd1(entier x, y)
{
si x = y
retourner (x)
sinon si x > y
retourner (pgcd1(x-y, y))
sinon
retourner (pgcd1(x, y-x))
}

55
EXERCICE

Question 2 :

 Donner en langage ALGO une version


itérative de l’algorithme pgcd1 construit
précédemment.

56
SOLUTION A LA QUESTION 2

(entier) pgcd2(entier x, y)
{ entier u ← x, v ← y
tant que u ≠ v
{
si u > v
u ← u-v
sinon
v ← v-u
}
retourner(u)
}
57
PROGRAMMES RECURSIFS
Preuve de correction (sûreté)

Pour prouver la correction d’un programme récursif, il faut :


 Prouver que les conditions d’arrêt des appels récursifs sont
vérifiées après un nombre fini d’appels (terminaison) : par
exemple la condition n = 0 est atteinte après n appels récursifs
dans le calcul de facto(n).
 Prouver par récurrence que le programme répond bien à ses
spécifications. Pour cela :
. on vérifie que lors du dernier appel, le résultat est correct.
. puis en remontant l’historique des appels, on démontre que si
à une étape quelconque de l’historique le résultat est correct
alors il l’est aussi à l’étape suivante.

On pourra utiliser ces principes pour prouver la correction des


programmes facto ou fibo.
58
EXERCICE

 Donner la trace d’exécution du programme suivant :


(réel) fonct1(entier x, n) // x et n entiers naturels
{ réel y ← 1
si n  0
{ y ← x * fonct1(x, n-1)
y ← -y/n
}
écrireln(y)
retourner(y)
}

pour les valeurs 4 (resp. 3) de x (resp. n).

 Prouver la correction du programme.


59
Trace d’exécution
fonct1(4, 3)
y ← 4 * ((-4)2/2!)
y ← -4 * ((-4)2/2! * 3)
écrireln((-4)3/3!), retourner((-4)3/3!)
fonct1(4, 2)
autres instructions suspendues y ← 4 * (-4/1)
car récursivité non terminale. y ← -4 * (-4/1* 2)
(y ← x *… ; y ← -y/n ; écrireln(y)) écrireln((-4)2/2!), retourner((-4)2/2!)
fonct1(4, 1)
y ← 4 *1
idem y ← -4/1
écrireln(-4/1), retourner(-4/1)
fonct1(4, 0) On va remonter
idem et l’historique des appels.
dernier appel à fonct1 écrireln(1), retourner(1)

60
Résultat

 Le résultat d’exécution est –10,666…


avec l’affichage :
1
-4
8
- 10,666…

 Le programme retourne la valeur de (-x)n/n!


 Le programme affiche les valeurs de
l’expression (-x)i/i! pour i = 0, 1, 2, ….., n 61
Preuve de correction

 Terminaison : il y a arrêt des appels récursifs après n


appels car à chaque appel à fonct1 la valeur de n est
décrémentée de 1 jusqu’à atteindre la valeur 0.
 On va prouver que le programme écrit les valeurs
de (-x)i/i! (pour i de 0 jusqu’à la valeur de n en entrée).
 Au dernier appel (pour n = 0), fonct1 écrit 1 et on a
bien : (-x)0/0! = 1.
 En remontant l’historique des appels, si à la kème
étape la valeur écrite est (-x)k/k! alors à la k+1ème
étape on traite les instructions :
y ← x * fonct1(x,k) c’est à dire y ← x * (-x)k/k!
y ← -y/(k+1) c’est à dire y ← -x * (-x)k/(k! * (k+1))
écrireln (y) c’est à dire écrireln (-x)k+1/(k+1)!

CQFD 62
Programme fonct1 modifié

En supposant que l’on a :


(réel) fonct(entier x, n) // x et n entiers naturels
{ réel y ← 1
si n  0
{ y ← x * fonct(x, n-1)
y ← -y/n
}
retourner(y)
}
 Donner la trace d’exécution du programme suivant pour les
valeurs 4 (resp. 3) de x (resp. n) :
principal(entier x, n) // x et n entiers naturels
{ inclure fonct
réel y ← 1
y ← fonct(x, n)
retourner(y)
}
63
Trace d’exécution
fonct (4, 3) est appelée dans principal y ← 4 * ((-4)2/2!)
y ← -4 * ((-4)2/2! * 3)
retourner(y) dans principal est suspendue retourne((-4)3/3!)
on traite retourner(y) dans principal
fonct(4, 2) y ← 4 * (-4/1)
autres instructions suspendues y ← -4 * (-4/1* 2)
car récursivité non terminale retourne((-4)2/2!)
fonct(4, 1)
y ← 4 *1
idem y ← -4 / 1
fonct(4, 0) retourne(-4/1)
on remonte l’historique des appels à fonct
idem + retourne(1)
dernier appel à fonct on traite retourner(y) dans fonct
n=0
64
PROGRAMMES ITERATIFS

 Un (fragment de) programme est itératif quand il


utilise au moins une structure itérative.

 Par exemple :

entier facto-iter(entier n) entier facto-iter2(entier n)


{ entier facto ← 1 { entier i, facto ← 1
tant que n > 1 pour i de 2 à n
{ facto ← n * facto { facto ← i * facto }
n ← n-1 } retourner(facto)
retourner(facto) }
} 65
Exercice

 Donner un programme itératif nbDiviseurs avec :


 en entrée deux nombres entiers supérieurs à 1
 et qui retourne un tableau qui contient le
nombre qui a le plus de diviseurs (différents de
1 et de lui-même) ainsi que ce nombre de
diviseurs.

* On pourra utiliser les fonctions suivantes qu’on supposera construites


en ALGO :
- reste qui appliquée à deux entiers p et q retourne le reste de la
division de p par q.
- ent qui appliquée à un nombre n retourne sa partie entière.

66
Solution

Tableau[entier][2] nbDiviseurs(entier n1, n2)


{ inclure ent, reste
entier compte1 ← 0 si compte1 = compte2
entier compte2 ← 0 { res[1] ← -1
Tableau[entier][2] res res[2] ← compte1 }
sinon si compte1 > compte2
pour i = 2 à ent(n1/2) { res[1] ← n1
{ si reste(n1,i) = 0
res[2] ← compte1 }
compte1 ← 1+ compte1 }
sinon
pour j = 2 à ent(n2/2) { res[1] ← n2
{ si reste(n2,j) = 0 res[2] ← compte2 }
compte2 ← 1+ compte2 } retourner(res)
}

67
 Tracez les valeurs des mises à jour des
variables jugées importantes lors de
l’exécution de :

nbDiviseurs(12,27)

68
Solution
n1 = 12
n2 = 27 ent(n1/2) = 6
i=2
compte1 = 0 compte1 = 1 ent(n2/2) = 13
compte2 = 0 i=3 j=2
compte1 = 2 compte2 = 0
i=4 j=3
compte1 = 3 compte2 = 1
i=5 …
compte1 > compte2
compte1 = 3 j=9
i=6 compte2 = 2
compte1 = 4 …
j = 13 12 | 4
compte2 = 2
69
PROGRAMMES ITERATIFS
Invariant de boucle

 Un invariant de boucle est une assertion S qui


doit être vraie après un nombre quelconque
d’itérations de cette boucle (sans sortir de la
boucle ou à la sortie de la boucle).

 Par exemple, un invariant de la boucle tant que


de la fonction facto-iter vue p.65 est :
« Après la kème itération, facto = n!/(n-k)! »
qui est une assertion dépendant des valeurs de
k. On la notera Sk.
70
PROGRAMMES ITERATIFS
Preuve de correction

Pour montrer la correction d’un programme


itératif, il faut :
 Montrer que le résultat du programme est correct
lorsqu’aucun passage dans la boucle n’est réalisé
(cas de base)
 Montrer la correction de la boucle :
 prouver que le nombre d’itérations est fini,
 prouver l’invariant de boucle (en général par
récurrence).
71
PROGRAMMES ITERATIFS
Preuve de correction
(Exemple de la fonction facto-iter vue p.65)
 Cas de base : si n = 0 ou n = 1 : 0 itérations et on
rend facto qui vaut 1. Correct.
 Terminaison : comme à chaque itération la valeur
de n est décrémentée de 1 et que la condition du
tant que est « n>1 », on aura au-plus n-1 itérations.
 Preuve par récurrence sur k :
. Pour k = 1, après la 1ère itération on a facto = n
c’est à dire S1 : facto = n!/(n-1)!
. Supposons Sk vraie, alors après la (k+1)ème
itération, facto = (n-k)*facto = (n-k)(n!/(n-k)!) =
n!/(n-k-1)! = n!/(n-(k+1))! i-e : Sk+1 vraie. 72
Une alternative et/ou un
complément aux preuves
de correction :

IV - le test

 Le test est une activité complémentaire de la preuve en


raison de son caractère (le plus souvent) dynamique

 Cf support de cours sur le test

73
IV (suite) –

Elements de typage

74
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types

Le liant entre données et contrôle


 Les types précisent la nature des constituants d’un
programme et permettent aussi un contrôle de
correction du contenu d’un programme.
On doit utiliser ou retourner des données respectant une nature définie
sinon erreur sémantique.
 Il y a 2 catégories de types :
1. les types primitifs, en général prédéfinis dans un langage
de programmation,
2. les types construits (avec une commande de construction
de types dans un langage utilisé, TAD)
 Des méthodes (opérateurs, procédures) et parmi elles
des primitives associées à un type permettent la
manipulation basique d’entités de ce type. 75
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types

 Les types primitifs : entier, réel, caractère, booléen


sont prédéfinis dans tous les langages de haut
niveau.
 Des opérateurs primitifs arithmétiques, booléens
… permettent une manipulation d’entités de type
entier, booléen…

 Une valeur associée à une variable de type primitif


est une entité compatible avec ce type.
 Par exemple, une valeur d’une variable réelle x
pourra être l’entier 3.
76
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types

Un type abstrait (de données) est :


• Soit un type que l’on « abstrait », non prédéfini dans un
langage et que l’on peut construire dans ce langage à partir de
types primitifs ou d’autres types déjà construits.

Par exemple, le type salaire (exemple 1, page 9) pourra être


construit à partir du type réel (charge salariale annuelle) et du
type entier (année)

• Soit un type générique prédéfini ayant comme support physique


une structure de données complexe.

Par exemple : une liste, un arbre, etc.


77
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types

 La valeur d’une variable de type abstrait peut être une référence


à une entité de ce type, permettant un accès à cette valeur.

Par exemple, la valeur de la variable x de type salaire pourra


être une référence (un identifiant, une adresse, une position …) à un
couple (11238.45, 2003).
La première composante correspondant à cette référence sera
la valeur 11238.45 et la seconde composante la valeur 2003.

78
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types
Exemple de représentation
dans un langage algorithmique

programme(……)
{
entier x // déclaration d’une variable x et de son type entier
ensEnt E, F // E et F variables de type ensEnt construit ou spécifié
// une fois les variables E et F déclarées, leur accès
// par référence est possible
x←3 // affectation de l’entier 3 à x
insérerp(x, E) // insertion de 3 dans E, procédure construite ou spécifiée
F←E // on affecte la référence de E (valeur de E) à F

}
79
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : les types

Retour sur l’affectation

 L’affectation permet d’attribuer à une entité d’un type donné,


une valeur du même type ou compatible avec ce type.
 Dans le cas d’un type abstrait, on peut aussi affecter à une
entité d’un type donné la valeur d’une référence (nom,
adresse) à une entité de ce type.
 Il est possible de noter &type la référence à un type.
 Dans l’exemple précédent, on suppose la définition du sous-
programme insérerp : insérerp(entier x; ensEnt E)
mais on pourrait aussi avoir : insérerp(entier x; &ensEnt E)
ou encore insérerp(&entier x; &ensEnt E) ou …
80
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

Instruction d’appel

 L’appel à un (sous-) programme appelé,


éventuellement paramétré, dans un
programme appelant permet lors de son
traitement, l’exécution de son contenu.

81
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

 L’instruction d’appel contient le nom du programme


appelé, ce dernier étant appliqué à une liste de
données (paramètres) qu’il manipule (cette liste
pouvant être vide).
 Lors de l’appel, les paramètres effectifs de l’appel
remplacent les paramètres formels de la définition du
programme appelé.
 Il existe différents mécanismes pour le passage des
paramètres : l’appel par valeur (ou par copie), l’appel
par référence, l’appel par adresse, l’appel par nom.
82
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

L’appel par valeur

L’appel du programme appelé se fait sur des


paramètres effectifs qui sont des valeurs
possibles d’arguments qui ne sont pas des
références, et qui sont copiés dans les
paramètres formels.
83
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par valeur

Programme appelé
permuterVal(entier u, v)
// entier n’est pas une référence à une valeur entière.
// Les valeurs pour u, v en entrée sont stockées aux adresses de u, v
{
entier w
w←u // On affecte la valeur de u à l’adresse de w
u←v // On affecte la valeur de v à l’adresse de u
v←w // On affecte la valeur de w à l’adresse de v
}
84
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par valeur

Programme appelant
appelVal()
{
entier a, b
lire(a, b) /* 2 valeurs saisies par l’utilisateur sont
affectées aux variables a et b */
permuterVal(a, b) /* les valeurs de a et b sont
passées en paramètres effectifs */
écrire(a, b)
} 85
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par valeur

Une exécution possible de appelVal

3 On saisit une valeur de a

4 On saisit une valeur de b

3 4 Affichage des valeurs de a et de b.

Il n’y a pas eu permutation de a et b !!


86
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par valeur

 Explications :
- Les paramètres formels u et v de permuterVal sont
activés (u et v ont le même comportement que des
variables locales à permuterVal).
- 3 (resp. 4) la valeur saisie de a (resp. b) est liée par
recopie à la variable u (resp. v) lors de l’appel.
- Les contenus des variables u et v sont permutés.
- Les contenus des variables a et b ne sont pas
modifiés ; on retrouvera les valeurs saisies de a et b.
- Affichage de : 3 4
87
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

L’appel par référence

L’appel du programme appelé se fait sur des


paramètres effectifs qui sont des références
d’arguments, qui peuvent être des adresses
(ou des identificateurs), et qui sont liés aux
paramètres formels.
88
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
Exemple d’appel par référence

Programme appelé

permuterRef(&entier u, v)
// &entier est une référence à un emplacement contenant une valeur entière.
// Référence u, v permet d’accéder à la valeur de u, v
{
entier w
w←u // On affecte la valeur référencée par u à l’adresse de w
u←v // On affecte la valeur référencée par v à la référence (adresse) u
v←w // On affecte la valeur de w à la référence (adresse) v
}
89
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par référence

Programme appelant
appelRef()
{
entier a, b
lire(a, b) /* 2 valeurs saisies par l’utilisateur sont
affectées aux variables a et b */
permuterRef(a, b) /* les références de a et b sont
passées en paramètres effectifs */
écrire(a, b)
} 90
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par référence

Une exécution possible de appelRef

3 On saisit une valeur de a

4 On saisit une valeur de b

4 3 Affichage des valeurs de a (ex b) puis de b (ex a).

Il y a eu permutation de a et b !

91
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
exemple d’appel par référence

 Explications :
- Les paramètres u et v de permuterRef sont activés.
- 3 (resp. 4) la valeur saisie de a est stockée à
l’adresse de a (resp. b) et c’est cette adresse qui est
liée à la référence u (resp. v) lors de l’appel.
- Les contenus référencés par u et v sont permutés.
- Par conséquent, les contenus des variables a et b ont
été permutés.
- Affichage de : 4 3 après la permutation.
92
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

L’appel par adresse


 L’appel par adresse est une implantation possible de l’appel
par référence.
 C’est aussi un mode de passage de paramètres requis dès
lors que l’on souhaite récupérer des valeurs de paramètres
modifiées par le sous-programme appelé.
 Ainsi, ce ne sont pas les valeurs des variables qui sont
passées en paramètres mais les valeurs de leurs adresses.
 Souvent, cela conduit à définir les paramètres formels du
sous-programme appelé en tant que pointeurs.
93
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

L’appel par adresse en C


L’appel par adresse en C se traduit par l’utilisation de pointeurs
dans le sous-programme appelé, comme dans l’exemple des
permutations suivant :

int main (void) {


int a=1, b=2; void permut (int *px, int *py) {
printf(«%d %d\n»,a, b); int m=*px;
permut(&a, &b); *px = *py;
printf(«%d %d\n»,a, b); *py = m;
return 1; }
}

94
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF : l’appel

L’appel par nom

L’appel du programme appelé se fait sur des


noms d’arguments. Ces noms peuvent être
considérés, après vérification de leur unicité,
comme des références.
L’appel par nom se traite alors comme un
appel par référence.
95
EXERCICE
On considère les 4 programmes suivants :

permuter1(entier x, y) permuter2(entier x; &entier y)


{ {
entier temp entier temp
temp ← x temp ← x
x←y x←y
y ← temp y ← temp
} }
Appel par valeur sur 1er paramètre Appel par valeur sur 1er paramètre
Appel par valeur sur 2ème paramètre Appel par référence sur 2ème paramètre

permuter3(&entier x; entier y) permuter4(&entier x; &entier y)


{ {
entier temp entier temp
temp ← x temp ← x
x←y x←y
y ← temp y ← temp
} }
Appel par référence sur 1er paramètre Appel par référence sur 1er paramètre
Appel par valeur sur 2ème paramètre Appel par référence sur 2ème paramètre

96
EXERCICE (suite)

 On considère le programme appelant :

appel()
{
entier a, b
a←3
b←4
écrireln(« valeur initiale a = », a, « ---- b = », b)
permuterN(a, b)
écrire(« valeur finale a = », a, « ---- b = », b)
}

 Donner le résultat d’exécution de appel dans les cas où


N = 1, 2, 3, 4.
 Expliquer pourquoi pour N = 1, 4.

97
SOLUTION

permuter1 permuter2
valeur initiale a = 3 ---- b = 4 valeur initiale a = 3 ---- b = 4
valeur finale a = 3 ---- b = 4 valeur finale a = 3 ---- b = 3
Pas de permutation car appel par Appel par valeur sur a et par référence
valeur sur a et b. sur b.

permuter3 permuter4
valeur initiale a = 3 ---- b = 4 valeur initiale a = 3 ---- b = 4
valeur finale a = 4 ---- b = 4 valeur finale a = 4 ---- b = 3
Appel par valeur sur b et par référence Permutation car appel par référence
sur a. sur a et b.

98
. Au moment de l’appel par valeur de permuter1 :
par recopie la valeur 3 de a est liée à x et la valeur 4 de b
est liée à y
.. Après exécution de temp ← x
temp contient la valeur 3 de x
.. Après exécution de x ← y
x contient la valeur 4 de y
.. Après exécution de y ← temp
y contient la valeur 3 contenue dans temp.

Bien que les contenus de x et de y aient été permutés, les


contenus de a et de b restent inchangés !!
Comme l’écriture porte sur a et b, ceci explique l’affichage.
Licence Informatique S5 99
. Au moment de l’appel par référence de permuter4 : la référence
(adresse de) x est celle de a et la référence y est celle de b
.. Après exécution de temp ← x
La variable temp contient la valeur référencée par x, qui est celle
de a
.. Après exécution de x ← y
La valeur référencée par x (et donc par a) est la valeur référencée
par y (qui est celle de b)
.. Après exécution de y ← temp
La valeur référencée par y (et donc par b) est la valeur de temp,
qui était celle de a initialement.

Les références a et b ont été permutées via x et y ;


comme l’écriture porte sur a et b, ceci explique l’affichage.
Licence Informatique S5 100
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
V - les structures de données

 Une structure de données sert de support à


l’information, elle en facilite le stockage, la gestion et
l’accès.
 Une cellule est le composant élémentaire d’une
structure de données. Elle a une adresse unique et
peut contenir un nombre fini de champs, chacun de ces
champs servant de support à une information.
 Certaines structures de données servent à la définition
de types prédéfinis dans les langages de
programmation. C’est le cas des pointeurs, des
tableaux, des enregistrements (structures). 101
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les pointeurs

 Un pointeur est une cellule à 2 champs. Le premier


porte une information d’un type particulier et l’autre
porte une adresse qui permet un accès à une autre
cellule (cellule pointée).
 Un pointeur est une représentation possible d’une
référence dans un langage de programmation.
Exemple :
entier *x
Cela signifie que x est un pointeur d’entier (la cellule
pointée porte une information entière). 102
EXERCICE

 Quels sont les effets des déclarations dans les trois


extraits de codes suivants ?

entier *ptr entier x entier *px


entier x entier *px ← &x entier y
x←5 px ← &y
ptr ← &x *px ← 10

Ex1 Ex2 Ex3


103
Solution (Ex1, Ex2)
Ex1 :
Adresse Contenu Variable

ad1 ad2 ptr -> La variable ptr a été


initialisée par une adresse
ad2 5 x valide qui est celle de la
variable x qui contient la
valeur 5.

Ex2 :
La variable pointeur d’entier px a été déclarée et
initialisée à l’adresse de la variable entière x.
104
Solution (Ex3)

Adresse Contenu Variable

ad1 ad2 px

ad2 10 y

L’adresse de la variable y est affectée à px puis la valeur prise


par y est 10 car on affecte 10 à *px, à l’adresse pointée par px
c’est à dire l’adresse de y.
105
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les tableaux

 Un tableau à 1 dimension et n positions (de taille 1*n = n) est un


agrégat de n cellules contiguës portant des informations de même
type.
Chaque position contient un élément du tableau.
Tous les éléments contenus dans un tableau sont de même type.

Schématiquement :
……

En langage ALGO, A[i] définit un accès (en lecture ou en


écriture) au contenu de la position de numéro i dans A.

106
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les tableaux

 Un tableau à m dimensions et à m(*n) positions (de


taille m*n) est un agrégat de m tableaux à une
dimension de taille n.
 Tous les éléments d’un tel tableau ont le même type.
Remarque : Une fois définies, les valeurs de m et n
ne sont pas modifiables.
Syntaxe possible (en langage algorithmique) :
tableau[type][m n] nom
type est le type des éléments du tableau.
107
EXERCICE

On suppose que l’on déclare un tableau T comme suit :


tableau[entier][23] T
(ou T[entier][23] si pas de confusion possible)

1- Quels sont les types de T[1] et de T[1][2] ?


2- On affecte respectivement les valeurs 1, 2, …, 6
sur les positions de T dans l’ordre lexicographique.
-> Donner en ALGO un fragment de programme
qui réalise cette affectation.
108
Solution

1- T[1] est un tableau d’entiers (à 1 dimension avec


3 positions),
T[1][2] est un entier (situé sur la ligne de numéro 1 et la
colonne de numéro 2 dans T).

2- entier x ← 1
pour i de 1 à 2
pour j de 1 à 3
{ T[i][j] ← x
x ← x+1 }
… 109
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les enregistrements

 Un enregistrement, ou structure, est un agrégat de


cellules appelées champs, chaque champ pouvant
contenir une information de type différent.

 Les enregistrements sont très utiles dans la


construction de nouveaux types à partir de types
déjà construits ou prédéfinis dans un langage.

 En particulier, ils permettent la réalisation de TAD


en langage algorithmique.
110
CONSTITUANTS SYNTAXIQUES D’UN
PROGRAMME IMPERATIF :
les enregistrements

 Syntaxe possible pour un type « enregistrement » (en langage ALGO)

type nomType =
{
existantType1 champ1
… // champi est un nom de champ
// existantTypei est un nom de type primitif ou déjà défini
existantTypen champn
}

 Accès à l’information stockée dans un champ : l’accès à l’information


contenue dans le champ champi de la structure x de type nomType se
fait par :
x.champi
111
EXEMPLE en C

Ci-après l’équivalent en C de la déclaration précédente.

struct nomStruct
{
existantType1 champ1;

existantTypen champn;
};
typedef struct nomStruct nomType;

112

Vous aimerez peut-être aussi