Chapitre 11 : Vérification d`un algorithme

publicité
Chapitre 11 : Vérification d’un algorithme
• Problème général:
Supposons qu’un algorithme A effectue un certain
traitement sur des données, il faut prouver
mathématiquement que
• étant donné les hypothèses sur ces données
• A calcule effectivement le bon résultat
• Pour faire une preuve mathématique il faut:
– Pouvoir formaliser
• les hypothèses,
• le résultat escompté
• Et de façon plus générale l’état d’un programme n’importe où lors
de l’exécution
– Avoir une démarche mathématique pour faire la preuve.
1
Le problème de la preuve de programmes
• Pour cela,
– on s’aide de la logique des prédicats (ou logique du premier
ordre).
– un programme sera vu comme un « transformateur » de
formules logiques.
• Exemple: le « formule » suivante : {x=3} x=x+2 {x=5}
Attention ici x=3 est
une formule logique
vraie si x vaut 3; ce n’est
pas une assignation C++ !!
Etat avant
l’exécution
Etat après
l’exécution
se lit: si x vaut 3 avant l’instruction x=x+2,
2
alors après
l’instruction x vaut 5
Le problème de la preuve de programmes
• Attention: toute formule logique peut a priori être vrai ou fausse; ainsi
on peut voir que
– {x=3} x=x+2 {x=5} est vraie
– {x=0} x=x+2 {x=5} est fausse
• Le problème de la preuve de programme sera de vérifier si la
« formule »:
{hypothèses de départ} Algorithme {résultat}
est bien vraie
3
Rappel de logique: le calcul des propositions
• Syntaxe:
– Soit un ensemble P de propositions p, q, r, …
– L’ensemble des formules f possibles en logique
des propositions est :
}
f ::= p (pour n’importe quelle proposition p)
f ::= ! f
négation
f ::= f op f
f ::= ( f )
implications
::=
signifie
peut être
réécrit en
avec
op ::= v | ¶ | f | a | j
ou
inclusif
et
4
|
signifie ou
• vers le droite
• vers la gauche
• double
Exemples de formules du calcul des propositions
– p ¶ (q f r)
–pj!r
5
Rappel de logique: le calcul des propositions
• Sémantique:
– Une formule peut être vraie (True) ou fausse (False) (on dit
que son domaine sémantique est l’ensemble {True ,False}
– Ayant par exemple la formule f ≠ p ¶ (q f r), en donnant
une valeur True ou False à chaque proposition (p, q et r),
l’interprétation de la formule c’est-à-dire son évaluation
est soit Vraie soit Fausse.
– C’est en regardant la sémantique de chaque opérateur (via
sa table de vérité) que l’on peut interpréter une formule.
• Exemple: si v(p)=True, v(q)=True, v(r)=False donne v(f) =
False.
{
• En effet True ¶ ( True f False) vaut False
6
False
Rappel de logique: le calcul des propositions
• Définitions: une formule logique est
– consistante si elle est vraie pour au moins une
interprétation
– Valide ou est une tautologie si elle est vraie pour toues les
interprétations.
– Deux formules f1 et f2 sont (logiquement) équivalentes
(dénoté f1 ¤ f2 si pour toute interprétation v : v(f1) = v(f2)
c’est-à-dire si f1 j f2 est toujours vrai
– Si f1 f f2 est toujours vrai on dit que f1 implique
logiquement f2 (dénoté f1 fi f2)
– Priorités des opérateurs logiques : ( < = « plus prioritaire
que »)
! < v , ¶ < f , a , j < ¤, fi
7
¤ et fi sont des
méta-opérateurs
Exemple de formules logiquement équivalentes
1.
2.
3.
4.
5.
!! f ¤ f
f∧f¤f¤f∨f
f1 ∧ f2 ¤ f 2 ∧ f1
f1 ∨ f2 ¤ f 2 ∨ f1
f1 j f2 ¤ f2 j f1
6.
7.
f1 ∧ (f2 ∧ f3) ¤ (f1 ∧ f2) ∧ f3
f1 ∨ (f2 ∨ f3) ¤ (f1 ∨ f2) ∨ f3
8. T ∧ f ¤ f
9. T ∨ f ¤ T
Lois de De Morgan
{
8
10. F ∧ f ¤ F
11. F ∨ f ¤ f
12. f1 f f2 ¤ !f1 ∨ f2
13. f1 f T ¤ T
14. f1 f F ¤ !f1
15. T f f2 ¤ f2
16. F f f2 ¤ T
17. !(f1 ∨ f2 ) ¤ (!f1 ) ∧ (! f2)
18. !(f1 ∧ f2 ) ¤ (!f1 ) ∨ (! f2)
Rappel de logique: la logique des prédicats
• Appelée également logique du premier ordre
• On rajoute
– des variables (simples ou indicées)
– des opérateurs arithmétiques et relationnels
– des quantificateurs A (pour tout) et E (il existe)
• Syntaxe:
– Ax f(x) formule lue : pour tout x on a f(x)
– Ex f(x) formule lue : il existe x tel que f(x)
Variable x liée par le quantificateur
Par opposition aux variables libres
9
(voir exemples)
Exemples de formules de logique des prédicats
1.
2.
3.
4.
5.
x < y + 1 // x et y sont des variables libres : formule consistante
x=7jy=3
// idem
Ax (x.y=0 f x=0) // x est lié par le Ax et y est libre: f consistante
Ax (x.y = 0 f x = 0 ∨ y = 0) // x lié par le Ax, y libre : f valide
Ax (0 ¯ x < 0 f x = 7) // x est une variable liée f valide !!
La formule 5. est trivialement valide !
Les variables libres seront
les variables de notre algorithme:
on décrira ainsi l’état
10 d’un algorithme
Implication et équivalence logique
•
•
Ces notions sont étendues
Exemples:
1.
Ax f(x) ¤ ! Ex !f(x)
2. E x f(x) ¤ ! A x !f(x)
3.
Si le domaine des valeurs possibles pour x n’est pas vide:
Ax f(x) fi Ex f(x)
4. E x (f1(x) ∨ f2(x)) ¤ (E x f1(x)) ∨ (E x f2(x))
5.
A x (f1(x) ∧ f2(x)) ¤ (A x f1(x)) ∧ (A x f2(x))
11
Preuve partielle et totale
•
•
•
Il est généralement impossible de tester complètement un
algorithme (pour toutes les données possibles)
Si on veut être sûr que l’algorithme est correct il faut se
rabattre sur des méthodes analytiques (mathématiques)
pour le vérifier.
Démarche de la preuve de correction d’un algorithme
1.
2.
3.
Spécifier: exprimer le propriétés mathématiques / logiques sur les
variables du programme, avant et après son exécution
prouver la correction partielle : si l’algorithme se termine, il
vérifie la spécification
prouver la correction totale : l’algorithme se termine
12
Exemple de preuve de programme: le pgcd
• Spécification
– Avant l’exécution: Pré-condition x et y sont des entiers
positifs.
– Après l’exécution on veut comme résultat : Post-condition
x contient le pgcd des 2 valeurs initialement introduites
– Problème:
• x et y changent de valeur dans le programme et on veut qu’à la fin
x = pgcd(x initial, y initial)
– Solution:
• on suppose qu’il existe 2 instructions au début de l’exécution:
x0 = x ;
y0 = y ;
• et on veut qu’à la fin fin x = pgcd(x0 , y0 ) avec x0 et y0 contenant
toujours les valeurs initiales
13
Exemple de preuve de programme: le pgcd
• Spécification
{x˘
˘0 ∧ y˘
˘0 ∧ x=x0 ∧ y=y0}
while (y > 0)
{
int Save = y;
y = x % y;
x = Save;
}
{x = pgcd(x0,y0)}
Précondition
Code à
vérifier
Postcondition
14
Exemple de preuve de programme: le pgcd
Il faut faire la
• Preuve partielle :
si l’algorithme se termine, il vérifie la spécification
•
Preuve totale :
(en plus de la preuve partielle) l’algorithme se termine
toujours = preuve de terminaison
Le problème c’est les boucles (while, for, do)!
15
Exemple de preuve de programme: le pgcd
•
Preuve partielle :
Supposons que le code soit une boucle while instruction
dont on connait la précondition déterminant l’état des
variables avant la boucle et la postcondition c’est-à-dire le
résultat désiré dans les variables après cette boucle (comme
pour le pgcd)
•
•
•
Pour faire la preuve partielle :
il faut attacher à chaque boucle un invariant c’est-à-dire une
affirmation (sous forme de formule logique) appelée aussi
assertion vraie à chaque fois que l’exécution du
programme atteint le test de la condition.
En particulier, si l’exécution atteint cette boucle, l’invariant
doit initialement être vrai.
De plus, l’invariant et la négation de la condition de
16
la boucle doit impliquer la postcondition
Preuve partielle pour le pgcd
•
•
•
•
Formellement notons:
Pré : la précondition de la boucle
Post : la postcondition de la boucle
I : un invariant de la boucle
•
C : la condition de la boucle
•
Il faut :
–
–
// initialement l’invariant est vrai
Pré fi I
I ∧! C fi Post // à la sortie de la boucle on a la résultat désiré
–
Et pour que I soit un invariant, il faut : {I ∧ C} instruction {I}
qui exprime que ayant {I ∧ C} comme précondition à instruction,
alors {I} est une bonne postcondition de instruction.
17
Preuve partielle pour le pgcd
•
•
Formellement pour pgcd on peut prendre:
Pré ≡ x˘˘0 ∧ y˘˘0 ∧ x=x0 ∧ y=y0
•
•
Post ≡ x = pgcd(x0,y0)
I ≡ : x˘˘0 ∧ y˘˘0 ∧ pgcd(x0,y0) = pgcd(x,y)
•
C≡y > 0
•
Il faut :
–
–
Pré fi I
I ∧! C fi Post
–
{I ∧ C} instruction {I} !
!
!
En vertu de la règle:
• pgcd(x,0) = x
En vertu des 2 règles:
• x ˘ 0 ∧ y>0 fi x%y ˘ 0
• x18˘ 0 ∧ y>0 fi pgcd(x,y) = pgcd(y, x%y)
Preuve totale pour le pgcd
•
•
Il faut prouver que l’algorithme se termine
toujours = preuve de terminaison
Démarche: associer une fonction entière f
(appelée fonction de terminaison) sur les
(certaines) valeurs des variables du programmes:
f : {valeurs des variables} f N
telle que:
La valeur de f a
–
I∧Cfif˘0
–
{f = f0 ∧ I ∧ C} instruction {f < f0}
décru après
l’instruction
f0 = la valeur de f avant
l’exécution de l’instruction
19
Preuve totale pour le pgcd
•
•
La conséquence d’une telle définition pour la
fonction de terminaison f est qu’on est sûr que
partant de tout entier positif, après un nombre
fini d’itérations, on doit sortir de la boucle
Fonction de terminaison pour pgcd: y
–
–
y décroît strictement à chaque itération
y est positive tant que l’invariant est vérifié
Donc l’algorithme pgcd est
totalement correct ☺
20
Exemple 1: Recherche du minimum
Précondition
Invariant
Fonction de
terminaison
Postcondition
{0<n}
int IndProv = 0;
int i=1;
¯IndProv<i ∧
{0<i¯
¯n ∧ 0¯
Aj(0¯
¯j<i f V[IndProv] ¯V[j]}
{n-i}
while (i<n)
{
if (V[i] < V[IndProv])
IndProv = i;
++i
}
{0¯
¯ IndProv <n ∧
Aj(0¯
¯j<n f V[IndProv]¯
¯V[j]}
21
Exemple 2: Image miroir
Valeur
initiale de V
Précondition
Invariant
{0<n ∧ Aj(0¯
¯j<n f V[j]=V0[j]}
int i=0;
{0¯
¯j<i f V[j]=V0[n-1-j] ∧ V[n-1-j]=V0[j])
¯i¯
¯n/2 ∧ Aj(0¯
∧ Aj(i¯
¯j¯
¯n-1-i f V[j]=V0[j]}
{n/2-i}
Fonction de
terminaison
Postcondition
while (i<n/2)
{
int Save = V[i];
V[i] = V[n-1-i];
V[n-1-i]=Save;
++i;
}
{0<n ∧ Aj(0¯
¯j<n f V[j]=V0[n-1-j]}
22
Exemple 3 : Tri par sélection
Précondition
Invariant 2
Fonction de
terminaison 2
Invariant 1
Fonction de
terminaison 1
{0<n ∧ Aj(0¯
¯j<n f V[j]=V0[j]}
{0¯
¯k<i-1 f V[k]¯
¯V[k+1])
¯i<n ∧ Ak(0¯
∧ Am,k(0¯
¯V[k]
¯m<i¯
¯k<n f V[m]¯
∧ E une bijection f A j(0¯
¯j<n f (V[j]=V0[f(j)])}
{n-1-i}
for (int i = 0; i<n-1; ++i)
{
elem Save;
int min = i;
{i<j¯
¯n ∧ i¯
¯min<j ∧ Ak(0¯
¯k<j f V[min] ¯V[k]}
{n-j}
for (int j = i+1; j<n; ++j)
if (V[j]<V[min])
min = j;
E une bijection f nécessite
la logique de second ordre et n’est
pas complètement formalisé ici!
Save = V[min];
V[min] = V[i];
V[i] = Save;
Postcondition
Valeur
initiale de V
}
{0<n
∧ Ak(0¯
¯k<n-1 f V[k]¯
¯V[k+1]
¯j<n f (V[j]=V0[f(j)])}
∧ E une bijection f A j(0¯
23
Exemple 3 : Tri par insertion
Précondition
Invariant 2
Fonction de
terminaison 2
Invariant 1
Fonction de
terminaison 1
Valeur
initiale de V
∧ Aj(0¯
¯j<n f V[j]=V0[j]}
{0<i¯
¯k<i-1 f V[k]¯
¯V[k+1])
¯n ∧ Ak(0¯
∧ E une bijection f A j(0¯
¯j<n f (V[j]=V0[f(j)])}
{0<n
{n-i}
for (int i = 1; i<n; ++i)
{
int j;
elem Save = V[i];
{-1<j¯
¯i-1 ∧ Ak(j+2¯
¯k¯
¯i f V[k]>Save}
{j+1}
for (j = i-1; j>=0 && V[j] > Save; --j)
V[j+1] = V[j];
V[j+1] = Save;
}
{0<n ∧ Ak(0¯
¯k<n-1 f V[k]=V[k+1]
∧ E une bijection f A j(0¯
¯j<n f (V[j]=V0[f(j)])}
E une bijection f nécessite
Postcondition
24
la logique de second ordre et n’est
pas complètement formalisé ici!
Téléchargement