Validar un algoritmo mediante razonamiento lógico significa analizar si sus pasos producen el resultado esperado para todos los casos relevantes.
Probar un programa ejecutándolo es importante, pero no siempre alcanza. También necesitamos razonar sobre lo que el algoritmo promete hacer.
La lógica permite revisar condiciones, casos, resultados esperados y propiedades que deben mantenerse durante la ejecución.
Validar un algoritmo consiste en comprobar que resuelve el problema correcto y que lo hace bajo las condiciones previstas.
No se trata solo de que funcione con un ejemplo, sino de entender por qué debería funcionar con todos los casos que pertenecen al problema.
Una precondición describe qué debe cumplirse antes de ejecutar un algoritmo.
Precondición: la lista no está vacía.
Si un algoritmo calcula el promedio de una lista, necesita que la lista tenga al menos un elemento para evitar una división por cero.
Una postcondición describe qué debe cumplirse después de ejecutar el algoritmo, si las precondiciones eran verdaderas.
Postcondición: el resultado es el promedio de todos los valores de la lista.
La postcondición expresa el objetivo lógico del algoritmo.
function promedio(valores) {
let suma = 0;
for (const valor of valores) {
suma += valor;
}
return suma / valores.length;
}
Para validar este algoritmo, debemos razonar que suma acumula todos los valores y que valores.length representa la cantidad de elementos.
Un algoritmo debe distinguir qué entradas acepta y qué entradas rechaza.
| Entrada | Situación | Acción esperada |
|---|---|---|
| [4, 6, 8] | Lista válida | Calcular promedio |
| [] | Lista vacía | Rechazar o informar error |
| null | No hay lista | Rechazar o informar error |
Un invariante es una propiedad que se mantiene verdadera durante una parte del algoritmo, especialmente dentro de un ciclo.
En el cálculo de una suma, después de cada iteración, la variable suma debe contener la suma de los elementos procesados hasta ese momento.
let suma = 0;
for (const valor of valores) {
suma += valor;
}
Invariante: al terminar cada vuelta, suma contiene la suma de todos los valores ya recorridos.
Si este invariante se mantiene hasta el final, entonces suma contiene la suma total de la lista.
El razonamiento por casos consiste en revisar cada situación posible por separado.
Por ejemplo, para validar una regla de acceso podemos analizar:
Para la condición usuarioActivo && tienePermiso, la tabla permite verificar todos los casos.
| Usuario activo | Tiene permiso | Puede acceder |
|---|---|---|
| false | false | false |
| false | true | false |
| true | false | false |
| true | true | true |
Los casos límite son valores que están justo en el borde de una condición.
if (edad >= 18) {
permitirIngreso();
}
Para validar esta regla, debemos revisar edades como 17, 18 y 19. El valor 18 es especialmente importante porque define el límite.
Además de producir el resultado correcto, un algoritmo debe terminar.
En un ciclo, debe existir alguna condición que avance hacia el final.
while (contador < 10) {
contador++;
}
La variable contador aumenta en cada vuelta, por lo que eventualmente dejará de cumplir la condición.
La corrección parcial significa: si el algoritmo termina, su resultado es correcto.
La corrección total agrega otra exigencia: además de ser correcto, el algoritmo debe terminar.
Ambas ideas son útiles para razonar con precisión sobre algoritmos.
El razonamiento lógico no reemplaza las pruebas, pero ayuda a elegir mejores casos de prueba.
Una buena estrategia combina:
La validación de algoritmos mediante razonamiento lógico permite ir más allá de probar ejemplos aislados. Al analizar condiciones, invariantes, casos y resultados esperados, podemos construir programas más confiables.
En el próximo tema estudiaremos aplicaciones de la lógica en bases de datos, inteligencia artificial y sistemas expertos.