Escolar Documentos
Profissional Documentos
Cultura Documentos
¿QUE ES LA
COMPLEJIDAD DE UN
ALGORITMO?
INDICE
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?....................................................................2
INTRODUCCION...........................................................................................................................2
EL TAMAÑO DE UN PROBLEMA...............................................................................................3
LA COMPLEJIDAD NO ES UN NUMERO: ES UNA FUNCION...............................................4
PEOR CASO, MEJOR CASO.........................................................................................................6
ASINTONAS Y ORDENES DE COMPLEJIDAD.........................................................................7
ORDENES DE COMPLEJIDADES MAS COMUNES.................................................................8
OPERACIONES CON LA COMPLEJIDAD..................................................................................9
RESUMEN, CONCLUSIONES Y RECOMENDACIONES.......................................................10
Página 1
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
INTRODUCCION
Por hacer una similitud acerca de lo complicado que es este concepto, la dificultad de la
complejidad es -salvando las distancias- como la de la predicción meteorológica: todos intuimos lo
complicado que es hacer una predicción meteorológica... miles de datos, fórmulas, modelos y
cálculos... sin embargo, cuando un meteorólogo nos explica con algo de gracia la predicción del
tiempo, la podemos entender bastante bien. Ya estamos muy acostumbrados a cosas como borrasca,
anticiclón, marejadilla, cota de nieve... Lo mismo pasa en cierto modo con la complejidad:
enfrentarnos a un algoritmo para hacer un estudio de su complejidad requiere de un gran esfuerzo.
Sin embargo, cuando alguien estudia un algoritmo y nos habla de su complejidad, entender el
concepto no es tan complicado.
Entender la complejidad es importante porque a la hora de resolver muchos problemas, utilizamos
algoritmos ya diseñados. Saber valorar su valor de complejidad puede ayudarnos mucho a conocer
cómo se va a comportar el algoritmo e incluso a escoger uno u otro.
Así que en este artículo, nos vamos a dedicar a intentar exponer qué es la complejidad de un
algoritmo desde un punto de vista sencillo y sin pretensiones, intentado distinguir qué impacto tiene
el que un algoritmo tenga una u otra complejidad. Y, como de costumbre, adoptamos grandes
simplificaciones, con el único ánimo de obtener una visión general de los conceptos. En cuanto a
cómo obtener la complejidad de un algoritmo... no nos vamos a meter mucho: los formalismos
necesarios quedan totalmente fuera del alcance de éste breve artículo divulgativo.
Primeramente, debemos tener claro qué es un algoritmo. Podemos entender por algoritmo una
secuencia de intrucciones cuyo objetivo es la resolución de un problema. El término clave aquí es
Página 2
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
el de problema.
Existen multitud de problemas de computación que se pueden resolver mediante un algoritmo.
Bueno... pues para resolver cada problema, podemos obtener más de un algoritmo que lo
solucione... pero... ¿Cual de ellos es el mejor? Sería conveniente poder aplicar algún tipo de
"puntuación" a los algoritmos, y que cuanta más puntuación sacara un algoritmo, pues supondremos
que es mejor. Eso es, en cierto modo, la complejidad.
Saber si un algoritmo es mejor que otro puede estudiarse desde dos puntos de vista: un algoritmo es
mejor cuanto menos tarde en resolver un problema, o bien es tanto mejor cuanta menos memoria
necesite.
A la idea del tiempo que consume un algoritmo para resolver un problema le llamamos
complejidad temporal y a la idea de la memoria que necesita el algoritmo le llamamos
complejidad espacial.
La complejidad espacial, en general, tiene mucho menos interés. El tiempo es un recurso mucho
más valioso que el espacio. (Esto lo podemos ver también en el mundo real: si tienes dinero puedes
comprarte una casa más grande, pero no puedes comprarte unos cuantos años más de vida).
Así que cuando hablamos de complejidad a secas, nos estamos refiriendo prácticamente siempre a
complejidad temporal.
Bueno... pues ya hemos presentado de manera intuitiva esto de la complejidad: la complejidad de un
algoritmo es un "valor", por así decirlo, que nos da una idea de cuánto va a tardar un algoritmo en
resolver el problema para el que fue diseñado.
EL TAMAÑO DE UN PROBLEMA
La idea que subyace tras el concepto de complejidad temporal de un algoritmo es, básicamente,
medir cuánto tarda en resolver el problema.
Para resolver cualquier problema, son necesarios unos datos de entrada sobre los que trabaja el
algoritmo y que describen una ocurrencia concreta del problema que queremos resolver. El
algoritmo, finalmente obtiene una o varias soluciones al problema (si es que el problema tiene
soluciones).
Sin embargo, debemos tener en cuenta algunas consideraciones. Por ejemplo, piensa en un típico
algoritmo para ordenar los elementos de un vector. Seguro que conoces alguno. El algoritmo consta
de una serie de instrucciones que se repiten una y otra vez (bucles), y probablemente, de una serie
de selecciones (comparaciones) que hacen que se ejecute uno u otro camino dentro del algoritmo.
Se hace necesaria una pregunta: ¿Tardará lo mismo un algoritmo de ordenación en ordenar un
vector con 100 valores que uno con 100000 valores?.... Obviamente no. Pues aquí es donde tenemos
que empezar a hablar del tamaño o talla del problema.
Un algoritmo de ordenación debería ser capaz de ordenar un vector con cualquier numero de
elementos. Sin embargo, el tamaño del vector incide directamente en el tiempo que tarda el
algoritmo en resolverse.
Pues cualquier problema tiene un tamaño, que es un valor o un conjunto de valores que se pueden
Página 3
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
obtener de los datos de entrada y que si varían, normalmente tienen una repercusión en el tiempo
que tardará el algoritmo en finalizar (aunque en algunos casos no).
Por ejemplo, del problema de ordenar un vector, la talla del problema nos la da el número de
elementos del vector.
En un algoritmo que halle el término n-ésimo de la sucesión de Fibonacci, la talla nos la dá el
propio término número n que queremos hallar.
Cada problema tiene uno o varios valores que determinan su talla.
La complejidad se calcula en función de una talla genérica, y no concreta. Por ejemplo, la
complejidad de un algoritmo de ordenación se calcula pensando en un array de longitud n, y no 5,
120 o 100000.
Otra consideración a tener en cuenta a la hora de tratar con la complejidad es que si estamos
contando el tiempo que tarda un algoritmo en resolver un problema ¿En qué ordenador lo
ejecutamos? Parece obvio que el mismo algoritmo ejecutado en un ordenador el doble de rápido que
otro tardará la mitad en encontrar la solución. ¿Cuál debería ser entonces la unidad de medida de la
complejidad? Ninguna unidad de tiempo nos vale: ni segundos ni milisegundos, porque el resultado
variaría de un ordenador a otro.
Además... parece obvio también que el mismo algoritmo tardará más o menos en solucionar un
problema de una talla u otra. Es decir, no puede tardarse lo mismo en ordenar un array de 100
valores que uno de 100000.
Bueno... pues vamos a adoptar una simplificación que nos permita no tener en cuenta en qué
ordenador se ejecutará el algoritmo: en lugar de medir tiempos, vamos a contar las instrucciones
que debe realizar el algoritmo. Supondremos que cada instrucción se ejecuta en un tiempo
constante.
Nos podemos permitir esa simplificación porque lo que realmente queremos saber es cómo crece el
número de instrucciones necesarias para resolver el problema con respecto a la talla del problema.
Eso es realmente la complejidad.
Por ejemplo, observa ésta función (da igual cuál sea su propósito).
Ejemplo 1
Página 4
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
En este algoritmo hay un par de bucles para que se ejecutan uno después del otro.
Observemos el primer bucle. Se ejecuta n veces, y en su interior hay una instrucción (la de la línea
5). Eso quiere decir que la línea 5 se ejecuta n veces. Después se ejecuta el segundo bucle, que
contiene en su interior dos instrucciones (las de las líneas 8 y 9). Como ese segundo bucle se ejecuta
también n veces y tiene dos instrucciones, se realizan 2n instrucciones. Finalmente hay una
instrucción en la línea 11 que se ejecuta una sola vez.
Bien.... el número de instrucciones que se ejecutan en total son n+2n+1... es decir, 3n+1
Todavía no hemos llegado al fondo de la cuestión, pero vamos encaminados. Podemos decir que la
complejidad de ese algoritmo es 3n+1, porque ese es el número de instrucciones que hay que
realizar para solucionar el problema cuando la talla del problema es n.
La idea que subyace es que podemos saber cómo se comporta el algoritmo conforme la talla del
problema va creciendo. En este caso, si representamos 3n+1 con respecto a n nos daremos cuenta de
que esta función es una recta. Para este algoritmo podemos suponer que cuando lo traslademos a un
lenguaje de programación concreto sobre un ordenador concreto, si para un n=100 tarda 7 unidades
de tiempo en solucionarlo, con unas pocas operaciones podemos deducir cuántas unidades de
tiempo tarda para un n=1000. Por supuesto, no es un valor exacto, pero lo que nos importa es saber
de qué manera aumenta el tiempo con respecto a la talla del problema.
Para terminar de ver la importancia de esto, vamos a ver un par de funciones más de ejemplo.
Ejemplo 2
Ilustración 1: Ejemplo 3
Página 5
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
Página 6
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
Ejemplo 4
Contiene en su interior un bucle mientras que no se ejecuta un número determinado de veces (como
ocurría en los ejemplos anteriores, que utilizaban bucles for). Imagina que pasamos a esa función un
vector de n=100 enteros, y un entero x=17. Es evidente que la talla del problema es n=100, ya que
es lo que determina el número de instrucciones que se ejecutarán. Sin embargo, es posible que el
valor 17 esté situado en la posición 30 del vector, con lo que el bucle se realizará 30 veces, o quizá
en la posición 50, o quizá no esté en el vector, con lo que el bucle se ejecutará n=100 veces,
recorriendo todo el vector.
En éste caso, nos conviene distinguir dos métricas: qué es lo peor que nos puede pasar para un
problema de tamaño n, y qué es lo mejor que nos puede pasar para un problema de tamaño n.
Vamos a echar unas pocas cuentas para el ejemplo de arriba. Vamos a suponer que en el interior del
bucle mientras hay 2 instrucciones (el si y el incremento de i), y en el exterior hay 3, dos antes del
mientras y una después.
Pues bien... lo mejor que nos puede pasar es que encontremos el valor x a la primera. En ese caso, el
bucle se ejecuta una sola vez.. El número de instrucciones que realizamos son 3+2=5.
Lo peor que puede pasar es que el valor x no se encuentre en el vector, así que el bucle se ejecutará
n veces, recorriendo todo el vector. El número de instrucciones que realizamos es 3+2n
Para expresar esto, se utiliza una notación específica, diremos que para este algoritmo, su
complejidad en el peor caso es O(2n+3) A esta notación se le denomina "O Grande" (del inglés
"Big-O"), o simplemente "Complejidad en el peor caso". Aunque es una "O", realmente viene de la
letra griega Omicron. Fue introducida por el matemático Paul Gustav Heinrich Bachmann.
Análogamente, diremos que para este algoritmo su complejidad en el mejor caso es Ω(5). A esta
notación se le denomina "Omega" (por la letra griega omega mayúscula Ω) o simplemente,
"complejidad en el mejor caso".
En general, cuando decimos "complejidad" a secas, casi siempre nos referimos a la complejidad en
el peor caso. Es decir... cuánto va a tardar el algoritmo como mucho.
Ya tenemos una ligera idea informal de qué es la complejidad. Pero entonces ¿Cómo se comparan
unos algoritmos con otros? Bueno... la idea de la complejidad de un algoritmo, es conocer cómo se
Página 7
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
comporta el tiempo de ejecución conforme la talla del problema va creciendo.... especialmente para
valores muy grandes... lo más grandes que podamos imaginar, y especialmente en el peor de los
casos.
En ese contexto de tallas muy grandes podemos hacer otra "simplificación", por así decirlo. El
hecho es que podemos encontrar ciertas similitudes entre las funciones que definen la complejidad
de los algoritmos. Por ejemplo, si comparamos todos los algoritmos cuya complejidad es lineal (es
decir, una recta... por ejemplo: 10n+3, 32n+12, 56n+1... o cualquier otro) y los comparamos con
todos aquellos cuya complejidad es cuadrática (Por ejemplo, 2n2+3, 4n2+n, 6n2+4n+3...) y
dibujamos sus funciones de complejidad en una gráfica observaremos que conforme n se va
haciendo grande, tienen un patrón de crecimiento bien diferenciado.
En ese contexto, podemos agrupar todas las complejidades que crecen igual en el mismo saco. A ese
saco le vamos a llamar orden de complejidad. Hablando un poco más formalmente, para todas las
funciones que agrupemos en un mismo orden, encontraremos una asíntota que al multiplicarla por
un valor nos acote a nuestra función superiormente cuando estemos tratando el peor caso.
Por ejemplo, todas las complejidades cuadráticas están acotadas asintóticamente por "n2". Eso
quiere decir que para cualquiera de las complejidades cuadráticas que hemos visto antes, por
ejemplo 6n2+4n+3, existe un valor real c que hace que 6n2+4n+3 ≤ cn2 cuando n se hace muy
grande, es decir, cuando n→∞
De esta manera, la complejidad suele clasificarse en una serie de órdenes comunes.
La notación para expresar esto es como sigue: Por ejemplo, para decir que 6n2+4n+3 está
determinado por la asíntota superior n2, decimos "que 6n2+4n+3 pertenece al órden de n2" o "que
6n2+4n+3 es del órden de n2" y lo escribimos formalmente de ésta manera:
6n2+4n+3 ∈ O(n2)
Como ves, finalmente el concepto de complejidad se nos simplifica mucho. En lugar de hallar los
tiempos exactos que tarda un algoritmo en solucionar los problemas, con lo que nos quedamos en
con una asíntota que representa a todos los algoritmos cuyo tiempo crece de igual forma cuando la
talla del problema tiende a infinito. Eso hace que el cálculo de la complejidad se simplifique mucho.
Si no queremos hallar la función de complejidad real (y casi nunca queremos), sino el orden al que
pertenece la complejidad del algoritmo, a la hora de hallarla, prácticamente ni siquiera tenemos que
contar las instrucciones... Cada secuencia de instrucciones se cuenta como una sola instrucción. Lo
que influye en la complejidad son principalmente los bucles y las sentencias de selección que tienen
efecto sobre los bucles. En los algoritmos recursivos, la cosa se complica un poco más... Pero como
hemos dicho al principio, el cálculo de la complejidad de un algoritmo queda fuera del alcance de
éste artículo.
Cuando decimos que la complejidad de un algoritmo es de un orden concreto, por ejemplo n2,
podemos estar seguros de que para cualquier valor de n, y por muy mal que nos vayan las cosas
(peor caso), el valor que obtengamos nunca será mayor que cn2, siendo c un real mayor que 1.
Página 8
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
Como ya hemos mencionado, la complejidad, digamos "real" en el peor caso de los algoritmos
puede enmarcarse en un "orden de complejidad". Dicho de otra manera, la complejidad de un
algoritmo "crece como" el representante de su orden cuando la talla del problema se hace muy
grande.
Bien... pues los órdenes de complejidad que se suelen manejar son éstos, de mejor a peor.
Hay otros órdenes intermedios, e incluso superiores... (realmente, tantos como queramos), pero
usualmente se suelen utilizar los de la tabla de arriba, que son órdenes bastante representativos.
Lo que estamos haciendo, al escoger esos órdenes es particionar todas las posibles complejidades en
una serie de "clases de complejidad". Por supuesto, a su vez, cada una de esas "clases de
complejidad" podría volverse a dividir en distintas subclases, pero eso puede convenirnos algunas
veces y otras no es práctico, por lo menos desde una visión general. Por ejemplo, el órden
exponencial contiene a O(2n) y a O(3n) y a O(4n)... etc. Lo mismo ocurre con (nc), que contiene a
O(n2), y a O(n3) .... y a O(n1000)...
Página 9
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
Para la complejidad en el mejor caso se utilizan los mismos órdenes, sólo que en lugar de utilizar la
letra O (Omicron) para denotarla se utiliza Ω (Omega).
Así pues, por ejemplo, para el algoritmo de ejemplo que obtenía si un valor x estaba en un array de
tamaño n, decimos que su complejidad (en el peor caso) está en el orden O(n) y (en el mejor caso)
en el orden Ω(1).
Hay muchas operaciones referidas a estos órdenes de complejidad que se pueden aplicar, pero en
general, vamos a comentar algunas de las más sencillas. Todas ellas tienen que ver con el cálculo de
límites. Estas reglas nos permiten "simplificar".... y dado un orden de complejidad, poder llegar a
uno de los de la tabla anterior.
Página 10
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
Es decir, si por ejemplo obtengo que la complejidad de un algoritmo es 6n3+5n+1, es correcto decir
que la complejidad de ese algoritmo es del orden de O(6n3+5n+1), ya que todas la funciones de ese
orden, incluida 6n3+5n+1 están asintóticamente acotadas por ella. Pero con esas reglas, también
podemos decir que la complejidad es del orden de O(6n3+5n), y también del O(6n3) y también del
órden O(n3), porque
O(6n3+5n+1) ⊂ O(6n3+5n) ⊂ O(6n3) ⊂ O(n3)
Pero normalmente, nos basta con saber que un algoritmo tiene un orden O(n3), que es uno de los
órdenes de uso común en lugar de afinar más y decir que el orden es O(6n3+5n+1) porque
realmente cuando n es grande no nos aporta nada afinar más.
Bueno... al final no ha sido tan difícil. Resulta que un algoritmo que resuelve un problema con una
determinada talla (por ejemplo, n) tarda un tiempo, en general, mayor en obtener la solución cuanto
mayor es esa talla n (cosa que resulta obvia). La complejidad es una medida que nos da una idea de
cómo es ese crecimiento, resultando que para la mayor parte de algoritmos, ese crecimiento se
puede enmarcar en un determinado "orden", ya que todas las funciones que están en un orden
crecen de manera similar cuando los valores de la talla se van haciendo grandes.
Para cada algoritmo, son interesantes un par de medidas de complejidad: en el peor caso (que
señalamos con la notación O (Omicron, "Big-O"), y en el mejor, para el que utilizamos la notación
Ω (Omega). En especial, suele ser más imprescindible conocer el peor caso, ya que nos da una idea
de qué es lo que puede pasar cuando las cosas van realmente mal.
Toda esta teoría de la complejidad, que tuvo un gran auge hace algunos años, ha sido criticada
desde muchos puntos de vista. La verdad es que es muy útil en un nivel teórico, pero un nivel
práctico tampoco conviene obsesionarse demasiado. Es necesario tener en cuenta que todo esto
tiene que ver con límites cuando el tamaño de los problemas es muy grande y teniendo en cuenta el
peor de los casos. A efectos prácticos, si estamos seguros de que el tamaño de nuestros problemas es
pequeño, incluso un algoritmo con una complejidad "intratable" puede obtener soluciones en un
tiempo razonable.
Además, muchos algoritmos con una elevada complejidad en el peor caso, resulta que no se
comportan tan mal en la práctica, ya que muchas veces los problemas que se resuelven con él no
son el peor caso. A veces, incluso se manipulan un poco los problemas para que nunca presenten el
peor caso, o se aplican técnicas que ahorren trabajo al algoritmo (heurísticas, podas, etc). La
complejidad del algoritmo es la misma, pero en promedio se comportan mucho mejor que lo que
cabría esperar vista su complejidad en el peor caso, simplemente porque no se suele llegar al peor
caso.
Por último, comentar que existen otras medidas de complejidad que las vistas hasta aquí, que son
simplemente máximos y mínimos teóricos cuando la talla del problema tiende a un número muy
grande. Y también, cuando un algoritmo se quiere probar "en la práctica" se aplican otras técnicas,
principalmente teniendo en cuenta la probabilidad que tiene un problema en aparecer o no. Por
ejemplo, los algoritmos de ordenación de arrays más sencillos tienen una complejidad máxima
Página 11
¿QUE ES LA COMPLEJIDAD DE UN ALGORITMO?
teórica de O(n2). Uno de los peores casos que se pueden presentar es que el array esté ordenado
completamente al revés de como queremos... pero en una aplicación real es muy poco probable que
esto ocurra... con lo que a veces la realidad no es tan mala, especialmente cuando los problemas no
tienen una talla muy elevada.
Página 12