Las métricas de un algoritmo son criterios para comparar soluciones más allá de cuánto tardan.
En el artículo anterior nos centramos en la notación Big O, que sirve para medir cómo crece el tiempo de ejecución cuando aumenta el tamaño de la entrada. Es una herramienta muy útil, pero no cuenta toda la historia.
Un algoritmo puede ser rápido y aun así ser una mala elección. Puede consumir demasiada memoria, perder precisión, ser difícil de mantener o romperse con datos un poco raros. Así que hoy vamos a mirar el cuadro de mandos completo.
Resumen
Cuando elegimos un algoritmo, no miramos solo el velocímetro. También hay que mirar consumo, estabilidad y mantenimiento.
| Métrica | Pregunta clave | Cuándo importa mucho |
|---|---|---|
| Tiempo | ¿Cómo escala la velocidad? | Grandes volúmenes de datos |
| Espacio | ¿Cuánta memoria extra usa? | Sistemas con poca RAM o muchos datos |
| Estabilidad | ¿Respeta el orden previo? | Ordenaciones multicriterio |
| Precisión | ¿El resultado es exacto o aproximado? | Cálculo científico, finanzas, simulación |
| Robustez | ¿Qué pasa con entradas raras? | Software en producción |
| Legibilidad | ¿Se puede mantener sin sufrir? | Casi siempre |
Complejidad espacial
Si la complejidad temporal mide “cuánto tarda”, la complejidad espacial mide cuánta memoria necesita.
A menudo existe un intercambio clásico: podemos hacer algo más rápido si gastamos más memoria, o podemos ahorrar memoria si aceptamos repetir cálculos.
Por ejemplo, una caché guarda resultados previos para no recalcularlos. Eso puede acelerar muchísimo un programa, pero a cambio consume RAM.
Conviene distinguir dos cosas:
- Espacio de entrada: memoria ocupada por los datos que ya recibimos.
- Espacio auxiliar: memoria extra que necesita el algoritmo para trabajar.
Normalmente, cuando analizamos memoria, nos interesa especialmente el espacio auxiliar.
Supongamos que queremos invertir un array:
[1, 2, 3, 4] -> [4, 3, 2, 1]
Una opción es crear un array nuevo y copiar los elementos en orden inverso. Es sencilla, pero usa memoria adicional O(n).
Otra opción es intercambiar el primer elemento con el último, el segundo con el penúltimo, y así sucesivamente. Eso modifica el array original y usa memoria O(1). A esto se le llama trabajar in-place.
En sistemas embebidos, aplicaciones móviles o procesos con muchísimos datos, quedarse sin memoria suele ser peor que tardar un poco más.
Estabilidad
La estabilidad es importante sobre todo en algoritmos de ordenación.
Un algoritmo de ordenación es estable si, cuando dos elementos tienen la misma clave, mantiene el orden relativo que tenían antes.
Parece un detalle menor, hasta que haces ordenaciones encadenadas. Por ejemplo, una lista de empleados ya ordenada por nombre:
| Nombre | Departamento |
|---|---|
| Ana | Ventas |
| Carlos | Marketing |
| Daniel | Ventas |
Si ahora ordenamos por departamento con un algoritmo estable, Ana seguirá antes que Daniel dentro de Ventas. Si el algoritmo no es estable, ese orden puede perderse.
Algoritmos como Merge Sort son estables. QuickSort, en sus implementaciones clásicas, normalmente no lo es. En la práctica, las librerías modernas pueden usar variantes híbridas y optimizadas.
Exactitud y precisión numérica
Otro criterio es si el algoritmo devuelve la solución exacta o una aproximación.
Hay problemas donde buscar la solución perfecta es demasiado caro. En esos casos se usan heurísticas o aproximaciones que dan una respuesta suficientemente buena en un tiempo razonable.
Esto es habitual en problemas de rutas, optimización, inteligencia artificial o planificación. No siempre queremos “la respuesta perfecta”. Muchas veces queremos una respuesta buena antes de que se enfríe el café.
Estabilidad numérica
En cálculo numérico también importa cómo se comportan los errores de redondeo.
Los ordenadores representan muchos números reales con coma flotante (float, double). Eso introduce pequeños errores:
console.log(0.1 + 0.2); // 0.30000000000000004
Un algoritmo es numéricamente inestable si esos errores pequeños se amplifican paso a paso hasta estropear el resultado final.
Robustez
La robustez mide cómo se comporta un algoritmo ante entradas incorrectas, inesperadas o maliciosas.
Un algoritmo frágil asume que todo será perfecto:
- la lista nunca está vacía,
- el usuario siempre escribe un número,
- el archivo siempre existe,
- nunca llega un
null, - jamás se divide entre cero.
La realidad, por supuesto, tiene otras aficiones.
Un algoritmo robusto valida entradas, contempla casos límite y falla de forma controlada cuando no puede continuar.
Legibilidad y mantenibilidad
Esta métrica parece menos técnica, pero en proyectos reales pesa muchísimo.
A veces podemos hacer un algoritmo un 5% más rápido usando trucos difíciles de leer. La pregunta es si compensa. Si ese código lo vais a mantener durante años, quizá el coste humano sea mayor que el ahorro de CPU.
Cualquier persona puede escribir código que un ordenador entienda. El reto es escribir código que también entiendan las personas.
No significa que el rendimiento no importe. Significa que la claridad también es parte del diseño.
