Composición, invariantes y condicionales de Hoare
Pregunta central
¿Cómo se demuestra paso a paso que una secuencia de asignaciones o un condicional transforma cualquier estado inicial permitido en un estado que satisface la postcondición?
Mapa de la clase
flowchart LR A["Axioma de asignación"] --> WP["Calcular precondiciones hacia atrás"] WP --> SEC["Composición secuencial"] SEC --> R["Aserciones intermedias"] SEC --> INV["Propiedades preservadas"] INV --> IP["Invariante de programa"] A --> IF["Reglas para condicionales"] IF --> T["Rama verdadera"] IF --> F["Rama falsa"] T --> Q["Postcondición común Q"] F --> Q
La idea unificadora es que la ejecución avanza de izquierda a derecha, pero la demostración suele construirse de derecha a izquierda. Se parte de la postcondición deseada, se calcula qué debía ser cierto antes de la última instrucción y se repite hasta llegar al inicio. Cuando el flujo se divide por una condición, se demuestra cada camino por separado y ambos deben terminar garantizando la misma postcondición. (Diap. 1–19; láminas 82–100.)
1. Herramienta básica: sustitución en una asignación
Para una asignación , el axioma de asignación establece:
significa sustituir en cada aparición libre de por ; se lee “ con sustituida por ”. El resultado es la precondición más débil que garantiza .
Sentido de la sustitución
No se “despeja el programa”. Se imagina el valor que tendrá la variable después de la asignación y se reemplaza ese valor dentro de la postcondición.
flowchart RL Q["Postcondición Q"] --> S["Sustituir V por E"] S --> WP["Q con E en lugar de V"] WP --> H["Precondición de V := E"]
En las fórmulas siguientes, representa la aserción vacía { }: no impone ninguna restricción y es verdadera para todo estado.
2. Ejercicios de asignación
Ejemplo a: precondición de
Se quiere completar:
Paso 1. Identificar , y .
Paso 2. Sustituir por :
Paso 3. Simplificar:
Por tanto:
flowchart RL Q["Se desea j mayor que 0"] --> S["Sustituir j por i + 1"] S --> I["i + 1 mayor que 0"] I --> P["i mayor que -1"]
Comprobación: produce ; produce , que no cumple la desigualdad estricta.
Ejemplo b: precondición de
Paso 1. Sustituir por :
Paso 2. Resolver:
Así:
flowchart TD Q["Se desea y mayor que 1"] --> S["Sustituir y por x al cuadrado"] S --> A["x al cuadrado mayor que 1"] A --> B{"¿En qué región está x?"} B -->|"x menor que -1"| OK1["Sirve"] B -->|"x entre -1 y 1"| NO["No sirve"] B -->|"x mayor que 1"| OK2["Sirve"]
Los extremos y se excluyen porque producen , no .
Ejemplo c: postcondición de
Para distinguir momentos, escribimos para el valor inicial y para el final:
- .
- La asignación produce .
- .
- Por tanto, .
flowchart LR S0["Inicio: x0 mayor que 2"] --> C["x := x al cuadrado"] C --> E["x1 = x0 al cuadrado"] E --> Q["Final: x1 mayor que 4"]
Note
En una terna se reutiliza normalmente , pero evita confundir el valor anterior con el posterior.
Ejemplo d: precondición de
Paso 1. Sustituir por : .
Paso 2. Incluir el dominio: .
Paso 3. Analizar signos:
Por tanto:
flowchart TD X["Valor inicial x"] --> D{"¿x es cero?"} D -->|"Sí"| U["La división no está definida"] D -->|"No"| SG{"¿x es positivo?"} SG -->|"Sí"| POS["El recíproco es positivo; cumple"] SG -->|"No"| NEG["El recíproco es negativo; no cumple"]
3. Concatenación de código
La concatenación ejecuta primero y después . El estado final de se convierte en el inicial de . La regla de composición es:
es la aserción intermedia.
flowchart LR P["P"] --> C1["Ejecutar C1"] C1 --> R["Condición intermedia R"] R --> C2["Ejecutar C2"] C2 --> Q["Q"]
Para más instrucciones se aplica la regla repetidamente. Las aserciones intermedias suelen calcularse desde hacia atrás.
4. Ejemplo: mitad de una suma
Demostrar:
Paso 1. Última instrucción. Sustituir por :
Paso 2. Primera instrucción. Sustituir por :
Paso 3. Componer (2) y (1):
flowchart LR P["Sin restricción"] --> A["c := a + b"] A --> R["c = a + b"] R --> B["c := c / 2"] B --> Q["c = (a + b) / 2"] Q -. "verificar hacia atrás" .-> R R -. "verificar hacia atrás" .-> P
La tautología explica por qué no hace falta restringir o , suponiendo que dividir entre esté definido.
5. Ejemplo: construir
Se supone que no cambia.
Paso 1. Retroceder por :
Paso 2. Retroceder por :
Paso 3. Retroceder por :
Paso 4. Componer (3), (2) y (1).
flowchart LR P["Sin restricción"] --> C1["s := 1"] C1 --> R1["s = 1"] R1 --> C2["s := s + r"] C2 --> R2["s = 1 + r"] R2 --> C3["s := s + r al cuadrado"] C3 --> Q["s = 1 + r + r al cuadrado"]
Cada aserción intermedia registra cuánto de la postcondición ya se construyó.
6. Ejemplo: intercambiar variables
y representan valores iniciales y no cambian.
Paso 1. Antes de :
Paso 2. Antes de :
Paso 3. Antes de :
Paso 4. Concatenar (3), (2) y (1).
flowchart LR S0["Inicio: a = A; b = B"] --> C1["h := a"] C1 --> S1["h = A; b = B"] S1 --> C2["a := b"] C2 --> S2["a = B; h = A"] S2 --> C3["b := h"] C3 --> S3["Final: a = B; b = A"]
conserva el valor inicial de , que se perdería al ejecutar .
7. Invariante de un programa
Un invariante de programa para un bloque es una aserción preservada:
No exige que las variables conserven sus valores; exige que se conserve la relación expresada por .
Es decir, para cualquier aserción que es a la vez , pre condición y post condición de un código se le denomina invariante.
flowchart LR S0["El estado inicial satisface I"] --> C["El bloque cambia el estado"] C --> S1["El estado final también satisface I"]
Ejemplo: preservar
Demostrar que es invariante de:
i := i + 1
r := r * 2Paso 1. Retroceder por :
Paso 2. Retroceder por :
Paso 3. Componer:
flowchart LR A["Antes: r = 2 elevado a i"] --> C1["i := i + 1"] C1 --> M["r = 2 elevado a i - 1 con el nuevo i"] M --> C2["r := 2r"] C2 --> B["Después: r = 2 elevado a i"]
Con valores históricos:
Comprobaciones:
- produce .
- produce .
flowchart TD E1["i = 2; r = 4"] --> B1["Incrementar i; duplicar r"] B1 --> F1["i = 3; r = 8"] E2["i = 6; r = 64"] --> B2["Incrementar i; duplicar r"] B2 --> F2["i = 7; r = 128"]
Relación con los ciclos
Si el bloque es el cuerpo de un ciclo y la propiedad también vale antes de la primera iteración, entonces funciona como invariante de ciclo.
8. Regla para if sin else
En if B then C1:
- Si es verdadera, se ejecuta .
- Si es falsa, el estado queda igual.
La regla condicional es:
La segunda premisa representa la rama skip.
flowchart TD P["Estado satisface P"] --> B{"¿B es verdadera?"} B -->|"Sí: P y B"| C1["Ejecutar C1"] B -->|"No: P y no B"| SKIP["No hacer nada"] C1 --> Q1["Probar Q"] SKIP --> Q2["Probar que Q ya se cumplía"] Q1 --> Q["Q"] Q2 --> Q
Ejemplo: actualizar un máximo
Demostrar:
, , .
Es decir:
Rama falsa. En un orden total:
Como no cambia el estado:
Rama verdadera.
Buscamos demostrar que:
Demostrando la precondición:
Con lo que obtenemos que por debilitamiento:
Asi que podemos fortalecer:
Lo cual es una tautología:
Como cualquier aseveración fortalece la vacía entonces: , por consecuencia:
Las dos ramas garantizan la misma postcondición:
flowchart TD I["Valores max y a"] --> B{"¿max es menor que a?"} B -->|"Sí"| ASG["max := a"] ASG --> EQ["max = a"] EQ --> Q["max mayor o igual que a"] B -->|"No"| GE["max ya era mayor o igual que a"] GE --> Q
9. Regla para if con else
Para if B then C1 else C2, ambas ramas ejecutan código:
flowchart TD P["Estado satisface P"] --> B{"Evaluar B"} B -->|"Verdadera"| C1["Ejecutar C1 con P y B"] B -->|"Falsa"| C2["Ejecutar C2 con P y no B"] C1 --> Q1["Probar Q"] C2 --> Q2["Probar Q"] Q1 --> Q["Postcondición común Q"] Q2 --> Q
acompaña a ambas ramas; lo adicional es o . Las dos deben establecer , o una condición más fuerte que implique .
Ejemplo complementario: máximo de dos valores
No aparece desarrollado en las diapositivas, pero aplica la regla:
if a >= b then max := a else max := bSe desea .
- Si , produce .
- Si , produce .
flowchart TD S["Valores a y b"] --> D{"¿a es mayor o igual que b?"} D -->|"Sí"| A["max := a"] D -->|"No"| B["max := b"] A --> Q["max domina a y b"] B --> Q
10. Método general de demostración
Para una secuencia
- Escribir la postcondición final .
- Tomar la última asignación y calcular .
- Usar el resultado como postcondición de la instrucción anterior.
- Repetir hasta llegar al principio.
- Verificar que la precondición declarada implica la calculada.
- Unir las ternas con la regla de composición.
flowchart RL Q["Postcondición final Q"] --> W3["wp de la última instrucción"] W3 --> R2["Condición intermedia R2"] R2 --> W2["wp de la instrucción anterior"] W2 --> R1["Condición intermedia R1"] R1 --> W1["wp de la primera instrucción"] W1 --> P0["Precondición calculada"] P["Precondición dada"] --> IMP["Comprobar P implica P0"] P0 --> IMP
Para un condicional
- Identificar , y .
- Demostrar .
- En la rama falsa, añadir .
- Sin
else, probar . - Con
else, demostrar . - Concluir con la regla condicional.
11. Precisiones importantes
Dominio
En , además de sustituir, debe exigirse . Toda operación parcial aporta obligaciones de dominio.
Aserción vacía
{ }representa , la aserción verdadera para todo estado; no una condición imposible.
Valores anteriores y posteriores
En , el lado derecho usa el valor anterior y el izquierdo recibe el nuevo. elimina la ambigüedad.
Invariante de bloque y de ciclo
prueba preservación por un bloque. Para un ciclo también debe demostrarse que vale antes de la primera iteración.
ifsinelseLa rama falsa no desaparece: ejecuta
skip, por lo que debe implicar .
12. Complemento de AllLectures: del programa anotado a la prueba
Una secuencia larga se vuelve más fácil cuando se inserta una aserción en cada punto importante. Por ejemplo, el intercambio queda anotado así:
{X=x₀ ∧ Y=y₀}
R := X;
{R=x₀ ∧ Y=y₀}
X := Y;
{R=x₀ ∧ X=y₀}
Y := R
{Y=x₀ ∧ X=y₀}Paso 1. Cada asignación se demuestra con sustitución.
Paso 2. La postcondición de un renglón coincide con la precondición del siguiente.
Paso 3. La regla de composición elimina las aserciones intermedias y deja la terna del programa completo.

En un condicional no hay una sola condición intermedia: hay dos obligaciones, una bajo y otra bajo . Ambas deben llegar a la misma .

Ejercicio corto
Demuestra .
Solución paso a paso
- Rama : sustituir por reduce la meta a .
- Rama : sustituir por reduce la meta a .
- Las dos fórmulas son verdaderas bajo la guarda de su rama.
- La regla condicional une ambas derivaciones.
Véase también la explicación guiada de condicionales.
13. Referencia diapositiva por diapositiva
| Diap. | Lámina | Contenido | Sección |
|---|---|---|---|
| 1 | 82 | Precondiciones de y | §2 a–b |
| 2 | 83 | Postcondición de ; precondición de | §2 c–d |
| 3 | 84 | Concatenación de código | §3 |
| 4 | 85 | Regla de concatenación y ejemplo de la media | §3–4 |
| 5 | 86 | Demostración de la media | §4 |
| 6 | 87 | Inicio del ejemplo | §5 |
| 7 | 88 | Fin del polinomio e inicio del intercambio | §5–6 |
| 8 | 89 | Demostración del intercambio | §6 |
| 9 | 90 | Invariante y ejemplo | §7 |
| 10 | 91 | Demostración formal del invariante | §7 |
| 11 | 92 | Casos e | §7 |
| 12 | 93 | Comprobación algebraica | §7 |
| 13 | 94 | if sin else | §8 |
| 14 | 95 | Regla y diagrama sin else | §8 |
| 15 | 96 | Ejemplo del máximo | §8 |
| 16 | 97 | Rama verdadera y consecuencia | §8 |
| 17 | 98 | Conclusión del ejemplo | §8 |
| 18 | 99 | if con else | §9 |
| 19 | 100 | Regla y diagrama con else | §9 |
Conceptos atómicos
- Terna de Hoare
- Axioma de asignación de Hoare
- Precondición más débil
- Regla de composición de Hoare
- Invariante de programa
- Invariante de ciclo
- Regla condicional de Hoare
- Regla de consecuencia de Hoare
Dudas para revisar
- ¿Cómo se extienden estas reglas al
whiley cómo se demuestra terminación? Véase Ciclos: invariante, salida y variante. - ¿Qué notación usará el curso para
skip? - ¿Los ejercicios asumen números reales, racionales o punto flotante?
Resumen después de clase
El axioma de asignación permite calcular precondiciones por sustitución y demostrar desde la salida hacia la entrada. La composición conecta instrucciones mediante aserciones intermedias, como en la media, el polinomio y el intercambio. Un invariante es una relación preservada aun cuando cambien los valores individuales. En un condicional se analizan por separado y , y ambos caminos deben garantizar la misma postcondición.
Referencia
- Programación Avanzada Notas 5, diapositivas 1–19 (láminas 82–100).
/Users/diegovillalba/Downloads/AllLectures.pdf, páginas PDF 23–33 (composición, condicionales, regla dewhilee invariantes).