Notación Big O sin dolor: cómo medir tu código

Vivid, blurred close-up of colorful code on a screen, representing web development and programming.
Foto de Markus Spiske en Pexels

Cuando empezamos a programar nos topamos con la temida notación Big O. Mucha teoría, pocos ejemplos y, a veces, la sensación de que es un tema reservado a científicos de datos. En este artículo quiero desmitificarla y, sobre todo, enseñarte cómo medir la complejidad de tu código en la práctica, usando herramientas que ya tienes a mano.

¿Qué es la notación Big O?

En términos simples, la notación Big O describe cómo crece el tiempo de ejecución o el uso de memoria de un algoritmo cuando aumentamos el tamaño de la entrada (n). No se trata de medir milisegundos exactos, sino de entender la tendencia: ¿mi función será lineal (O(n)), cuadrática (O(n²)) o quizá logarítmica (O(log n))?

Métricas prácticas en JavaScript

En el navegador o en Node.js disponemos de performance.now() y console.time para medir tiempos reales. Lo interesante es combinarlos con distintas tamaños de entrada y observar la curva.

function medirTiempo(fn, ...args) {
  const inicio = performance.now();
  const resultado = fn(...args);
  const fin = performance.now();
  console.log(Tiempo: ${ (fin - inicio).toFixed(3) } ms);
  return resultado;
}

// Ejemplo: suma de los primeros n números
function sumaIterativa(n) {
  let total = 0;
  for (let i = 1; i <= n; i++) {
    total += i;
  }
  return total;
}

for (let tamaño = 1e3; tamaño <= 1e6; tamaño *= 10) {
  medirTiempo(sumaIterativa, tamaño);
}

Ejecutando el bucle anterior vemos que el tiempo crece de forma aproximadamente lineal con tamaño. Si cambiamos la implementación por una recursiva sin memoización, la curva se vuelve exponencial y el script se bloquea rápido.

Identificando la complejidad a ojo

  • Bucle simple que recorre un array: O(n).
  • Bucle anidado que recorre dos veces el mismo array: O(n²).
  • Búsqueda binaria en un array ordenado: O(log n).
  • Operaciones sobre objetos (inserción, borrado, acceso) en la mayoría de los mapas/hashes modernos: O(1) amortizado.

Una buena práctica es escribir comentarios al lado del bucle indicando la complejidad esperada. Así, cuando vuelvas a leer el código, tendrás una pista rápida.

Herramientas y trucos para afinar la medición

Además de performance.now(), Chrome DevTools ofrece el panel Performance, que captura flame charts y permite ver cuántas veces se llama a cada función. En Node.js, el módulo benchmark o paquetes como benchmark.js facilitan comparaciones precisas.

Otro truco útil es normalizar los resultados. Ejecuta la misma función 1000 veces y calcula la media; de esa forma eliminas variaciones debidas a la GC o a procesos en segundo plano.

Cuando la teoría y la práctica chocan

No todos los algoritmos que son O(n log n) en papel serán más rápidos que un O(n²) con constantes pequeñas. Por ejemplo, el algoritmo de ordenación insertion sort (O(n²)) suele ser más veloz que quick sort (O(n log n)) para arrays de menos de 20 elementos. Por eso, siempre combina la estimación teórica con pruebas reales.

Conclusión

Entender la notación Big O no tiene por qué ser un dolor de cabeza. Con unos pocos minutos de medición y un ojo entrenado para identificar bucles y recursiones, puedes obtener una visión clara del coste de tu código. Recuerda: la complejidad es una guía, no una sentencia definitiva. Usa las herramientas que ya tienes, prueba con diferentes tamaños de datos y, sobre todo, documenta tus hallazgos. Así, tu código será más predecible, mantenible y, lo mejor de todo, más rápido.