Probleme – Le triangle : partitionner, puis éprouver les limites
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 4 — Discipline de programmation, validation et test
Énoncé
Une fonction classe un triplet d'entiers en invalide, équilatéral, isocèle ou scalène.
- Écrire la fonction, avec sa spécification.
- Partitionner le domaine d'entrée et proposer un jeu de tests.
- Une version soumise remplace
a + b <= cpara + b < c. Combien de vos tests l'attrapent ? - Mesurer, sur , la proportion de triplets qui séparent les deux versions.
Corrigé
1. La fonction.
typedef enum { INVALIDE, EQUILATERAL, ISOCELE, SCALENE } classe_t;
/* Classe le triplet (a, b, c) vu comme les longueurs des cotes d'un
triangle. Rend INVALIDE si une longueur est <= 0 ou si l'inegalite
triangulaire STRICTE n'est pas verifiee (un triangle plat est refuse).
Precondition : aucune. */
classe_t classer(int a, int b, int c) {
if (a <= 0 || b <= 0 || c <= 0) { return INVALIDE; }
if (a + b <= c || a + c <= b || b + c <= a) { return INVALIDE; }
if (a == b && b == c) { return EQUILATERAL; }
if (a == b || b == c || a == c) { return ISOCELE; }
return SCALENE;
}
La spécification tranche deux questions que l'énoncé laissait ouvertes : les longueurs nulles ou négatives, et le triangle plat (). Sans elles, on ne peut pas écrire un seul test — et c'est le sens de la règle « la spécification s'écrit avant le corps ».
2. Le partitionnement, et les limites.
| Classe | Cas | Attendu | Rôle |
|---|---|---|---|
| équilatéral | équilatéral | nominal | |
| isocèle | , , | isocèle | les trois positions |
| scalène | scalène | nominal | |
| longueur nulle | invalide | limite basse | |
| longueur négative | invalide | hors domaine | |
| inégalité violée | invalide | nominal | |
| triangle plat | invalide | la limite |
Deux remarques de méthode. D'abord, il faut trois cas isocèles et non un : la condition est une disjonction de trois égalités, et une seule d'entre elles serait éprouvée par . C'est le test exhaustif d'une condition composée, appliqué. Ensuite, n'est pas un cas « bizarre » : c'est exactement la frontière entre valide et invalide, là où .
3. La version soumise, mesurée sur les onze cas :
a b c | correct | version soumise | divergence
3 3 3 | equilateral | equilateral |
3 3 5 | isocele | isocele |
3 4 5 | scalene | scalene |
1 2 3 | invalide | scalene | OUI
1 2 4 | invalide | invalide |
0 1 1 | invalide | invalide |
-1 2 2 | invalide | invalide |
5 3 3 | isocele | isocele |
3 5 3 | isocele | isocele |
2 2 3 | isocele | isocele |
5 5 8 | isocele | isocele |
Un seul test sur onze l'attrape, et c'est le triangle plat — celui qu'on aurait supprimé en le trouvant tordu. Les dix autres, y compris les trois cas isocèles et le cas négatif, sont muets.
4. Le balayage exhaustif. Sur les triplets de , séparent les deux versions, soit . Ce ne sont pas de triplets « au hasard » : ce sont exactement ceux où une des trois sommes égale le troisième côté. Un tirage aléatoire aurait donc une chance sur neuf de tomber dessus par essai — et il en faudrait une vingtaine pour être raisonnablement sûr. Un test aux limites, un seul, suffit.
Le programme officiel demande de sensibiliser « au test des limites ». La raison est mécanique : les fautes de programmation les plus fréquentes sont des erreurs d'un cran — écrit , écrit , écrit . Or une erreur d'un cran ne change le comportement que sur une valeur de chaque frontière. Au milieu d'une classe, les deux versions coïncident ; à la frontière, elles divergent.
Un jeu de tests qui ne visite que « des cas représentatifs » manque donc précisément la famille de fautes la plus probable. La bonne façon de construire un test n'est pas « quelle entrée typique ? » mais « quelle entrée séparerait ce code d'un code décalé d'un cran ? ».
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.