Atlasingeniería

Estructuras de datosComplejidadTema 1

Cómo saber si un algoritmo va a escalar

Big O no mide segundos ni premia el código corto. Describe cómo crece el trabajo cuando crece la entrada, y permite descartar una solución lenta antes de ejecutarla.

Para este tema conviene tener claro:Condicionales y ciclos

Un algoritmo puede tardar un milisegundo con diez datos y volverse inutilizable con un millón. Medirlo una vez no alcanza. Lo que importa es cómo crece el trabajo cuando crece la entrada, y eso es lo que intenta capturar la notación Big O.

Qué se cuenta, ya que no son segundos

No contamos segundos porque dependen de la máquina, el lenguaje y lo que esté ejecutándose al mismo tiempo. Contamos operaciones relevantes en función del tamaño de entrada nn.

const contains = (values: number[], target: number): boolean => {
  for (const value of values) {
    if (value === target) return true;
  }
  return false;
};

En el peor caso, contains mira los nn elementos. Si duplicamos la cantidad de datos, duplicamos aproximadamente el trabajo. Decimos que su tiempo crece como O(n)O(n).

Por qué sobrevive sólo el término dominante

Supongamos que una cuenta exacta da

T(n)=3n2+20n+500.T(n)=3n^2+20n+500.

Para entradas grandes, n2n^2 domina a nn y a la constante. Big O conserva esa forma de crecimiento y descarta coeficientes:

T(n)O(n2).T(n)\in O(n^2).

No significa que las constantes nunca importen. Importan al optimizar un sistema real. Significa que una constante no puede salvar un crecimiento peor para siempre: por muy rápido que sea hoy un algoritmo cuadrático, uno lineal termina ganándole cuando nn crece.

La escalera de las familias comunes

Las familias más comunes forman una escalera:

OrdenCuando nn se duplicaEjemplo típico
O(1)O(1)casi igualacceso por índice
O(logn)O(\log n)suma un pasobúsqueda binaria
O(n)O(n)se duplicarecorrer una lista
O(nlogn)O(n\log n)un poco más del doblemergesort
O(n2)O(n^2)se cuadruplicacomparar todos contra todos
O(2n)O(2^n)se eleva al cuadradoexplorar subconjuntos

La diferencia se vuelve brutal rápido. Con n=1.000.000n=1.000.000, log2n\log_2 n ronda 20; n2n^2 es un billón. No son optimizaciones pequeñas: son problemas de otra escala.

Antes de seguir, predecí

Un algoritmo exponencial resuelve n = 20 en un segundo. ¿Cuánto tarda con n = 40?
OrdenOperacionesA mil millones por segundoDónde aparece
O(1)11.0 nsAcceder a un índice
O(log n)44.3 nsBúsqueda binaria
O(n)2020 nsRecorrer una lista
O(n log n)8686 nsMergesort
O(n²)400400 nsComparar todos contra todos
O(2ⁿ)1.048.5761.0 msProbar todos los subconjuntos
Movés n y mirás en qué momento cada curva se despega. La columna de tiempo es lo que tardaría una máquina que hace mil millones de operaciones por segundo.

Un ciclo no es automáticamente O(n)

Un ciclo no implica automáticamente O(n)O(n) y dos ciclos no implican automáticamente O(n2)O(n^2). Hay que mirar cuántas veces corre cada uno.

Dos ciclos consecutivos de nn pasos hacen n+n=2nn+n=2n, que sigue siendo O(n)O(n). Dos ciclos anidados de nn pasos hacen nn=n2n\cdot n=n^2. Pero si el índice se duplica en cada vuelta,

for (let step = 1; step < size; step *= 2) {
  visit(step);
}

el ciclo corre sólo log2n\log_2 n veces. La sintaxis se parece; la progresión del índice es la que decide el costo.

De dónde sale un logaritmo

La búsqueda binaria trabaja sobre datos ordenados. En cada comparación descarta la mitad de lo que queda. Después de kk pasos quedan n/2kn/2^k candidatos. Cuando queda uno,

n2k=1k=log2n.\frac{n}{2^k}=1 \quad\Rightarrow\quad k=\log_2 n.

Esa ecuación explica O(logn)O(\log n) mejor que memorizarlo. La base del logaritmo no aparece en Big O porque cambiar de base sólo multiplica por una constante.

Antes de anunciar una complejidad, decir qué es n

Antes de anunciar una complejidad hay que decir qué representa nn. En un grafo suelen importar dos tamaños, vértices VV y aristas EE; un recorrido cuesta O(V+E)O(V+E). En una matriz rectangular importan filas y columnas; recorrerla cuesta O(fc)O(f\cdot c).

Reducir todo a una sola letra puede esconder el dato que realmente crece. “Es lineal” no alcanza: lineal ¿en usuarios, mensajes, píxeles o conexiones entre nodos?

O, Omega y Theta: tres cosas distintas

Formalmente, O(g(n))O(g(n)) es una cota superior: para entradas suficientemente grandes, el costo no crece más rápido que una constante por g(n)g(n). Ω(g(n))\Omega(g(n)) es una cota inferior y Θ(g(n))\Theta(g(n)) indica que ambas coinciden.

En conversaciones de programación se usa “es O(n)O(n)” para decir “crece linealmente”, que formalmente sería Θ(n)\Theta(n). La distinción importa en demostraciones, pero en una entrevista o revisión de código suele importar más declarar el caso analizado y justificar el conteo.

Lo que la notación esconde

nO(log n)O(n)O(n log n)O(n²)O(2ⁿ)
10310331001.024
100710066410.00010³⁰
1.000101.0009.96610⁶no entra en el universo
1.000.0002010⁶2 × 10⁷10¹²
La última fila es la que fija la intuición: con un millón de elementos, lineal y n log n son prácticamente lo mismo, y cuadrático son diez mil segundos de cómputo. La frontera práctica está entre n log n y n².

Cierre

Big O no adivina rendimiento: elimina soluciones que no pueden escalar. Definí el tamaño de entrada, contá cómo avanza el algoritmo y quedate con el crecimiento dominante. Después medí constantes y hardware. Primero elegís una familia viable; recién entonces optimizás sus detalles.

Autoevaluación

¿Lo entendiste?

Una función hace dos ciclos consecutivos de n pasos cada uno. ¿Cuál es su complejidad?
Un algoritmo O(n²) tarda 1 segundo con n = 1.000. ¿Cuánto tarda, aproximadamente, con n = 10.000?
¿Por qué en Big O no aparece la base del logaritmo?
Un recorrido de un grafo cuesta O(V + E). ¿Qué error se comete al decir que «es lineal»?