Vous êtes sur la page 1sur 34

M1103—Cours de C++ (4)

{ A. Casali / M. Laporte
Plan
A. La classe pair

B. Matrice (2D)

C. Tri

D. Récursivité

E. Tests (2ème partie)


A. Classe pair
On souhaite écrire des fonctions qui renvoie 2 instances de deux types
différents.

Exemple :
1. la fonction find () qui renvoie vrai si la sous-chaine passée en
paramètre a été trouvée et sa position;
2. La fonction Int32Parse () qui renvoie vrai si la chaine passée en
paramètre peut être convertie en entier, ainsi que cet entier.

La classe pair permet de regrouper deux instances de deux types


(identiques ou différents) au sein de la même variable.

#include <utility>
using namespace std;
pair <Type1, Type2> VarIdent;
On construit une pair en appelant la fonction make_pair () de profil
pair <Type1, Type2> make_pair (Val1, Val2);

Il faut obligatoirement que Val1 soit de type Type1


et Val2 de type Type2;

Exemple :

pair <bool, int> Int32Parse (const string & str)


{
istringstream iss (str);
int x;
iss >> x;
if (!iss.fail ()) return make_pair (true, x);
return make_pair (false, 0);
}
On accède à l’instance du premier type en utilisant la «propriété» first,
l’instance du second type en appelant second

void Display (pair <bool, int> MyPair)


{
if (MyPair.first)
cout << MyPair.second << endl;
else
cout << "la conversion a échouée" << endl;
}

pair <bool, int> MyPair = Int32Parse ("10");


Display (MyPair); 10
MyPair = Int32Parse ("10xyz");
Display (MyPair); 10
MyPair = Int32Parse ("abc10xyz");
Display (MyPair); la conversion a
échouée
Plan
A. La classe pair

B. Matrice (2D)

C. Tri

D. Récursivité

E. Tests (2ème partie)


B. Matrice (2D)
B.1 Déclaraction

vector <vector <AType>> VarIdent;


typedef vector <AType> CVLine;
typedef vector <CVLine> CMatrix;
CMatrix VarIdent; // Mat

On a une matrice 0x0


B.2 Dimensionnement

1. Fixer le nombre de lignes à NbLines


Mat.resize (NbLines);
2. Fixer le nombre de colonnes à NbColumns
for (unsigned i(0); i < Mat.size (); ++i)
Mat[i].resize (NbColumns); Désigne la ième ligne
for (CVLine & Aline : Mat)
Aline.resize (NbColumns);

3. Fixer le nombre de lignes à NbLines et le nombre de colonnes à NbColumns


Mat.resize (NbLines, vector <AType> (NbColums);

B.3 Dimensionnement lors de la déclaration

CMatrix Mat(NbLines, vector <AType> (NbColums);


B.4 Accès à une cellule

On accède à l’élément de la ième ligne, jème colonne en utilisant la notation [i][j].

Exemple : afficher le contenu d’une matrice


for (unsigned i (0); i < Mat.size(); ++i)
{
for (unsigned j(0); j < Mat[i].size(); ++j)
cout << Mat [i][j];
cout << endl;
}

Aucun contrôle quant à la validité des indices est effectué

for (const CVLine & ALine : Mat)


{
for (const AType & Cel : ALine)
cout << Cel;
cout << endl;
}
Plan
A. La classe pair

B. Matrice (2D)

C. Tri

D. Récursivité

E. Tests (2ème partie)


C. Tri de tableau
C.1 Tri par sélection

A la ième itération, on cherche le plus petit élément du tableau (V) entre les positions
[i, V.size()[ et on le permute avec l’élément en position i.

5 1 2 12 24 13 10 2

1 5 2 12 24 13 10 2

1 2 2 12 24 13 10 5
C.2 Tri par insertion

Chaque élément est placé à sa position finale dans le vecteur : on cherche à placer le
ième élément à sa place dans le sous vecteur [0, i[

5 1 2 12 24 13 10 2

1 5 2 12 24 13 10 2

1 2 5 12 24 13 10 2
C.3 Tri a bulles

Si l’élément d’indice i est plus grand que celui d’indice i +1, on les permute

5 1 2 12 24 13 10 2

1 2 5 12 13 10 2 24

1 2 5 12 10 2 13 24
Plan
A. La classe pair

B. Matrice (2D)

C. Tri

D. Récursivité

E. Tests (2ème partie)


D. Récursivité
D.1 Définition

un sous-programme est dit récursif si sa définition fait appel à lui-


même, directement ou pas.

D.2 Formulation informatique

void [T’] f (T [&] x, …) chaque appel à f() résout une partie du


{ problème sur le "paramètre" x puis confie
le reste du problème, plus simple, ... à f()
... sur le "paramètre" y.
f (y); C’est le principe de la stratégie « diviser
... pour régner » (divide & conquer).

}
Et on sort quand de l’appel récursif ?
Il doit toujours y avoir une condition de sortie /
terminaison (même si elle est cachée).
D.3 Récursivité directe simple

la récursivité est directe simple lorsqu'un sous-programme s'appelle


directement lui-même une seule fois.
Exemple : Calcul de n!
Formulation sans récurrence :

Formulation avec récurrence :

unsigned Facto (unsigned n)


{
if (n <= 1) return 1;
return n * Facto (n-1);

}
D.5 Attention avec la récursivité

La récursivité n’est pas la solution à tous nos problème.

Exemple : afficher le contenu d’une string caractère par caractère

Non récursif :
Procedure AfficheChaine (Chaine : in str)
debut
pour (i vrariant_de 0 à taille(Chaine) – 1)
faire
Afficher (Chaine [i]); Récursif Oui Non
ffaire
fin Nb Contexte N+1 1
Nb Objets 2N + 2 2
Avec récursivité :
Procedure AfficheChaine (Chaine : in str) (utilisés)
debut
AffichCarac (Chaine, 0);
fin

procedure AffichCarac (Chaine :in string, Pos : in entier_nat)


debut
si (Pos vaut taille (Chaine) return;
afficher (Chaine [Pos]);
AffichCarac (Chaine, Pos +1);
fin
D.5 Récursivité directe multiple

la récursivité est directe multiple lorsqu'un sous-programme s'appelle


directement lui-même une ou plusieurs fois.
Exemple : Calcul du nombre combinatoire

unsigned Combi (unsigned k, unsigned n)


{
if (n == k) return 1;
if (1 == k) return 1;
return Combi (k, n-1) + Combi (k-1, n-1);
}
Combi (3,5) 10

4 Combi (3,4) Combi (2,4) 6

1 Combi (3,3) Combi (2,3) 3 3 Combi (2,3) Combi (1,3) 3

1 Combi (2,2) Combi (1,2) 2


1 Combi (2,2) Combi (1,2) 2

La trace de l’algorithme forme un arbre binaire : à chaque


embranchement de l’arbre (nœud), on a au plus deux sous – arbres (fils).
Exemple : trier un vecteur

void QuickSort (vector <T> &V, unsigned Min, unsigned Max)


{
if (Max == Min) return;
unsigned Pivot = V[Min];
unsigned PosPivot (Ventiler (V, Pivot, Min, Max));
QuickSort (V, Min, PosPivot);
QuickSort (V, PosPivot + 1, Max);
}

5 1 2 12 24 13 10 2

Appel de Ventiler ()
1 2 2 5 24 13 10 12

Tous les chiffres plus petits que Pivot (ici 5) sont dans le sous
vecteur d’indice plus petit que PosPivot (ici 3), les autres sont
dans le sous vecteur d’indice plus grand.
On trie chacun des sous vecteurs indépendamment l’un
de l’autre.
D.6 Récursivité croisée / mutuelle

Deux algorithmes sont mutuellement récursifs si l’un fait appel à


l’autre, et l’autre fait appel à l’un.

Exemple : parité d’un entier


bool Pair (unsigned n)
{
return ((0 == n) ? true : Impair (n-1));
}

bool Impair (unsigned n)


{
return ((0 == n) ? false: Pair (n-1));
}

Pair (2) => Impair (1) => Pair (0) => true

Impair (2) => Pair (1) => Impair (0) => false
D.7 Récursivité terminale

Un sous programme est récursif terminale lorsqu’aucun traitement ne


suit l’appel récursif.

Exemple :

procedure AffichCarac (Str :in string, Pos : in entier_nat)


debut
si (Pos vaut taille (Chaine) return;
afficher (Chaine [Pos]);
AffichCarac (Str, Pos +1);
fin

Pour ce type de sous programme, on peut facilement casser l’appel récursif


en utilisant des instructions itératives (boucles).
Pour les autres, on doit passer par l’instanciation d’une pile qui permet
d’enregistrer les différentes informations renvoyées / modifiées lors des
appels récursifs successifs.
D.8 Mémoïsation

Technique d’optimisation de codage permettant de réduire le temps d’exécution


d’un sous programme en mémorisant ses résultats

Combi (3,5) 10

4 Combi (3,4) Combi (2,4) 6

1 Combi (3,3) Combi (2,3) 3 3 Combi (2,3) Combi (1,3) 3

1 Combi (2,2) Combi (1,2) 2


1 Combi (2,2) Combi (1,2) 2

On a aucun intérêt à recalculer Combi (2,3)

On stocke dans un tableau les valeurs déjà trouvées, sinon


on trouve 0.
unsigned Combi (unsigned k, unsigned n, CMatrix & TP)
{
if (TP [n][k] != 0) return TP [n][k];
TP [n][k] = Combi (k, n-1) + Combi (k-1, n-1);
return TP [n][k];
}

Reste à savoir comment on initialise la matrice TP!


void InitTP (CMatrix & TP, unsigned n)
{
TP.resize (n + 1);
for (unsigned i (1); i < TP.size (); ++i)
{
TP[i].resize (i + 1);
TP [i][1] = 1; //if (1 == k) return 1;
TP [i][i] = 1; // if (n == k) return 1;
}
}
D.9 Limiter le nombre d’appels récursifs

Pb : les derniers appels récursifs du tri rapide sont très couteux (mémoire +
temps), alors que la taille du sous-tableau est petite. Dans ce cas, on trie le sous-
tableau avec une autre méthode.

void QuickSort (vector <T> & V, unsigned Min, unsigned Max)


{
if (Max <= Min + 15) {
BubleSort (V, Min, Max);
return;
}
unsigned Pivot = V[Min];
unsigned PosPivot (Ventiler (V, Pivot, Min, Max));
QuickSort (V, Min, PosPivot);
QuickSort (V, PosPivot + 1, Max);
}
Plan
A. La classe pair

B. Matrice (2D)

C. Tri

D. Récursivité

E. Tests (2ème partie)


E. Tester ses programmes
La première qualité d’un programme c’est d’être juste (répondre au besoin du
client).

Entrée Programme Sortie


Entrée : connue (ou à construire)
Programme : maitrise du code source (ou pas)
Sortie : Est-elle conforme aux attentes?

Normalement, ce n’est pas le programmeur qui fait les tests, mais le


testeur. Dans la pratique, c’est souvent la même personne.

Exemple de méthodes de tests :


• Test boite noire;
• TDD (Test Driven Developpement)

En général, on écrit une fonction de test par fonction développée.


E.1 Test interne

Fonction à tester :
void Init (CVInt & V, unsigned Size)
{
V.resize (Size);
for (unsigned i (0); i < Size; ++i) V[i] = i + 1;
}

int main () Exactement les mêmes


{ paramètres.
CVInt VInt;
Init (Vint,10);
TestInit (Vint, 10);
} Test inversé
Macro du langage

void TestInit (const CVInt & V, unsigned Size)


{
if (Size != V.size ()) cerr << "I don’t know what to tell"
<< endl;
if (VInt[1] != 1) cerr << "Assertion failed: (VInt[1] == 1),
function TestInit, file " << __FILE__ << " line, " << __LINE__ <<
'.' << endl;…}
Utilisation de la procédure assert () de la bibliothèque cassert.

void TestInit (const CVInt & V, unsigned Size)


{
assert (Size == V.size ());
assert (VInt[1] == 1);
}

• Si une assertion est fausse, le programme


s’arrête (même si on encapsule l’appel à la
fonction de test dans un bloc try/catch).
• Seules les erreurs sont affichées sur la console.

Exemple :
Assertion failed: (VInt[1] == 1), function TestInit, file
../amphi2_5/main.cpp, line 124.
E.2 Test externe

Entrée Programme Sortie obtenue

diff

Sortie théorique

On ne teste plus une fonction, mais le programme dans son ensemble.


1. On redirige l’entrée clavier depuis un fichier texte, la sortie console
vers un autre fichier
2. On crée un fichier « Oracle » : la sortie attendue pour un fichier
d’entrée particulier
3. On compare si la sortie console est conforme à nos attentes
(utilisation de la commande diff ou d’autres programmes).
E.3 Test de performance

On ne commence les tests de performance qu’à partir du


moment où le programme est juste!

Idée :
Temps1 <- DemanderHeure ();
appeler la fonction;
Temps2 <- DemanderHeure ();
CalculerDifference (Temps2, Temps1);

Sémantiquement juste, mais complètement faux dans la pratique : si on exécute


plusieurs fois le programme de test, on n’obtiendra jamais le même résultat (l’OS
n’étant pas dans le même état) => il faut faire N (suffisamment grand selon le temps
disponible) tests et faire la moyenne des N tests.
Stocke dans timer1 l’horodatage courant.
Nécessite une adresse mémoire => passage par
#include <ctime>
référence dans l’appel.
tyepedef vector<time_t> CVTime;
void Test (unsigned NbTests)
{
CVTime VTime (NbTests);
//FonctionToTest () params initialization
for (unsigned (i (0); i < NbTests; ++i) {
time_t timer1, timer2;
time (&timer1);
FonctionToTest (ListOfParams);
time (&timer2);
//Manipulate (difftime (timer2, timer1));
VTime [i] = difftime (timer2, timer1);
}
Manipulate (MakeAvergae (VTime));
}
Renvoie la différence (en secondes) entre
MakeMedian () serait plus timer2 et timer1.
parlant!
Ou mieux si NbTest est suffisamment grand (à votre guise)
#include <ctime>

tyepedef vector<time_t> CVTime;


void Test (unsigned NbTests, unsigned NbToRemove)
{
CVTime VTime (NbTests);
for (unsigned (i (0); i < NbTests; ++i) {
time_t timer1, timer2;
time (&timer1);
FonctionToTest (ListOfParams);
time (&timer2);
VTime [i] = difftime (timer2, timer1);
}
for (unsigned i (0); i < NbToRemove / 2; ++i) {
VTime.erase (max_element (VTime.begin (), VTime.end ()));
VTime.erase (min_element (VTime.begin (), VTime.end ()));
}
Manipulate (MakeAvergae (VTime));

}
Est-ce suffisant?
NON : on n’a pas fait varier les paramètres de la fonction à
tester !!

Exemple:
On souhaite étudier l’impacte du passage par référence d’un vecteur dans
une fonction d’affichage.
void ShowVectV1 (const Vect & V);
void ShowVectV2 (const Vect V);

NbElem NbIter TempsMoyenV TempsMoyenV


1 2
1 000 100 000 0 0
10 000 100 000 0 0
100 000 100 000 0 0
10 000 000 100 000 2 3
1 000 000 000 100 000 209 245

Vous aimerez peut-être aussi