Descargar la presentación
La descarga está en progreso. Por favor, espere
Publicada porGermán Contreras Jiménez Modificado hace 7 años
1
Demostración de correctitud y terminación de ciclos
17 de octubre de 2014
2
Que vimos hasta ahora: Entre otras cosas vimos: Transición de estados Demostración de instrucciones “if” Teorema del Invariante Demostración de correctitud y terminación de ciclos
3
Agenda Otro ejemplo de demostración de correctitud y terminación de ciclos utilizando los cinco puntos del Teorema del Invariante: La precondición implica el invariante El cuerpo del ciclo preserva el invariante La postcondición vale al final La función variante es monótona decreciente El ciclo termina
4
Problema: filtrarMultiplos
Dado un arreglo de enteros “a”, planteamos el problema de filtrar en “a” todos los elementos que son múltiplos de un número “m” dado, y pasar a otro arreglo “b” en las mismas posiciones todos los elementos de “a” que no son múltiplos de “m”, y adicionalmente obtener la suma de los elementos que quedaron en “a”. Ejemplo: 3 1 7 6 pre(a): m: 3 3 6 a: 1 7 b: suma: = 12
5
Especificación: filtrarMultiplos
Problema filtrarMultiplos (a:[ℤ], b:[ℤ], n : ℤ, m : ℤ, suma : ℤ) { requiere n == |a| == |b| ; requiere m > 0 ; requiere ∀(x ← a) x > 0 ; modifica a, b, suma ; asegura a == [if x mod m==0 then x else 0 | x ← pre(a)] ; asegura b == [if x mod m==0 then 0 else x | x ← pre(a)] ; asegura suma == ∑ [x | x ← pre(a), x mod m == 0] ;
6
Implementación: filtrarMultiplos
void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ suma = 0; int i = 0; while (i < n) { if (a[i] % m == 0) { suma = suma + a[i]; b[i] = 0; } else { b[i] = a[i]; a[i] = 0; } i++;
7
Puntos a Demostrar Definimos: Demostramos la correctitud del ciclo:
Estados: Ei Predicados: P, Pc, Pif, Qif, Qc, Q Invariante: I Guarda: B Función Variante: v Cota: c Demostramos la correctitud del ciclo: La precondición implica el invariante: Pc I El cuerpo del ciclo preserva el invariante: {I ^ B} cuerpo {I} La postcondición vale al final: I ^ ¬B Qc La función variante es monótona decreciente El ciclo termina: (I ^ v ≤ c) ¬B
8
Transición de Estados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: // estado Einicial; suma = 0; int i = 0; // Pc: // estado E0; while (i < n) { // I: // B: // v: , c: // estado E1; // vale I & B // Pif: if (a[i] % m == 0) { suma = suma + a[i]; b[i] = 0; } else { b[i] = a[i]; a[i] = 0; } // Qif: // estado E2; // vale Qif i++; // estado E3; // Qc: // estado E4; // vale Qc // Q:
9
Predicados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0;
10
Predicados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0; // implica Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0
11
Predicados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0; // implica Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0 // estado E0; while (i < n) { // I: (0 <= i <= n) // && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then // a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-[i..n)) a[k]==pre(a)[k] // && suma==sum(a[0..i)) // && n == |a| == |pre(a)| if (a[i] % m == 0) { suma = suma + a[i]; b[i] = 0; } else { b[i] = a[i]; a[i] = 0; }
12
Predicados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0; // implica Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0 // estado E0; while (i < n) { // I: (0 <= i <= n) // && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then // a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-[i..n)) a[k]==pre(a)[k] // && suma==sum(a[0..i)) // && n == |a| == |pre(a)| // B: i < n // v: n – i, c: 0 // estado E1; // vale I & B if ...
13
Predicados void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0; // implica Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0 // estado E0; while (i < n) { // I: (0 <= i <= n) // && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then // a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-[i..n)) a[k]==pre(a)[k] // && suma==sum(a[0..i)) // && n == |a| == |pre(a)| // B: i < n // v: n – i, c: 0 // estado E1; // vale I & B if ... i++ } // Qc: (paratodo k<-[0..n)) if (pre(a)[k] % m == 0) then // && suma==sum(a[0..n)) // estado E4; // vale Qc
14
Predicados asegura a == [if x mod m==0 then x else 0 | x ← pre(a)] ;
void filtrarMultiplos (int a[], int b[], const int n, const int m, int &suma){ // P: n == |a| == |b| && m > 0 && (paratodo x <- a) x > 0 && // a==pre(a) && b==pre(b) && suma=pre(suma) // estado Einicial; suma = 0 int i = 0; // implica Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0 // estado E0; while (i < n) { // I: (0 <= i <= n) // && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then // a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-[i..n)) a[k]==pre(a)[k] // && suma==sum(a[0..i)) // && n == |a| == |pre(a)| // B: i < n // v: n – i, c: 0 // estado E1; // vale I & B if ... i++ } // Qc: (paratodo k<-[0..n)) if (pre(a)[k] % m == 0) then // && suma==sum(a[0..n)) // estado E4; // vale Qc // Q: (paratodo k<-[0..n)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] else a[k]==0 // && (paratodo k<-[0..n)) if (pre(a)[k] % m == 0) then b[k]==0 else b[k]==pre(a)[k] // && suma==sum([pre(a)[k] | k <- [0..n), pre(a)[k] % m == 0]) asegura a == [if x mod m==0 then x else 0 | x ← pre(a)] ; asegura b == [if x mod m==0 then 0 else x | x ← pre(a)] ; asegura suma == ∑ [x | x ← pre(a), x mod m == 0] ;
15
La precondición implica el invariante: Pc I
Precondición del ciclo Pc: suma==0 && i==0 && a==pre(a) && n==|a|==|b| && m>0 Invariante I: (0 <= i <= n) (1) && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] && b[k]==0 (2) else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-[i..n)) a[k]==pre(a)[k] (3) && suma==sum(a[0..i)) (4) && n == |a| == |pre(a)| (5) Asumiendo que Pc se cumple, veamos que vale cada una de las partes de I. Como vale Pc: Como i==0 en Pc, entonces 0<=i. Además, como n==|a| en Pc, entonces n>=0 porque la longitud de un arreglo no puede ser negativa. Y como además i==0, implica i==0<=n, implica i<=n. Entonces (0 <= i <= n) es verdadero. Como i==0, paratodo ( k <- [0..i) ) se puede escribir paratodo ( k <- [0..0) ) que siempre es verdadero porque [0..0) es un intervalo vacío. Como i==0, paratodo ( k <- [i..n) ) a[k]==pre(a)[k] se puede escribir paratodo ( k <- [0..n) ) a[k]==pre(a)[k]. Luego como n==|a| y k está en rango de a, la expresión anterior se reduce a a==pre(a), que es verdadero por Pc. Como i==0, entonces suma==sum( a[0..i) ) se puede escribir como suma == sum( a[0..0) ) == sum( a[] ) == 0 que es verdadero por Pc. De Pc sabemos que n == |a| == |pre(a)|, entonces es verdadero. Y como demostramos que todas las partes son verdaderas, entonces implica I.
16
El cuerpo del ciclo preserva el invariante: {I ^ B} cuerpo {I}
Invariante I: (0 <= i <= n) (1) && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] && b[k]==0 (2) else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-[i..n)) a[k]==pre(a)[k] (3) && suma==sum(a[0..i)) (4) && n == |a| == |pre(a)| (5) Guarda B: i < n Asumiendo que en E1 vale I y vale B, hay que demostrar que en E3 también vale I. A continuación analizamos cada uno de los estados: E1, E2, E3. // estado E0; while (i < n) { // estado E1; if (a[i] % m == 0) { // estado Eif1; suma = suma + a[i]; // estado Eif2; b[i] = 0; // estado Eif3; } else { // estado Eelse1; b[i] = a[i]; // estado Eelse2; a[i] = 0; // estado Eelse3; } // estado E2; i++; // estado E3; // estado E4;
17
Estado E1: vale I && B // estado E0; while (i < n) { // estado E1; vale I ^ B; // por B, implica i < n; // implica (0 <= i < n) (1) // && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) // then a[k]==pre(a)[k] && b[k]==0 (2) // else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-[i..n)) a[k]==pre(a)[k] (3) // && suma==sum(a[0..i)) (4) // && n == |a| == |pre(a)| (5) // Pif: I ^ B if (a[i] % m == 0) { // estado Eif1; suma = suma + a[i]; // estado Eif2; b[i] = 0; // estado Eif3; } else { // estado Eelse1; b[i] = a[i]; // estado Eelse2; a[i] = 0; // estado Eelse3; } …
18
Estado E2 while (i < n) { // Pif: I ^ B if (a[i] % m == 0) { suma = suma + a[i]; b[i] = 0; } else { b[i] = a[i]; a[i] = 0; } // Qif: && // ( (paratodo k<-[0..i)) && ) && // ( (paratodo k<-(i..n)) a[k]==pre(a)[k] && b[k]==pre(b)[k] ) && // (pre(a)[i]%m==0 && && b[i]==0 && || // (pre(a)[i]%m!=0 && && a[i]==0 && && // n==|a|==|b| // estado E2; vale Qif; // implica (0 <= i < n) // && (paratodo k<-[0..i]) if (pre(a)[k] % m == 0) // then a[k]==pre(a)[k] && b[k]==0 // else b[k]==pre(a)[k] && a[k]==0 // && (paratodo k<-(i..n)) a[k]==pre(a)[k] // && suma==sum(a[0..i]) // && n == |a| == |pre(a)| i++; De los “vale” del estado E2, queremos llegar a alguna expresión que nos acerque a nuestra definición del invariante. Por ejemplo, como vale Qif y en Qif vale podemos derivar la expresión (2). Ver documento pdf con el detalle de los pasos realizados). (1) (2)
19
Estado E3 Por lo tanto queda demostrado que se preserva el invariante.
while (i < n) { if (a[i] % m == 0) { suma = suma + a[i]; b[i] = 0; } else { b[i] = a[i]; a[i] = 0; } i++; // estado E3; // vale + 1 && // && // && // En E3 vale i == + 1, Entonces == i – 1 En E3 vale y en E3 vale en E3 vale Por expresión a la que llegamos en E2, reemplazando tenemos (0 <= < n) && (paratodo if (pre(a)[k] % m == 0) then && else && && (paratodo && && n == |a| == |pre(a)| (4) Reemplazando (1) en (3) tenemos (0 <= i-1 < n) && (paratodo k<-[0..i-1]) if (pre(a)[k] % m == 0) && (paratodo k<-(i-1..n)) && (5) Reemplazando (2) en (4) tenemos then a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-(i-1..n)) a[k]==pre(a)[k] && suma==sum(a[0..i-1]) (6) Como último paso queda (0 <= i <= n) (por propiedad de Z) && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) (por propiedad de secuencias) && (paratodo k<-[i..n)) a[k]==pre(a)[k] (por propiedad de secuencias) && suma==sum(a[0..i)) (por propiedad de secuencias) Por lo tanto queda demostrado que se preserva el invariante.
20
La postcondición vale al final: I ^ ¬B Qc
Invariante I: (0 <= i <= n) (1) && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] && b[k]==0 (2) else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-[i..n)) a[k]==pre(a)[k] (3) && suma==sum(a[0..i)) (4) && n == |a| == |pre(a)| (5) Negación de la Guarda B: i >= n Postcondición del ciclo Qc: (paratodo k<-[0..n)) then a[k]==pre(a)[k] && b[k]==0 && suma==sum(a[0..n)) && n == |a| == |pre(a)| Por (1) en la expresión del invariante y por la negación de la guarda tenemos: (0 <= i <= n) && (i>=n), entonces i==n. Reemplazando en la fórmula del invariante, queda: i==n && (paratodo k<-[0..n)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] && b[k]==0 else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-[n..n)) a[k]==pre(a)[k] && suma==sum(a[0..n)) && n == |a| == |pre(a)| La sub expresión (paratodo k<-[n..n)) a[k]==pre(a)[k] se puede eliminar por ser lista vacía. Y por lo tanto llegamos a la expresión de Qc, que es lo que queríamos demostrar.
21
La función variante es monótona decreciente
¿Cuánto vale v==n-i en el estado E3? == (n – == n – porque n no se modifica. Por otro lado sabemos que: == y: == entonces: == Reemplazando: == n – ( + 1) == n – – 1 == (n – – 1 == – 1 < Por lo tanto queda demostrado que v decrece. Función variante v: n-i, c:0 Estado E1 vale I ^ B Estado E3 vale + 1 && && &&
22
El ciclo termina: (I ^ v ≤ c) ¬B
Invariante I: (0 <= i <= n) (1) && (paratodo k<-[0..i)) if (pre(a)[k] % m == 0) then a[k]==pre(a)[k] && b[k]==0 (2) else b[k]==pre(a)[k] && a[k]==0 && (paratodo k<-[i..n)) a[k]==pre(a)[k] (3) && suma==sum(a[0..i)) (4) && n == |a| == |pre(a)| (5) Función variante v=n-i, c:0 (6) Negación de la Guarda B: i >= n Asumimos que vale I && v <=c Luego, como por (6) tenemos v: n-i y c:0 Reemplazando nos queda n-i <=0 que implica n <= I que es la negación de la guarda.
23
Conclusión Con estas demostraciones que cubren los cinco puntos del Teorema del Invariante podemos concluir que el ciclo es correcto según la especificación y termina. Queda como ejercicio la demostración de Pif Qif y de P Q Para más detalles ver el PDF complementario con la resolución.
Presentaciones similares
© 2025 SlidePlayer.es Inc.
All rights reserved.