Verificación de condicionales y ciclos por inducción
Pregunta central
¿Cómo pasamos de demostrar que cada rama de un condicional es correcta a demostrar que una propiedad se conserva durante cualquier número de iteraciones de un ciclo?
Mapa de la clase
flowchart LR H["Terna de Hoare"] --> IF["Separar B y no B"] IF --> R1["Probar rama verdadera"] IF --> R2["Probar rama falsa"] R1 --> Q["Postcondición común Q"] R2 --> Q Q --> W["Verificar ciclos while"] W --> IND["Inducción sobre el número de iteraciones"] IND --> INV["Invariante de ciclo"] INV --> EXIT["Invariante + condición de salida"] EXIT --> POST["Postcondición"] W --> VAR["Variante decreciente"] VAR --> TERM["Terminación"]
La primera parte completa la verificación de condicionales con else. La segunda introduce la inducción matemática como herramienta para demostrar que una relación entre variables sigue siendo cierta después de iteraciones. Esa relación persistente es el invariante del ciclo. (Diap. 1–17; láminas 101–117.)
1. Recordatorio: demostrar un if significa cubrir todos sus caminos
Para el programa:
if B then C1 else C2la regla condicional de Hoare es:
La estructura de la prueba es sencilla:
- En la rama verdadera sabemos .
- En la rama falsa sabemos .
- Cada rama puede ejecutar instrucciones distintas.
- Ambas deben terminar garantizando la misma postcondición .
Important
No basta con que el programa produzca el resultado esperado en algunos valores. La prueba debe cubrir todas las entradas admitidas por y los dos posibles resultados de evaluar .
2. Ejemplo gradual: elegir el mayor de dos valores
Queremos demostrar:
Aquí:
2.1. Antes de las fórmulas: intuición con números
- Si y , la condición es verdadera y se asigna .
- Si y , la condición es falsa y se asigna .
- Si , la condición es falsa y se asigna .
En los tres casos, no es menor que ninguno de los dos valores.
2.2. Rama verdadera:
P.D.
La asignación es . Calculamos hacia atrás desde , sustituyendo por :
La primera parte, , siempre es cierta. Además debilitando tenemos:
Por la regla de consecuencia:
2.3. Rama falsa:
En un orden total, negar equivale a afirmar . La asignación es . Sustituyendo por :
siempre es cierta y la condición de la rama ya proporciona . Por tanto:
2.4. Unir las ramas
Las dos ramas establecen , así que la regla condicional permite concluir:
flowchart TD S["Cualquier par a, b"] --> C{"¿a es mayor que b?"} C -->|"Sí"| A["m := a"] C -->|"No"| B["m := b"] A --> QA["m = a y a es mayor que b"] B --> QB["m = b y b es mayor o igual que a"] QA --> Q["m es mayor o igual que a y b"] QB --> Q
3. Ejemplo con una rama imposible
Ahora la precondición ya afirma que :
Notemos como en nuestro problema:
Rama verdadera
Buscamos demostrar que:
Mediante el esquema de demostración usado anteriormente tenemos:
Es decir, se cumple que:
Convenientemente, notemos como la precondición de la rama es:
Así para nuestra definición
La asignación garantiza directamente :
Rama falsa
Buscamos demostrar que
En esta ocasión se cumple que para la precondición
Lo que nos permite denotarlo como
Para lo que podemos garantizar que:
Para conseguir el argumento buscado, notemos que:
Es decir, debilitando la precondición anterior tenemos que se cumple:
Con lo cual conseguimos la forma buscada por nuestra definición.
Para darle sentido notemos como precondición de esta rama sería:
No existe un estado que satisfaga simultáneamente ambas partes. La rama es inalcanzable para cualquier entrada permitida. Por ello, la terna de esa rama es verdadera vacíamente:
Así, el condicional completo garantiza .
Una contradicción no significa que el programa falló
Significa que ese camino no puede ejecutarse desde la precondición dada. Esta idea simplifica muchas pruebas de programas.
4. Del condicional al ciclo
En un condicional hay un número finito de caminos y se prueba cada uno. En un ciclo, el cuerpo puede ejecutarse una cantidad no fijada de veces:
0 veces, 1 vez, 2 veces, ..., n vecesNo podemos escribir una prueba separada para cada . Necesitamos una sola demostración que cubra todos los casos. Ahí aparece la inducción matemática.
Microejemplo: un contador
x := 0
while x < 3
x := x + 1Si es el valor después de iteraciones:
| 0 | 0 |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
La tabla sugiere . Pero observar cuatro filas no demuestra la fórmula. La inducción permite probarla para todo que alcance el ciclo.
5. Inducción matemática aplicada a un programa
Para demostrar una proposición para todo , se prueban dos piezas:
- Caso base: es verdadera.
- Paso inductivo: para un arbitrario, suponemos y demostramos .
Entonces se concluye:
En un ciclo que comienza contando en , la correspondencia es:
| Inducción | Ejecución del ciclo |
|---|---|
| Estado antes de ejecutar el cuerpo por primera vez | |
| Hipótesis | Relación cierta después de iteraciones |
| Paso | Una ejecución del cuerpo conserva la relación |
| para todo | La relación es un invariante |
flowchart LR B["Estado inicial: P(0)"] --> I1["Ejecutar una vez"] I1 --> P1["P(1)"] P1 --> I2["Ejecutar una vez"] I2 --> P2["P(2)"] P2 --> D["..."] D --> PN["P(n)"] PN --> STEP["El cuerpo preserva P"] STEP --> PN1["P(n+1)"]
Important
La hipótesis de inducción no afirma que sea cierta “porque sí”. Se la supone temporalmente para un arbitrario y se usa para demostrar que el siguiente estado también satisface la propiedad.
6. Primer ciclo: calcular un cuadrado mediante sumas
La función de las diapositivas es:
Cuadrado(A):
C := 0
D := 0
while D != A
C := C + A
D := D + 1
return CPara corrección total supondremos y . es el acumulador y cuenta cuántas veces hemos sumado .
6.1. Construir la conjetura con un caso pequeño
Sea :
| Iteraciones | Comprobación | ||
|---|---|---|---|
| 0 | 0 | 0 | |
| 1 | 4 | 1 | |
| 2 | 8 | 2 | |
| 3 | 12 | 3 | |
| 4 | 16 | 4 |
El patrón es:
Al eliminar , obtenemos una relación directa entre las variables del programa:
Esta igualdad es el invariante.
Explorador de los dos ciclos
El explorador muestra el estado después de cada iteración. Los valores cambian, pero la igualdad resaltada se conserva. En el primer algoritmo, cuenta las sumas ya realizadas; en el segundo, cuenta los factores que aún faltan.
6.2. Demostración del invariante
Caso base, . Antes de entrar al ciclo:
Por tanto:
Hipótesis de inducción. Supongamos que después de iteraciones:
Paso inductivo. Una iteración adicional produce:
Usando la hipótesis:
Se demostró . Por inducción:
6.3. Usar la salida para obtener el resultado
El invariante por sí solo no dice que . Hay que combinarlo con la condición de salida.
Como parte de , aumenta de uno en uno y , al terminar se cumple . Entonces:
Por tanto, la función devuelve el cuadrado de .
6.4. ¿Por qué termina?
Mientras el ciclo continúa, . La cantidad:
es un entero no negativo y disminuye exactamente en en cada iteración. No puede disminuir indefinidamente, así que el ciclo llega a .
Dominio necesario
Si , comienza en y solo aumenta; nunca alcanzará . La prueba del resultado sería solo condicional a que terminara. Exigir permite demostrar también la terminación.
7. Segundo ciclo: descubrir que calcula una potencia
La subrutina recibe y no negativos y modifica :
Potencia(N, M):
R := 1
while M > 0
R := R * N
M := M - 1
return RPara distinguir el exponente original del valor mutable, llamaremos:
- : exponente recibido al entrar;
- : valor que queda después de iteraciones;
- : acumulador después de iteraciones.
7.1. Empezar con un ejemplo:
| Producto conservado | |||
|---|---|---|---|
| 0 | 1 | 5 | |
| 1 | 2 | 4 | |
| 2 | 4 | 3 | |
| 3 | 8 | 2 | |
| 4 | 16 | 1 | |
| 5 | 32 | 0 |
Cada iteración mueve un factor desde la parte “pendiente” hacia . Se observa:
Eliminamos :
Una forma más útil, que evita dividir potencias, es:
La igualdad dice: resultado acumulado × trabajo pendiente = resultado total esperado.
7.2. Caso base
Antes de la primera iteración:
Entonces:
7.3. Paso inductivo
Supongamos que después de iteraciones:
Si el cuerpo vuelve a ejecutarse, , y produce:
Comprobamos el invariante en el nuevo estado:
Por inducción, la relación se conserva durante todo el ciclo.
Prueba sin división
Las diapositivas despejan . La forma multiplicativa es más robusta porque no necesita dividir por y deja visible la idea de “calculado × pendiente”.
7.4. Salida y resultado
El ciclo termina cuando . Sustituimos en el invariante:
Como :
La subrutina calcula elevado al exponente original .
7.5. Terminación
La variante es el propio :
Si , durante el ciclo y cada iteración resta . Por tanto, tras exactamente iteraciones se alcanza .
8. Método para inventar un invariante
Los invariantes rara vez aparecen por adivinación pura. Un procedimiento útil es:
- Nombrar los estados. Usar para el valor de después de iteraciones.
- Trazar pocos casos. Calcular con números pequeños.
- Buscar una fórmula con . Por ejemplo, o .
- Eliminar . Combinar las fórmulas para obtener una relación entre variables del programa.
- Conservar los datos originales. Si una entrada se modifica, nombrar su valor inicial, como .
- Probar inicialización y mantenimiento. Los ejemplos sugieren; la inducción demuestra.
- Combinar con la salida. debe implicar la postcondición.
- Buscar una variante. Una cantidad entera no negativa que disminuya prueba terminación.
flowchart TD T["Trazar 0, 1, 2, 3 iteraciones"] --> F["Formular variables en función de n"] F --> E["Eliminar n"] E --> I["Invariante candidato I"] I --> B{"¿Vale al inicio?"} B -->|"No"| T B -->|"Sí"| M{"¿El cuerpo preserva I?"} M -->|"No"| F M -->|"Sí"| X["Combinar I con la condición de salida"] X --> Q["Postcondición"]
9. Corrección parcial frente a corrección total
Para un ciclo while do , una demostración completa separa dos obligaciones:
Corrección parcial
- El invariante vale antes del ciclo.
- Si vale antes del cuerpo, vuelve a valer después:
- Al salir, implica la postcondición .
Terminación
Se exhibe una variante que:
- toma valores en los enteros no negativos mientras el ciclo continúa;
- disminuye estrictamente en cada iteración.
En esta clase:
| Programa | Invariante principal | Condición al salir | Variante |
|---|---|---|---|
| Cuadrado por sumas | |||
| Potencia por productos |
10. Errores frecuentes
Probar solo ejemplos
Una tabla ayuda a descubrir el patrón, pero no sustituye el caso base ni el paso inductivo.
Confundir valor inicial y valor actual
En el algoritmo de potencia, cambia. La respuesta debe expresarse con el exponente original , no con el final, que vale .
Olvidar el estado de cero iteraciones
El invariante debe ser cierto antes de ejecutar el cuerpo. Por eso el caso base suele ser .
Creer que el invariante ya es la postcondición
solo se convierte en cuando también sabemos que el ciclo salió con .
Demostrar el resultado pero no la terminación
Sin una variante o argumento equivalente solo se obtiene corrección parcial: “si termina, el resultado es correcto”.
11. Plantilla reutilizable
Para verificar un ciclo:
1. Especificar la precondición P y la postcondición Q.
2. Nombrar el valor original de cualquier entrada que vaya a cambiar.
3. Trazar unas pocas iteraciones y proponer el invariante I.
4. Inicialización: demostrar P => I.
5. Mantenimiento: demostrar {I y B} cuerpo {I}.
6. Salida: demostrar I y no B => Q.
7. Terminación: encontrar una variante entera no negativa que disminuya.Esta plantilla conecta la inducción con la lógica de Hoare: el caso base prueba la inicialización y el paso inductivo prueba el mantenimiento.
12. Ejercicios de comprobación
- En el ciclo del cuadrado, usa y escribe los estados desde hasta la salida. Verifica en cada fila.
- En el ciclo de potencia, usa y . ¿Qué valor tiene después de cada iteración?
- Explica por qué no es un invariante del segundo ciclo.
- ¿Qué falla en la terminación de Cuadrado(A) si ?
- En la terna con precondición , ¿por qué no necesitamos que la asignación produzca realmente ?
Respuestas breves
- ; en todos, .
- Siempre vale : .
- Tanto como cambian en sentidos opuestos; por ejemplo, al inicio y . La relación conservada es .
- empieza en y aumenta; nunca alcanza .
- La precondición de la rama falsa es , una contradicción; esa rama es inalcanzable.
13. Complemento de AllLectures: las obligaciones de un ciclo
La notación compacta de la regla de while se traduce en preguntas concretas. Para
{P} while B do {I} C {Q}hay que demostrar:
- Entrada: .
- Preservación: .
- Salida útil: .

Para corrección total se agregan dos obligaciones sobre una variante entera :
- .
- Una vuelta del cuerpo reduce estrictamente.

Ejemplo paso a paso: sumar de 1 a
i := 0; s := 0;
while i < n do
i := i + 1;
s := s + iProponemos
[ I:\ s=\frac{i(i+1)}2\land0\le i\le n, \qquad E=n-i. ]
- Con , el invariante es cierto.
- Si vale antes de la vuelta, después y [ s’=s+i’=\frac{i(i+1)}2+(i+1)=\frac{(i+1)(i+2)}2. ]
- Al salir, ; combinado con , resulta .
- Entonces .
- es no negativa bajo la guarda y disminuye en uno.
Con esto quedan demostradas corrección parcial y terminación.
14. Referencia diapositiva por diapositiva
| Diap. | Lámina | Contenido | Sección |
|---|---|---|---|
| 1–4 | 101–104 | Verificación del máximo de dos valores | §2 |
| 5–7 | 105–107 | Condicional con precondición | §3 |
| 8 | 108 | Principio de inducción matemática | §5 |
| 9–12 | 109–112 | Cuadrado mediante sumas e invariante | §6 |
| 13 | 113 | Definición de invariante e inicio de potencia | §7 |
| 14–17 | 114–117 | Invariante y prueba del algoritmo de potencia | §7 |
Conceptos atómicos
- Regla condicional de Hoare
- Inducción matemática aplicada a ciclos
- Invariante de ciclo
- Corrección parcial y corrección total
Dudas para revisar
- ¿Cuál es la regla formal de Hoare para while y cómo se deriva de estas tres obligaciones? Véanse §13 y Condición de verificación.
- ¿Qué convención adopta el curso para cuando el exponente inicial es cero?
- ¿Cómo cambia el invariante de potencia rápida cuando el exponente se divide entre dos?
Resumen después de clase
Un condicional se verifica demostrando una postcondición común en las ramas y ; una rama contradictoria puede ser inalcanzable. Para un ciclo se usa inducción sobre el número de iteraciones: el caso base establece el invariante antes de comenzar y el paso inductivo demuestra que una ejecución del cuerpo lo conserva. Al terminar, el invariante se combina con la negación de la guarda para obtener la postcondición. Una variante decreciente añade la prueba de terminación y convierte la corrección parcial en corrección total.
Referencia
- Programación Avanzada Notas 6, diapositivas 1–17 (láminas 101–117).
/Users/diegovillalba/Downloads/AllLectures.pdf, páginas PDF 28–35 y 47–91 (regla dewhile, invención de invariantes, condiciones de verificación y corrección total).