La PizarraMatemática.

Grado en Matemáticas

Algoritmos y análisis de complejidad

Un algoritmo puede acertar y aun así ser demasiado lento, o ser rápido y dar una respuesta incorrecta. Primero separaremos esas dos obligaciones. Después elegiremos qué tamaño tiene la entrada y qué operación cuesta una unidad; solo entonces tendrá sentido comparar crecimiento lineal, cuadrático o logarítmico.

Si necesitas repasar:

1. Antes de medir tiempo hay que demostrar qué hace el algoritmo

El problema define entradas permitidas y respuestas válidas. El algoritmo especifica los pasos. La corrección parcial dice que, si termina, entrega una respuesta válida. Para corrección total se demuestra además que termina en toda entrada permitida. Acertar en varias pruebas sirve para detectar fallos, pero no sustituye esa garantía.

Un invariante es una afirmación que vale al inicio y se conserva al repetir un bucle. Junto a la condición de salida demuestra la respuesta. Para terminar se usa, por ejemplo, una medida entera no negativa que disminuye estrictamente en cada repetición. Hay que comprobar inicio, conservación, salida y terminación, no solo nombrarlos.

Encontrar el máximo sin favorecer el cero

Entrada no vacía [−4,−1,−7]. Se inicia M en el primer dato y se recorren los restantes.

  1. Invariante

    Tras procesar i datos, M es el máximo de los primeros i.

  2. Conservar

    El siguiente paso reemplaza M por el mayor entre M y el dato nuevo; la afirmación sigue siendo cierta.

  3. Salida

    Se pasa de −4 a −1 y se conserva −1 al leer −7; al terminar i=n, M es el máximo de toda la lista.

  4. Terminar

    n−i baja uno por cada dato pendiente y nunca es negativo.

Resultado

El resultado es −1 y el argumento vale para cualquier lista no vacía.

Lo comprobamos

Inicializar M=0 devolvería cero en este ejemplo, aunque cero no aparezca: un contraejemplo basta para refutar esa versión.

El invariante de una suma empieza en la lista vacía

Se inicia s=0 y se añade cada elemento de [3,−2,5].

  1. Inicio

    Antes de leer nada, s es la suma de cero datos: cero.

  2. Conservación

    Añadir el siguiente valor convierte la suma del prefijo anterior en la del nuevo prefijo.

  3. Salida y límite

    Los acumulados son 3,1,6; tras tres pasos no quedan datos.

Resultado

La suma es seis; para una lista de longitud n se usan n incorporaciones al acumulador.

Lo comprobamos

La misma inicialización también permite la entrada vacía, que devuelve cero.

2. El tamaño de un entero no es su valor

Para una lista, n suele ser el número de elementos. Para un entero positivo N escrito en binario, su longitud es b=⌊log₂N⌋+1 bits. Un algoritmo que hace N pasos puede ser exponencial respecto a b, aunque parezca lineal al usar N como variable.

En un modelo unitario se cuentan comparaciones, accesos o sumas como una operación cuando los operandos caben en palabras apropiadas. Si los números crecen sin cota, ese modelo no describe el coste de bits. La suma escolar de b bits necesita recorrer como máximo b posiciones y un acarreo; la multiplicación escolar realiza un orden cuadrático de operaciones de bits.

No se concluye que todos los algoritmos de multiplicación tengan ese coste.

Contar hasta el valor puede ser muy caro para su descripción

N=1024 en binario es un uno seguido de diez ceros.

  1. Longitud

    Su representación tiene b=11 bits.
  2. Recorrido

    Visitar todos los enteros de 1 a N hace 1024 pasos, no once.

  3. Escala general

    Entre enteros de b bits, N está entre 2ᵇ⁻¹ y 2ᵇ−1; ese recorrido tiene coste Θ(2ᵇ).

Resultado

Es lineal en el valor N y exponencial en la longitud binaria b.

Lo comprobamos

Hay que declarar la variable de tamaño antes de decir “lineal”.

Una operación aritmética puede ocultar muchos pasos

Se suman dos enteros de un millón de bits con el método escolar.

  1. Modelo unitario idealizado

    Contar una suma como una operación oculta la longitud de los operandos.

  2. Modelo de bits

    Se procesan las columnas y los acarreos: O(b) trabajo para b bits.

  3. Almacenamiento

    El resultado puede necesitar b+1 bits, no una palabra fija independiente de b.

Resultado

Para números grandes, el coste debe incluir la longitud que se manipula.

Lo comprobamos

El modelo unitario es útil con tamaños de palabra justificados; no es una garantía física de suma constante para cualquier entero.

3. Tiempo, memoria auxiliar y memoria acumulada no son la misma medida

T(n) cuenta trabajo; S(n) cuenta memoria máxima ocupada al mismo tiempo. Se declara si incluye entrada y salida o solo espacio auxiliar. Guardar resultados intermedios puede reducir trabajo repetido y aumentar memoria. Sumar todo lo reservado y liberado durante una ejecución no mide necesariamente el máximo simultáneo.

La recursión mantiene llamadas pendientes en una pila. Su profundidad y lo guardado en cada llamada cuentan aunque el código no declare un arreglo. Si se copian sublistas, esas copias también importan; pasar intervalos de una lista evita confundir una descripción de subproblema con una copia completa.

Leer todos los datos puede necesitar poca memoria extra

Se busca el máximo de una lista almacenada de n números que caben en una palabra.

  1. Tiempo

    Hay n−1 comparaciones con el máximo actual.

  2. Auxiliar

    Basta un índice y un máximo: O(1) palabras auxiliares.

  3. Entrada

    La lista ya almacenada ocupa Θ(n) palabras, si se incluye en la medida.

Resultado

El tiempo es Θ(n) y el espacio auxiliar O(1).

Lo comprobamos

No se dice espacio total constante ignorando la entrada que permanece almacenada.

La profundidad no es el número total de llamadas

Un recorrido recursivo de un árbol binario completo guarda una cantidad constante de información por llamada y explora una rama cada vez.

  1. Profundidad

    Con altura h hay como máximo h+1 llamadas pendientes.

  2. Total

    Puede visitar todos los n vértices y hacer Θ(n) trabajo.

  3. Memoria

    En el árbol equilibrado h=Θ(log n), la pila ocupa O(log n) espacio auxiliar, sin contar el árbol.

Resultado

La suma de visitas puede ser lineal y la pila máxima logarítmica.

Lo comprobamos

En una cadena de n vértices la profundidad sería Θ(n); el equilibrio es una hipótesis relevante.

4. O acota por arriba; Θ declara un orden ajustado

Con costes no negativos y g positiva desde cierto umbral, T=O(g) significa que existen C>0 y n₀ con T(n)≤Cg(n) para todo n≥n₀. Ω usa una cota inferior cg(n), c>0. Θ exige ambas. Las constantes se eligen una vez, no dependen de n.

Una función puede tener varias cotas superiores: T(n)=n pertenece a O(n), O(n²) y O(2ⁿ), pero estas últimas son menos precisas. Escribir O no afirma que la cota sea óptima ni una igualdad exacta de operaciones. Los términos de menor orden no cambian Θ si se justifican las dos cotas.

Dar constantes concretas demuestra una Θ

T(n)=2n+10 para n≥1.

  1. Inferior

    T(n)≥2n.
  2. Superior desde un umbral

    Para n≥10, 10≤n, así T(n)≤3n.
  3. Definición

    c=2, C=3, n₀=10 prueban T=Θ(n).
Resultado

El coste crece linealmente aunque no sea exactamente n.

Lo comprobamos

Que también sea O(n²) no vuelve cuadrático su orden ajustado.

Acotar un polinomio completo

T(n)=n²+3n+2.

  1. Por debajo

    T(n)≥n² para n≥1.
  2. Por arriba

    3n≤3n² y 2≤2n², de modo que T(n)≤6n².
  3. Conclusión

    c=1, C=6, n₀=1 prueban Θ(n²).
Resultado

El término cuadrático da el orden ajustado.

Lo comprobamos

La cota superior seis es suficiente para la prueba; no necesita ser la menor constante posible.

5. Peor caso, promedio y amortizado requieren preguntas diferentes

El peor caso maximiza el coste entre entradas de igual tamaño y el mejor lo minimiza. El promedio necesita una distribución explícita de entradas. No se obtiene promediando sin más las dos cifras extremas: las probabilidades de cada caso deciden el resultado.

El análisis amortizado reparte el coste total de una secuencia de operaciones. No necesita azar: puede garantizar un coste medio por operación para cualquier secuencia válida. Una operación particular puede ser cara y el total seguir siendo pequeño porque esas operaciones caras no pueden ocurrir continuamente.

Búsqueda secuencial con una distribución declarada

Buscamos un elemento que aparece una vez en una lista de n posiciones, todas equiprobables.

  1. Casos extremos

    Si está primero se usa una comparación; si está último, n.

  2. Promedio justificado

    La esperanza es (1+2+⋯+n)/n=(n+1)/2 por la distribución uniforme.
  3. Otra distribución

    Si falta con probabilidad 1/2 y, cuando está, su posición es uniforme, la esperanza es n/2+(n+1)/4=(3n+1)/4.

Resultado

El promedio cambia al cambiar la distribución, incluso con los mismos extremos de coste.

Lo comprobamos

La coincidencia del primer promedio con la media de extremos ocurre por esta simetría particular; no es la definición general.

Amortizar copias al duplicar un arreglo

Capacidad inicial uno; al llenarse se duplica. Contamos una unidad por dato copiado y otra por dato añadido.

  1. Cinco inserciones

    Las copias al crecer son 1+2+4=7; las escrituras nuevas son cinco: total doce.

  2. Secuencia general

    Las capacidades anteriores forman una geométrica 1+2+4+⋯ menor que 2N tras N inserciones.

  3. Total

    Copias más N escrituras son menos que 3N; el coste amortizado es O(1) por inserción.

Resultado

Una inserción puede copiar Θ(N) datos, pero cualquier secuencia de N tiene coste total O(N) en este modelo.

Lo comprobamos

Se declaró qué se cuenta; incluir un coste proporcional a capacidad reservada mantiene O(N), aunque cambiaría la constante concreta.

6. Los límites de cada bucle determinan la suma

Un bucle con n repeticiones de trabajo constante cuesta Θ(n). Dos bucles consecutivos suman; uno dentro de otro exige sumar cuántos pasos internos se hacen para cada valor exterior. Si el interior tiene siempre n pasos, la suma es n·n; si tiene i, la suma es 1+⋯+n.

Los bucles triangulares se cuentan sumando longitudes de filas
El límite interior cambia con iiOperaciones12341+2+3+4=10No hay cuatro puntos en cada fila.
Para i de uno a cuatro, el bucle interior tiene respectivamente uno, dos, tres y cuatro pasos. Las filas contienen diez puntos en total. No es un cuadrado de dieciséis; el total general es uno más dos hasta n, igual a n por n más uno dividido entre dos.

La sumatoria escribe límites e índice para que no se pierdan filas. Para 1+⋯+n, escribir la suma en ambos sentidos da n pares de valor n+1; dividir por dos produce n(n+1)/2. Si el cuerpo cuesta i en vez de una unidad, ese trabajo también entra dentro de la suma.

El triángulo de cuatro filas no es un cuadrado

i recorre 1,…,4 y, para cada i, j recorre 1,…, i.

  1. Filas

    Las longitudes son 1,2,3,4.

  2. Exacto

    Hay 1+2+3+4=10 ejecuciones del cuerpo.
  3. General

    Para n filas son n(n+1)/2; entre n²/2 y n² para n≥1, así Θ(n²).

Resultado

La cuenta exacta es diez para cuatro filas, no dieciséis.

Lo comprobamos

Mismo orden asintótico no significa mismo número exacto de operaciones.

Consecutivos y anidados no se multiplican igual

Comparamos dos bucles separados de n pasos con un bucle n×n.

  1. Separados

    El primero termina y luego comienza el segundo: n+n=2n trabajos.

  2. Anidados

    Por cada una de n vueltas exteriores se hacen n interiores: n².

  3. Ejemplo

    Para n=3 los totales son seis y nueve, respectivamente.

Resultado

Los separados son Θ(n) y los anidados completos Θ(n²).

Lo comprobamos

Si el cuerpo de cada vuelta fuese más costoso que una unidad, habría que sumarlo además.

7. Las mitades se cuentan con logaritmos, bajo hipótesis de acceso y orden

Con n=2ᵏ, reducir a la mitad k veces deja uno: n/2ᵏ=1. Para la regla abstracta s←⌈s/2⌉ hasta s=1, el máximo de reducciones es ⌈log₂n⌉. Es una cuenta de cambios de tamaño, no una cifra universal de comparaciones para todo código de búsqueda binaria.

Cada reducción a la mitad baja una potencia de dos
32 candidatos hasta quedar uno3216842132=2⁵ : cinco reduccionesSe cuentan cambios, no filas.
Los tamaños treinta y dos, dieciséis, ocho, cuatro, dos y uno aparecen en seis filas, unidas por cinco reducciones a la mitad. La longitud de la cadena de cambios es el logaritmo en base dos del tamaño inicial; no se confunde con un conteo exacto de comparaciones de código.

La búsqueda binaria usa una lista ordenada y acceso al elemento central con el coste supuesto. Tras comparar, descarta una parte conservando todos los candidatos que podrían contener el objetivo. En una lista enlazada, llegar al centro puede costar más; si hay que ordenar antes, ese trabajo se suma.

Logaritmos de bases fijas mayores que uno difieren por un factor constante.

Seguir los cambios de tamaño

Partimos de 32 candidatos y hacemos mitades exactas hasta uno.

  1. Secuencia

    32,16,8,4,2,1.

  2. Cambios

    Son seis tamaños, pero cinco reducciones.

  3. Logaritmo

    32=2⁵, así log₂32=5.
Resultado

Hacen falta cinco reducciones.

Lo comprobamos

Para un único candidato ya hay cero reducciones, aunque el programa pueda necesitar compararlo con el objetivo.

Redondear hacia arriba conserva el caso más grande

Regla s←⌈s/2⌉ para s inicial 35.

  1. Seguir

    35→18→9→5→3→2→1.
  2. Contar

    Hay seis cambios.

  3. Potencias

    2⁵=32<35≤64=2⁶, así ⌈log₂35⌉=6.
Resultado

El máximo abstracto es seis reducciones.

Lo comprobamos

Esta regla de tamaños se declaró explícitamente; una implementación puede excluir el centro y tener otra cuenta exacta de comparaciones.

8. Un árbol de llamadas suma trabajo por niveles

En T(n)=2T(n/2)+n, con T(1)=1 y n potencia de dos, cada llamada crea dos mitades. El primer nivel trabaja n; el siguiente, 2·n/2=n; el siguiente vuelve a sumar n. Hay log₂n niveles internos y n hojas de coste uno. El total exacto es n log₂n+n.

El árbol de llamadas conserva trabajo total ocho en cada nivel interno
Dos mitades; sumar el trabajo del nivel8442222111111118 ; 4+4 ; 2+2+2+2 : siempre 8T(8)=8·3+8=32 si T(1)=1
La recurrencia T de n igual a dos T de n medios más n, con T de uno igual a uno, se despliega para n ocho. Los tres niveles internos tienen tamaños totales ocho, ocho y ocho; las ocho hojas cuestan uno cada una. El total es treinta y dos, igual a ocho por tres más ocho.

Si el trabajo de combinación está entre constantes positivas por n, el argumento da Θ(n log n). Con tamaños generales se fijan suelos, techos y caso base; los niveles cambian en sus bordes pero mantienen el orden para la división equilibrada usual. Una sola llamada por nivel no tiene el mismo árbol y puede costar mucho menos.

Sumar el árbol del dibujo

T(n)=2T(n/2)+n, T(1)=1; n=8.

  1. Niveles internos

    8;4+4;2+2+2+2: cada uno cuesta ocho.

  2. Hojas

    Ocho llamadas de tamaño uno cuestan ocho.

  3. Total

    8·3+8=32; por la regla también T(2)=4 y T(4)=12, luego T(8)=32.
Resultado

T(8)=32 y el orden general es Θ(n log n).

Lo comprobamos

La profundidad es tres, pero hay muchas llamadas por nivel: no se cuenta solo una rama.

Una sola mitad cambia la recurrencia

T(n)=T(n/2)+1 con T(1)=1 y n=2ᵏ.

  1. Desplegar

    Cada paso añade uno y conserva una sola llamada.

  2. Profundidad

    Tras k pasos se llega a T(1).

  3. Total

    T(2ᵏ)=k+1; para n=8 da cuatro.
Resultado

El orden es Θ(log n), con el coste base incluido.

Lo comprobamos

El uno adicional explica por qué tiempo total y número de reducciones pueden diferir en una constante.

9. Una cota de un programa no demuestra un límite de todo el problema

Las constantes pueden decidir qué algoritmo conviene en entradas pequeñas. Medir tiempos complementa el análisis, pero depende del equipo, implementación y entradas. “Polinómico” respecto a longitud significa O(nᵏ) para algún exponente fijo; ni una constante enorme ni un exponente enorme garantizan viabilidad práctica.

Mostrar que un algoritmo tarda Θ(n²) no prueba que todos los que resuelven el problema deban hacerlo. Para una cota inferior del problema se considera cualquier algoritmo dentro de un modelo declarado. Un argumento de información puede demostrar límites del modelo de comparaciones, sin trasladarlos automáticamente a otros modelos.

Las constantes pueden invertir al ganador

Comparamos costes A(n)=1000n y B(n)=n².

  1. n=100

    A cuesta 100000 y B 10000: B es menor.

  2. n=2000

    A cuesta 2000000 y B 4000000: A es menor.

  3. Cruce

    1000n=n² para n positivo da n=1000.
Resultado

El algoritmo lineal gana finalmente, pero no en todo tamaño.

Lo comprobamos

Las fórmulas son costes declarados del ejemplo, no tiempos reales universales de los algoritmos lineales y cuadráticos.

Un método lento no limita a todos los métodos

Se ordenan n datos mediante un método cuadrático; existe también mezcla con Θ(n log n) en el modelo de comparaciones adecuado.

  1. Primera cota

    Θ(n²) describe el primer algoritmo.

  2. Contraejemplo a una imposibilidad

    El segundo resuelve el mismo problema con crecimiento menor.

  3. Alcance

    La lentitud del primero no demuestra una cota inferior cuadrática del problema.

Resultado

Para hablar de imposibilidad hay que razonar sobre todos los algoritmos permitidos, no uno.

Lo comprobamos

El modelo debe conservarse en la comparación: coste de comparaciones y coste de bits no se intercambian.

Una cota inferior por información sí considera cualquier árbol de comparación

Ordenar n elementos distintos usando comparaciones de dos resultados.

  1. Resultados posibles

    Hay n! órdenes que el algoritmo debe distinguir.

  2. Árbol de decisiones

    Con altura h puede tener como máximo 2ʰ hojas; se requiere 2ʰ≥n!.

  3. Despejar

    h≥log₂(n!).

    Los últimos aproximadamente n/2 factores de n! son al menos n/2, así log₂(n!)=Ω(n log n).

Resultado

Todo algoritmo de ese modelo necesita Ω(n log n) comparaciones en algún caso.

Lo comprobamos

La conclusión está restringida al modelo de comparaciones; no contradice métodos que usan estructura adicional de claves y otras operaciones.

Ahora lo resolvemos de principio a fin.

EJEMPLO RESUELTO 1

Dos bucles

Se ejecuta una operación por cada par (i,j) con 1≤i,j≤n.

  1. Objeto contado

    Cada ejecución corresponde a un par ordenado (i, j), con ambos índices entre uno y n.

  2. Primer índice

    Hay n elecciones posibles de i.

  3. Continuaciones

    Para cada una, j recorre las n posibilidades completas.

  4. Exacto

    El producto n·n=n² cuenta cada par una vez.

  5. Asintótico

    Una operación por par produce Θ(n²) en el modelo indicado.

  6. Comprobar

    Para n=1 hay un par y para n=2 hay cuatro; duplicar n cuadruplica este recuento.

Resultado

Θ(n²).

Lo comprobamos

Para n=1 hay una operación y para n=2 hay cuatro; el producto cuenta pares, sin omitir ninguno.

EJEMPLO RESUELTO 2

Búsqueda binaria

Máximo de mitades necesarias para reducir 1024 candidatos a uno.

  1. Tamaño inicial

    Se parte de 1024 candidatos.

  2. Potencia

    1024=2¹⁰.
  3. Cada reducción

    Tras k mitades ideales quedan 1024/2ᵏ candidatos.

  4. Final

    Para quedar uno se necesita 2ᵏ=1024, así k=10.
  5. Secuencia

    1024,512,256,128,64,32,16,8,4,2,1: once tamaños y diez cambios.

  6. Alcance

    Son diez reducciones del intervalo, no una afirmación de diez comparaciones exactas para cualquier implementación.

Resultado

Diez reducciones.

Lo comprobamos

La secuencia de tamaños es1024,512,256,128,64,32,16,8,4,2,1: once tamaños y diez transiciones.

Los símbolos que hemos usado

n
Tamaño de entrada, definido según el problema.
T(n)
Cantidad de operaciones contadas para entradas de tamaño n, en el caso especificado.
O(g(n))
Cota superior asintótica por un múltiplo constante de g.
Ω(g(n))
Cota inferior asintótica por un múltiplo positivo de g.
Θ(g(n))
Cotas superior e inferior del mismo orden g.
S(n)
Espacio utilizado; debe precisarse si incluye entrada o solo memoria auxiliar.
log₂n
Logaritmo de base2, adecuado al contar reducciones a la mitad.

Ahora te toca a ti.

Tienes cinco ejercicios. Inténtalos; las pistas te ayudan si te atascas.

Practicar este tema →

Para ampliar