Probleme – De à , et la mesure qui le confirme
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 3 — Algorithmes, programmes et complexité
Énoncé
Étant donné un tableau d'entiers relatifs, on cherche la plus grande somme d'un segment (une tranche contiguë, éventuellement vide).
- Écrire l'algorithme naïf. Quelle est sa complexité ?
- Écrire un algorithme en un seul parcours, et prouver sa correction par un invariant.
- Mesurer les deux, et vérifier les ordres de grandeur sur les rapports.
- Que prouve cette mesure, et que ne prouve-t-elle pas ?
Corrigé
1. L'algorithme naïf.
/* Renvoie la plus grande somme d'un segment de t[0..n-1]. Le segment
vide etant admis, le resultat est toujours >= 0.
Precondition : n >= 0. */
long max_segment_naif(const int t[], int n) {
long meilleur = 0;
for (int i = 0; i < n; i = i + 1) {
long s = 0;
for (int j = i; j < n; j = j + 1) {
s = s + t[j]; /* somme de t[i..j] */
if (s > meilleur) { meilleur = s; }
}
}
return meilleur;
}
Il examine les segments, en temps constant chacun grâce à la somme accumulée : en temps, en espace. (Une version qui recalculerait chaque somme de zéro serait en — c'est la faute la plus fréquente sur cet exercice.)
2. L'algorithme en un parcours.
/* Meme specification. Complexite : Theta(n) en temps, Theta(1) en espace. */
long max_segment(const int t[], int n) {
long meilleur = 0, courant = 0;
/* INVARIANT (avant le tour i) :
meilleur = plus grande somme d'un segment de t[0..i-1] ;
courant = plus grande somme d'un segment SE TERMINANT en i-1
(le segment vide comptant pour 0). */
for (int i = 0; i < n; i = i + 1) {
courant = courant + t[i];
if (courant < 0) { courant = 0; }
if (courant > meilleur) { meilleur = courant; }
}
return meilleur;
}
Correction. Initialisation : avant le tour , les deux tranches sont vides et les deux variables valent .
Conservation. Soit l'invariant vrai avant le tour . Un segment se terminant en est, ou bien le segment vide (somme ), ou bien un segment se terminant en auquel on ajoute . Le meilleur de la seconde famille vaut donc , et le meilleur des deux familles vaut — précisément ce que calculent les deux premières lignes du corps. Ensuite, le meilleur segment de est ou bien inclus dans (valeur meilleur), ou bien se termine en (valeur courant recalculée) : le maximum des deux est ce que pose la troisième ligne.
Utilisation : à la sortie, et meilleur est la plus grande somme d'un segment de . Terminaison : variant .
Vérification. Sur , les deux fonctions rendent — c'est le segment . Sur , les deux rendent : le segment vide, conformément à la spécification. Ce second cas est le test qui compte : c'est le seul endroit où une lecture distraite de l'invariant donne au lieu de .
3. La mesure.
n | naif (ms) | Kadane (ms) | operations naif | operations Kadane
1000 | 0,355 | 0,00103 | 500500 | 1000
2000 | 1,414 | 0,00208 | 2001000 | 2000
4000 | 5,656 | 0,00417 | 8002000 | 4000
8000 | 22,531 | 0,00837 | 32004000 | 8000
16000 | 90,087 | 0,01676 | 128008000 | 16000
Les rapports de temps entre deux lignes consécutives :
| doublé de | à | à | à | à |
|---|---|---|---|---|
| naïf | ||||
| un parcours |
Doubler multiplie l'un par et l'autre par : c'est la signature de et de , lue directement sur les mesures. À , le rapport entre les deux programmes atteint .
4. Ce que la mesure prouve, et ce qu'elle ne prouve pas.
Elle prouve que la transcription est fidèle : si le code écrit ne réalisait pas l'algorithme analysé, les rapports ne seraient pas et . C'est un contrôle puissant et rarement pratiqué — et c'est pourquoi il faut regarder les rapports et non les temps absolus, qui ne disent rien hors de cette machine.
Elle ne prouve pas que les fonctions sont correctes : elles pourraient rendre toutes deux la même valeur fausse, et les rapports seraient inchangés. Elle établit mal, aussi, la différence entre et : un coût quasi-linéaire donnerait sur cette plage des rapports allant de à , et il faut des mesures très propres pour les séparer de . Les nôtres, à et , y parviennent — mais de justesse, et une machine chargée aurait suffi à brouiller la conclusion. Seule la lecture du code établit qu'il y a une boucle et une seule.
Preuve et mesure ne se remplacent donc pas : la preuve dit ce que fait l'algorithme, la mesure dit que le programme est bien celui-là. C'est la répartition des rôles du chapitre chap:discipline, et elle vaut pour tout le livre.
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.