Documentos de Académico
Documentos de Profesional
Documentos de Cultura
.
Polinomial si est en el orden de
.
Exponencial si est en el orden de
.
Otro aspecto importante al evaluar la complejidad de un algoritmo es que frecuentemente los
problemas tienen diversas instancias de un mismo tamao y el tiempo que tarda un algoritmo en
resolverlas puede variar mucho. En consecuencia, no tiene sentido evaluar la eficiencia de un algoritmo
solo en funcin del tamao de la instancia, pues el algoritmo debe funcionar correctamente para todas
las instancias. En el mejor escenario, el tiempo requerido suele ser poco. Sin embargo, si el tiempo de
respuesta es crtico, es necesario considerar el tiempo requerido para el peor escenario posible.
Tambin puede ser til analizar el caso promedio, aunque resulta muy difcil pues esto requiere conocer
a priori la distribucin de los casos a resolver. En consecuencia, el anlisis de la complejidad del caso
medio no ser considerado en este curso.
1.1.1 LA BSQUEDA DE LA EFICIENCIA
Es sabido que los computadores son cada vez ms rpidos. Esto podra conducir a la idea de que la
eficiencia de los algoritmos es cada vez menos importante. Sin embargo, no necesariamente es as.
Suponga que se tiene un algoritmo exponencial
que
tarda 10
que tarda 10
y suponga que
tarda 10
en la mquina
, que tarda 10
microsegundos y una
asignacin nunca requiere ms de
segundos.
7
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Por la definicin anterior se tiene que el tiempo de ejecucin del algoritmo est acotado por
+ +. Pero se sabe
que el tiempo requerido por cada operacin elemental no es importante pues se descartan las
constantes, por lo que se asume que pueden ser ejecutadas en tiempo unitario. Por tanto, se
puede afirmar que el algoritmo en cuestin es del orden de + +.
Formalizando la idea del ejemplo 1.1, se tiene que, para un algoritmo dado, la complejidad de tiempo
queda determinada por el tiempo de ejecucin de cada instruccin bsica y el nmero de veces que se
ejecuta esa instruccin.
Sea:
: un algoritmo para resolver un problema dado,
, ,
, 1 ,
, 1 .
La complejidad de tiempo del algoritmo A est dada por:
Como todas las instrucciones bsicas requieren un tiempo constante, se puede suponer que esos
tiempos son iguales, es decir:
= =
= 1. Luego, se tiene:
Es decir, la complejidad de tiempo corresponde al nmero total de instrucciones bsicas necesarias de
ejecutar para una computacin completa del algoritmo.
Ejemplo 1.2:
El problema de la suma de matrices consiste en, dadas ,
, calcular = +
:
Para 1 a hacer:
Para 1 a hacer:
Devolver .
Complejidad:
Aqu se observan dos ciclos Para y una asignacin, que es una instruccin bsica.
El primer ciclo Para se ejecuta veces.
Adems, el ciclo Para anidado dentro del primero se ejecuta veces.
8
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Luego, la instruccin bsica anidada en los dos ciclos se ejecuta veces (por el segundo
ciclo) por las veces que se ejecuta el primer ciclo.
La asignacin tiene lugar en un tiempo .
La complejidad de tiempo del algoritmo dado queda definida por:
=
Asumiendo = 1:
=
En particular, si = :
Del ejemplo 1.2 se desprenden las siguientes conclusiones, que se resumen en la figura 1.2:
Las instrucciones simples se ejecutan en un tiempo constante que se aproxima a 1.
Las instrucciones condicionales se consideran como una instruccin simple.
Los ciclos iterativos se ejecutan la cantidad de veces que tienen establecido repetirse. Esto es, si el
ciclo Para repite sus instrucciones desde 1 a , su tiempo es .
Los ciclos iterativos anidados se ejecutan en su tiempo correspondiente, multiplicado por las veces
que se repita el ciclo en el cual estn anidados.
FIGURA 1.2: Resumen para el clculo de complejidad computacional.
Ejemplo 1.3:
Problema: buscar un valor en un arreglo de enteros.
Entrada: el arreglo A y el valor c.
Salida: la posicin del valor en el arreglo.
Buscar(A, c):
j 1
Mientras A[j] < c y j < n hacer:
j j+1
Si A[j] = c hacer:
devolver j
9
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
En otro caso hacer:
devolver 0
Para determinar el tiempo de ejecucin del algoritmo, es necesario calcular la cantidad de
operaciones elementales que se realizan:
En la lnea 1 se ejecuta 1 operacin (asignacin).
En la lnea 2 se ejecuta la condicin del ciclo, conformada por un total de 4 operaciones:
dos comparaciones, un acceso al arreglo y un y lgico.
La lnea 3 cuenta con dos operaciones: un incremento y una asignacin.
La lnea 4 est conformada por una comparacin y un acceso al vector arreglo (2
operaciones).
La lnea 5 contiene una operacin de retorno si la condicin se cumple.
Anlogamente, la lnea 7 contiene una operacin de retorno si la condicin no se cumple.
En el mejor caso, solo se ejecuta la lnea 1 y la primera mitad de la condicin de la lnea 2, es
decir, solo 2 operaciones. A continuacin se ejecutan las lneas 5 a 7. As, para el mejor caso:
= 1 +2 +3 = 6
En el peor caso, se ejecuta la lnea 1 y el ciclo se repite hasta que se cumple la segunda
condicin. Cada iteracin del ciclo est dada por las instrucciones de las lneas 2 y 3 ms una
ejecucin adicional de la lnea 2 (para salir del ciclo). La ejecucin termina con la lnea 7. As,
para el peor caso se tiene que:
= 1 +4 +2
+4 +2 +1 = 6 +2
1.2 MODELO DE MQUINA
Se utiliza el modelo RAM (Mquina de Acceso Aleaotrio, Random Access Machine en ingls),
definido por el esquema de la figura 1.3.
Se denomina entrada a los datos del problema que se entregan al algoritmo y salida a la respuesta
entregada por ste.
FIGURA 1.3: Modelo RAM.
10
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
El anlisis de complejidad de un algoritmo se hace respecto al mejor caso (sus entradas ms favorables
en cuanto al tiempo requerido), al peor caso (las entradas menos favorables) y al caso medio.
Antes de seguir adelante, es necesario conocer algunas definiciones:
Paso: tiempo de una instruccin bsica.
Complejidad local: nmero total de pasos de la ejecucin completa del algoritmo para una entrada
dada.
Complejidad asinttica: menor cota superior de la complejidad local.
Complejidad del mejor caso: tiempo para ejecutar el algoritmo con la entrada ms favorable.
Complejidad del peor caso: tiempo en ejecutar el algoritmo con la entrada ms desfavorable.
Complejidad del caso medio: tiempo promedio en ejecutar el algoritmo con todas las entradas
posibles.
Dado un algoritmo A para resolver un problema , sean:
, . . . ,
, . . . ,
, . . . ,
/ 1 ,
La complejidad del peor caso es
/ 1 ,
La complejidad del caso medio est dada por:
La complejidad ms utilizada para confrontar los rendimientos de los algoritmos es la del peor caso.
Una de las razones es que da una cota para todas las entradas, incluyendo aquellas muy malas, que el
anlisis del caso promedio no puede ofrecer. Otra razn es que la complejidad del caso medio es ms
difcil de calcular, dado que es necesario conocer todas las posibles entradas del algoritmo y sus
probabilidades.
1.3 NOTACIN O
Dadas :
y :
tales que
10 2
11
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
FIGURA 1.4: Ejemplo de notacin O.
Propiedades de la notacin O:
1. + = + Regla de la suma
2. = = , . Constantes multiplicativas
3. = Regla de la multiplicacin
4. + = Cotas
5. Transitividad
Observaciones:
La notacin O desprecia las constantes aditivas y multiplicativas.
La medida de la complejidad es una aproximacin al tiempo real que el algoritmo requiere para
encontrar la solucin. Por ello aparece el smbolo ~ en lugar de =.
1.4 OTRAS NOTACIONES
Adems de la notacin O, existen otras notaciones asintticas que ayudan a acotar la complejidad de un
algoritmo:
Notacin : Dadas :
y :
tales que
.
Notacin : Dadas :
y :
tales que
y superiormente por
.
Notacin o: Dadas :
y :
y > 0. En otras
palabras, est acotada inferiormente por para todo valor
.
1.5 COMPLEJIDAD DE ALGORITMOS SECUENCIALES E ITERATIVOS
Anteriormente se estudi la forma de estimar el tiempo de ejecucin para un algoritmo dado mediante
la contabilizacin de las instrucciones que se ejecutan ante un escenario dado. Para determinar la
12
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
complejidad del algoritmo, basta con aplicar las cotas descritas sobre la frmula obtenida para el
tiempo de ejecucin.
As, para el ejemplo 1.3 se tiene que:
~6 = 1
~6 +2 =
Ejemplo 1.5:
Problema: dadas
, calcular =
.
Entrada:
.
Salida:
.
Para 1 a hacer:
Para 1 a hacer:
Para 2 a hacer:
Complejidad:
Aqu se observan tres ciclos Para anidados.
El primer ciclo Para se ejecuta veces.
Por cada vez que se ejecuta el primer ciclo, el segundo ciclo Para se ejecuta veces.
Por cada vez que se ejecuta el segundo ciclo, se tiene una instruccin bsica y un tercer ciclo
que se ejecuta 1 veces.
La complejidad de tiempo queda definida por
. En particular, si = =
, entonces
.
En este caso, la complejidad del mejor caso es igual a la complejidad del peor caso y a la
complejidad del caso medio, ya que cualquiera sea la entrada el algoritmo ejecuta todos los
pasos.
Ejemplo 1.6:
El factorial de un nmero natural se define por la multiplicacin desde 1 al nmero :
! = 1 2 3 .
Problema: dado , calcular !.
Entrada: .
Salida: !.
Si = 0 = 1 entonces:
1
En caso contrario hacer:
1
Para 2 a hacer:
13
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Complejidad:
Para la instruccin condicional (Si) y la asignacin se considera un tiempo unitario.
Para el caso contrario de la instruccin condicional se tiene:
Asignacin con tiempo 1.
Ciclo Para que se ejecuta n-1 veces.
Dentro del ciclo Para, una asignacin con tiempo 1.
Con los tiempos de cada instruccin y por las propiedades de la notacin O, la complejidad del
algoritmo 1.3 es = 1 +1 [1 + 1 1] = 1 +1 +n 1 . Aplicando las
propiedades de la notacin O se tiene que = 1 +1 + 1. Aqu se
podra ocupar la regla de la suma, pero se debe recordar que la notacin O desprecia las
constantes aditivas y multiplicativas, por tanto:
~.
Ejemplo 1.7:
La utilidad del uso de la complejidad aparece en su mayor expresin cuando se necesita optar
por alguna alternativa entre diferentes algoritmos para resolver un problema determinado. Para
esto, se debe saber discriminar entre un algoritmo y otro, para poder tomar la mejor decisin.
Suponga que la entrada de cierto problema tiene tamao y que, para resolverlo se encuentra
en la literatura las siguientes alternativas:
a. Algoritmo de tiempo
.
b. Transformarlo en otro problema para el cual se conoce un algoritmo de tiempo . La
transformacin demora log .
c. Modelarlo en un rbol de decisin de vrtices y ejecutar por cada vrtice del rbol un
algoritmo de tiempo .
La alternativa a no tiene mayor anlisis.
En la alternativa b se encuentra una transformacin. Esto no es ms que la aplicacin de un
algoritmo seguido de otro. Se puede observar entonces que la complejidad es + log .
Entre estas dos funciones se debe determinar la mayor para que la alternativa quede acotada a su
peor caso. Por ello, la complejidad de la alternativa b es ~ log .
La alternativa c del ejemplo 1.7 presenta un modelamiento con un rbol, por cada vrtice del
rbol se ejecuta un algoritmo de tiempo . Ahora bien, siendo los vrtices del rbol y
utilizando la regla de la multiplicacin, la alternativa c queda con complejidad ~
.
Se tiene entonces:
a.
b. log
c.
.
Se debe elegir la alternativa cuyo valor de funcin sea menor, esto es, porque el algoritmo
elegido debe ser el ms rpido, es decir que ocupe menor tiempo de ejecucin. Luego, en el
ejemplo 1.7 la alternativa elegida es la b.
14
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Ahora que ya se tienen las herramientas para analizar la complejidad de un algoritmo, es pertinente dar
algunas definiciones adicionales relativas a la eficiencia de algoritmos:
Un algoritmo es eficiente si su complejidad es un polinomio de la entrada, es decir, es de la forma
~ , donde es una constante.
Para algunos problemas es posible determinar el lmite inferior del problema que corresponde al
tiempo mnimo para resolverlo. Este lmite es una caracterstica del problema y refleja su dificultad
intrnseca para resolverlo. Cualquier algoritmo que lo resuelva utilizar por lo menos ese tiempo.
Un algoritmo es ptimo si su complejidad es ~, donde es el lmite inferior del problema.
Una observacin importante
Hay un aspecto del clculo de la complejidad que suele dar origen a confusin, y est ligado a la nocin de
instruccin bsica. Haba quedado establecido que las instrucciones bsicas son aquellas cuyo tiempo de
ejecucin est acotado superiormente por una constante que solo depende de la implementacin usada. En
otras palabras, pueden asociarse a las operaciones unarias y binarias que incorpora el procesador (asignacin,
cambio de signo, operaciones aritmticas, operaciones lgicas, comparaciones, etc.). Sin embargo, en los
ejemplos anteriores pareciera que no se hubiese considerado la condicin de la sentencia Si o de los ciclos
Para y Mientras, o bien que se hubiese considerado como instruccin simple a alguna sentencia que en
realidad contiene varias operaciones.
En efecto, si se tiene una sentencia como:
= 3 5 + / 6
Al momento de compilar o interpretar el programa se traduce en una serie de sentencias (los trminos
corresponden a variables temporales creadas por el compilador o intrprete):
= 5 +
= 3
6
=
No obstante, como se asume que cada una de ellas tiene un tiempo de ejecucin unitario y la notacin
asinttica descarta las constantes aditivas y multiplicativas, es posible ignorar este aspecto.
En el caso de las comparaciones y operaciones lgicas se observa el mismo fenmeno. Considere la
expresin:
3 < 8
Cuando se compila o interpreta el programa, esto se traduce en una secuencia de operaciones de la forma (los
nmeros agregados al comienzo de cada instruccin simplemente permiten identificar cada lnea para dirigir
los saltos):
0: si 3 3
1:
= falso
2: 4
3:
= verdadero
4: si < 8 7
5:
= falso
6: 8
7:
= verdadero
8:
15
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
1.6 COMPLEJIDAD DE ALGORITMOS RECURSIVOS
Se denomina recursin al proceso de resolver un problema grande descomponindolo en uno o ms
subproblemas que son de estructura idntica a la del problema original pero ms simples de resolver
que aquel. Cada subproblema se resuelve de forma similar, repitiendo la descomposicin hasta generar
subproblemas tan simples que se resuelven sin necesidad de descomponerlos. La solucin del problema
original se obtiene combinando las soluciones de los subproblemas en un proceso inverso a la
descomposicin realizada.
La recursin est muy ligada a la recurrencia matemtica, que en cuando se define una funcin f en
trminos de ella misma. Dos ejemplos clsicos de recurrencia matemtica son el clculo del factorial y
los nmeros de Fibonacci, que se muestran en los ejemplos 1.8 y 1.9, respectivamente.
Ejemplo 1.23: Funcin recurrente para el factorial.
=
1! 1
1 = 0
Ejemplo 1.24: Funcin recurrente para los nmeros de Fibonacci.
=
1 + 2 2
1 = 1
1 = 0
Cabe destacar que la recursividad es una propiedad de la estrategia para resolver un problema y no del
problema en s. As pues, se dice que un algoritmo es recursivo si corresponde a un procedimiento que
se invoca a s mismo hasta recibir como entrada un caso base.
En programacin, la recursin puede darse de dos formas:
1. Directa: en algn paso del conjunto de instrucciones aparece una invocacin al propio
procedimiento.
2. Indirecta: el procedimiento llama a otro procedimiento, y este ltimo llama a su vez al primero.
Asimismo, si un procedimiento llama a otros y, en algn momento, alguno de ellos llama al
primero, tambin es un caso de recursin indirecta.
En toda definicin recursiva de un problema se debe establecer una condicin de borde, es decir, una
condicin que no requiera otra definicin recursiva para su resolucin. sta condicin determina el
criterio de parada del algoritmo recursivo.
Para el ejemplo 1.23, por definicin se tiene que:
0! = 1
1! = 0! 1 = 1 1
2! = 1! 2 = 1 1 2
3! = 2! 3 = 1 1 2 3
etc.
Al definir 3! estamos usando 2!, y as hasta llegar al 0. En este caso, la condicin de borde es el
factorial de cero (0! = 1). En este punto se observa que la funcin ya no se llama a s misma, sino que
entrega el resultado y se detiene. En otras palabras, se asigna un valor directamente.
16
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Dado
1
Devolver Factorial.
Procedimiento
Si = 0 1 entonces:
Devolver 1
En caso contrario hacer:
Devolver 1.
La complejidad de un algoritmo recursivo se expresa mediante una ecuacin de recurrencia. Por
ejemplo, sea el tiempo para calcular !. Entonces:
=
1 +1 > 0
1 = 0
Si se resuelve esa ecuacin por sustitucin se encuentra:
= 1 +1 =
= 2 +1 +1 = 2 +2 =
= 3 +1 +1 +1 = 3 +3 =
= + =
= 1 + 1 =
= 0 + = +1
Luego, la complejidad del Algoritmo Factorial Recursivo es ~.
Una observacin importante es que, en este caso, la complejidad del algoritmo iterativo es igual a la del
algoritmo recursivo. No obstante, esto no siempre es as. Para algunos problemas es ms eficiente el
algoritmo recursivo; para otros, el algoritmo iterativo es mejor. Ms an, existen problemas para los
cuales es imposible desarrollar un algoritmo iterativo que no sea una simulacin de la recursin. La
secuencia de Fibonacci del ejemplo 1.9 es un caso en que el algoritmo iterativo es ms eficiente.
Complejidad:
Dado que un algoritmo recursivo se invoca a s mismo, al expresar el orden de su complejidad se
obtiene una ecuacin de recurrencia. Esta ecuacin depende del proceso de descomposicin del
problema en subproblemas.
Reduccin por sustraccin: si el tamao del problema decrece en una cantidad constante en cada
llamada, se realizan llamadas recursivas y las operaciones correspondientes a la parte no recursiva
del algoritmo toman un tiempo
, entonces:
= +
si .
17
Anlisis de Algoritmos y Estructura de Datos
Jacqueline Khler C.
Una observacin importante es que se considera un tiempo polinomial para la resolucin de un
problema solamente porque interesa que la solucin recursiva sea eficiente. Un tiempo exponencial,
por ejemplo, sera mucho ms costoso que un caso iterativo.
La solucin de esta ecuacin de recurrencia es de la forma:
=
< 1
= 1
> 1
Reduccin por divisin: si el algoritmo con un problema de tamao realiza llamadas recursivas con
subproblemas de tamao
entonces:
=
si .
La solucin de esta ecuacin de recurrencia es de la forma:
=
<
log =
>