Introducción a la Computación Primer Cuatrimestre de 2012 Corrección de Algoritmos 1 Especificación, algoritmo y programa Especificación de un problema: I ¿Qué problema tenemos? I Lenguaje formal (ej. lógica de primer orden). Algoritmo: Una solución abstracta del problema (escrita para humanos). I ¿Cómo resolvemos el problema? I Pseudocódigo. Programa: Una solución concreta del problema (escrita para computadoras). I ¿Cómo resuelve la computadora el problema? I Lenguaje de programación (ej. C, Python). 2 Especificación de problemas Una especificación es un contrato con 3 partes: 1. Encabezado Indica el nombre, el tipo y número de los argumentos, y el tipo del valor de retorno (RV ). 2. Precondición Es una condición sobre los argumentos de entrada. 3. Poscondición Es una descripción del resultado del algoritmo. Si la entrada es aceptable, el algoritmo debe terminar exitosamente y la salida debe cumplir las propiedades especificadas. 3 Ejemplo de especificación Encabezado: sumarPares : A ∈ Z[] → Z Precondición: {A = A0P } Poscondición: {RV = 0≤i<|A0 |∧Par (A0 [i]) A0 [i]} donde Par (n) ≡ (∃k) n = 2 ∗ k Obs: Si no se especifica el tipo de una variable, por defecto es Z. Lo siguiente es pensar un algoritmo que satisfaga esta especificación. Lo escribimos usando pseudocógido. 4 Pseudocódigo C a = b; if (cond) {..} else {..} while (cond) {..} ! && || == >= <= return exp; Pseudocódigo a←b if (cond) {..} else {..} while (cond) {..} ¬ ∧ ∨ = ≥ ≤ RV ← exp (*) Declaración local de variables auxiliares. Declaración global de variables auxiliares. (*) En C, “return exp;” termina la ejecución de la función; en nuestro pseudocógido, “RV ← exp” es una simple asignación, tras la cual el algoritmo continúa. 5 Ejemplo de algoritmo en pseudocógido Encabezado: sumarPares : A ∈ Z[] → Z Precondición: {A = A0P } Poscondición: {RV = 0≤i<|A0 |∧Par (A0 [i]) A0 [i]} RV ← 0 i ←0 while (i < |A|) { if (A[i] mod 2 = 0) { RV ← RV + A[i] } i ←i +1 } ¿Cómo sabemos si este algoritmo es correcto respecto de la especificación? donde i ∈ Z. 6 Testing vs. corrección de algoritmos Dada una especificación E de un problema y un algoritmo A. Testing de A: Experimentación de la corrida de A para un conjunto finito de datos de entrada, donde verificamos si cumple E. Corrección de A respecto de E : Demostración formal de que el programa P transforma todos los posibles datos de entrada, en salidas de acuerdo con E . 7 Corrección de algoritmos Terna de Hoare {precondición} algoritmo {poscondición} {P} S {Q} S modifica al estado P, ¿pero lleva a Q? Una forma de probarlo: Ver que la poscondición más fuerte de ejecutar S a partir de P, implica Q. ¿sp(S, P) ⇒ Q? sp(S, P) (strongest postcondition) es el predicado más fuerte posible como consecuencia de ejecutar S a partir del estado P. 8 Asignación {P} x ← E {Q} Queremos probar que sp(x ← E , P) ⇒ Q. Definición: sp(x ← E , P) ≡ (∃y ) x = E [x : y ] ∧ P[x : y ] donde: I y es una variable no usada; I H[v : E ] es la sustitución de cada instancia en H de la variable v por la expresión E . Ejemplo: (x + y )[y : z 2 + 1] = (x + z 2 + 1). 9 Asignación: Ejemplo Probar la corrección del siguiente algoritmo respecto de su especificación: P ≡ {x > 1} x ←x +1 Q ≡ {x > 2} sp(x ← x + 1, x > 1) ≡ ≡ (∃y ) x = (x + 1)[x : y ] ∧ (x > 1)[x : y ] ≡ (∃y ) x = y + 1 ∧ y > 1 ⇒x >2 10 Secuencialización {P} S1 ;S2 {Q} Queremos probar que sp(S1 ;S2 , P) ⇒ Q. Definición: sp(S1 ;S2 , P) ≡ sp(S2 , sp(S1 , P)) 11 Secuencialización: Ejemplo P ≡ {x > 1} x ←x +1 y ←2∗x Q ≡ {y > 4} sp(x ← x + 1; y ← 2 ∗ x, P) ≡ ≡ sp(y ← 2 ∗ x, sp(x ← x + 1, P)) ≡ sp(y ← 2 ∗ x, (∃a) x = (x + 1)[x : a] ∧ (x > 1)[x : a]) ≡ sp(y ← 2 ∗ x, (∃a) x = a + 1 ∧ a > 1) ≡ (∃b) y = (2 ∗ x)[y : b] ∧ ((∃a) x = a + 1 ∧ a > 1)[y : b] ≡ (∃b) y = 2 ∗ x ∧ (∃a) x = a + 1 ∧ a > 1 ≡ y = 2 ∗ x ∧ (∃a) x = a + 1 ∧ a > 1 ⇒y =2∗x ∧x >2 ⇒y >4 12 Condicional {P} if (B) S1 else S2 {Q} Queremos probar que sp(if (B) S1 else S2 , P) ⇒ Q. Definición: sp(if (B) S1 else S2 , P) ≡ sp(S1 , B ∧ P) ∨ sp(S2 , ¬B ∧ P) ¿Qué pasa si no hay “else”? Formalmente, if (B) S1 equivale a if (B) S1 else skip, donde skip es una instrucción especial que no tiene efecto: sp(skip, P) ≡ P Por lo tanto: sp(if (B) S1 , P) ≡ sp(S1 , B ∧ P) ∨ (¬B ∧ P) 13 Condicional: Ejemplo P ≡ {a = a0 ∧ b = b0 } if (a ≤ b) { RV ← 0 } else { RV ← a − b } Q ≡ {RV = a0 −̇b0 } donde x −̇y = 0 x −y si si x ≤y x >y 14 Condicional: Ejemplo P ≡ {a = a0 ∧ b = b0 } if (a ≤ b) { RV ← 0 } else { RV ← a − b } Q ≡ {RV = a0 −̇b0 } sp(if (a ≤ b){RV ← 0}else{RV ← a − b}, P) ≡ ≡ sp(RV ← 0, a ≤ b ∧ P) ∨ sp(RV ← a − b, a > b ∧ P) ≡ ((∃x) RV = 0[RV : x] ∧(a ≤ b ∧ P)[RV : x]) ∨ ((∃y ) RV = (a − b)[RV : y ] ∧(a > b ∧ P)[RV : y ]) ≡ ((∃x) RV = 0 ∧(a ≤ b ∧ P)) ∨ ((∃y ) RV = (a − b) ∧(a > b ∧ P)) ≡ (RV = 0 ∧(a ≤ b ∧ a = a0 ∧ b = b0 )) ∨ (RV = (a − b) ∧(a > b ∧ a = a0 ∧ b = b0 )) ⇒ RV = a0 −̇b0 15 Repaso de la clase de hoy I Pseudocódigo I Testing vs. corrección de algoritmos I Terna de Hoare; poscondición más fuerte (sp). I Asignación, secuencialización, condicional. Temas de la clase que viene I Corrección de ciclos. 16