Está en la página 1de 82

Análisis de Algoritmos

Elizabeth Pérez Cortés


René Mac Kinney Romero
Universidad Autónoma Metropolitana

12 de septiembre de 2005
2
Objetivos

Al nalizar el curso el alumno:

Será capaz de estimar los recursos de cómputo (memoria y tiempo) que


un algoritmo requiere para ejecutarse.

Conocerá y será capaz de utilizar técnicas de diseño adecuadas para


construir soluciones ecientes a diversas clases de problemas.

Reconocerá los problemas para los cuales no se conoce solución algo-


rítmica práctica y podrá aplicar criterios para darles soluciones apro-
ximadas ecientes.

3
4
Prefacio

El concepto de algoritmo es de importancia central en las ciencias de la


computación; matemáticos y cientícos de la computación lo han denido
en forma diferente. La palabra proviene del nombre de un matemático y as-
trónomo medieval de Uzbekistán, Mohammed ibn-Musa al-Khwarizmi, quien
dio las reglas para efectuar las 4 operaciones aritméticas básicas (suma, resta,
multiplicación y división) en el siglo IX.
El astrónomo medieval se estableció en Bagdad en la Casa del Conoci-
miento (Bait al-Hikma); escribió dos libros que jugaron un papel importante
en la historia de las matemáticas, uno de los cuales concierne al arte indio
de contar. Cuando su obra apareció traducida al latín en Europa, los lec-
tores empezaron a atribuirle no sólo el libro sino, también, un esquema de
numeración que hace uso de numerales indios, esto último se le atribuyó erró-
neamente. La nueva notación se convirtió en la de al-Khwarizmi, y en forma
descuidada fue nombrada algorismi y, nalmente, algorism o algorithm.
Esta palabra, originalmente derivada del nombre al-Khwarizmi, ahora signi-
ca, más ampliamente, cualquier regla particular de proceder u operar (tal
como lo es el método euclidiano para encontrar el máximo común divisor
de dos enteros). A partir de entonces y hasta nuestros días, la palabra, por
todo lo que representa, ha motivado muchos estudios de tipo teórico tanto
en matemáticas como en computación.
En la primera parte de este curso se introducen los conceptos básicos
para el análisis de algorítmos; se propone un procedimiento para estimar la
cantidad de recursos que un algoritmo consume cuando se ejecuta en una
computadora. Lo anterior nos permite conocer su valor práctico, es decir,
determinar si existe una solución algorítmica eciente para un problema en
particular. Enseguida, se da importancia a la velocidad de crecimiento de la
demanda de recursos por un algoritmo, conforme el tamaño del problema que
resuelve se incrementa. El procedimiento presentado nos permite comparar

5
6

y evaluar los costos de algoritmos tanto iterativos como recursivos.


En la segunda parte del curso nos avocamos al estudio de las principales
técnicas de diseño de algoritmos. Para cada una de ellas se caracterizan los
problemas a los que la técnica se puede aplicar, se presenta el método general
y se dan algunos ejemplos.
Finalmente, la tercera parte del curso está enfocada al estudio de la teo-
ría en torno a los problemas cuya solución algorítmica no se conoce o la
conocida requiere tantos recursos que en términos prácticos no resuelve mas
que problemas triviales. La teoría presentada permite identicar estos pro-
blemas así como también encontrar soluciones aproximadas (no óptimas pero
aceptables).
Índice general

I Análisis de algoritmos 11
1. Introducción 13
1.1. Conceptos Básicos . . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.1. Denición de algoritmo . . . . . . . . . . . . . . . . . . 14
1.2. Complejidad de algoritmos . . . . . . . . . . . . . . . . . . . . 15
1.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2. Comportamiento... 29
2.1. Dominio asintótico . . . . . . . . . . . . . . . . . . . . . . . . 29
2.2. Notación de orden . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.3. Interpretación de los conceptos enunciados . . . . . . . . . . . 41
2.4. Dominio asintótico con límites . . . . . . . . . . . . . . . . . . 43
2.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

3. Análisis de Algoritmos Iterativos 49


3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.2. Análisis por bloques . . . . . . . . . . . . . . . . . . . . . . . 52
3.2.1. Secuencia . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.2.2. Selección condicional . . . . . . . . . . . . . . . . . . . 52
3.2.3. Iteración . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

4. Análisis de Algoritmos Recursivos 59


4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.2. Sistemas recurrentes . . . . . . . . . . . . . . . . . . . . . . . 60
4.2.1. Expansión de recurrencias . . . . . . . . . . . . . . . . 62
4.3. Aproximando por integrales . . . . . . . . . . . . . . . . . . . 76
4.4. Elección de la operación básica . . . . . . . . . . . . . . . . . 77

7
8 ÍNDICE GENERAL

4.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

II Técnicas de Diseño de Algoritmos 83


5. Dividir para vencer 87
5.1. Forma genérica . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.2. Forma de la Función Complejidad . . . . . . . . . . . . . . . . 88
5.3. Mergesort
Ordenamiento por intercalación . . . . . . . . . . . . . . . . . 89
5.3.1. Problemas del algoritmo Mergesort. . . . . . . . . . . . 92
5.4. Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
5.5. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

6. Método Ávido 105


6.1. Problemas Tipo . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6.2. Almacenamiento en Cintas . . . . . . . . . . . . . . . . . . . . 106
6.3. Problema de la mochila . . . . . . . . . . . . . . . . . . . . . . 108
6.4. Intercalación óptima de archivos . . . . . . . . . . . . . . . . . 112
6.4.1. Problema . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.4.2. Estrategia . . . . . . . . . . . . . . . . . . . . . . . . . 113
6.4.3. Generalización a arboles de grado k . . . . . . . . . . . 115
6.4.4. Aplicación : Código de Human . . . . . . . . . . . . . 115
6.5. Arboles Generadores mínimos . . . . . . . . . . . . . . . . . . 116
6.5.1. Problema . . . . . . . . . . . . . . . . . . . . . . . . . 116
6.5.2. Estrategia . . . . . . . . . . . . . . . . . . . . . . . . . 116

7. Búsqueda con retroceso (Backtrack) 119


7.1. Problemas Tipo . . . . . . . . . . . . . . . . . . . . . . . . . . 119
7.2. Método de Solución . . . . . . . . . . . . . . . . . . . . . . . . 120
7.3. n Reinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7.3.1. Restricciones explicitas . . . . . . . . . . . . . . . . . . 123
7.3.2. Restricciones implícitas . . . . . . . . . . . . . . . . . . 123
7.4. Suma de Subconjuntos . . . . . . . . . . . . . . . . . . . . . . 125
7.4.1. Formulación 1 . . . . . . . . . . . . . . . . . . . . . . . 126
7.4.2. Restricciones . . . . . . . . . . . . . . . . . . . . . . . 126
7.4.3. Formulación 2 . . . . . . . . . . . . . . . . . . . . . . . 127
7.4.4. Restricciones . . . . . . . . . . . . . . . . . . . . . . . 128
ÍNDICE GENERAL 9

7.5. Coloreado de grácas . . . . . . . . . . . . . . . . . . . . . . . 128


7.5.1. Solución . . . . . . . . . . . . . . . . . . . . . . . . . . 129

8. Árboles de juego 133


8.1. Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
8.2. Solución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.3. Método Min-Max . . . . . . . . . . . . . . . . . . . . . . . . . 134
8.4. Funciones de Utilidad . . . . . . . . . . . . . . . . . . . . . . . 134
8.5. Gato (n-en línea) . . . . . . . . . . . . . . . . . . . . . . . . . 136
8.5.1. Poda alfa-beta . . . . . . . . . . . . . . . . . . . . . . . 136
8.5.2. Reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

9. Complejidad de los algoritmos diseñados con Backtrack. 137


9.1. Planicación y Acotamiento . . . . . . . . . . . . . . . . . . . 138
9.1.1. Problema de la ramicación y acotamiento . . . . . . . 138
9.1.2. Búsqueda de menor costo . . . . . . . . . . . . . . . . 139

III Problemas NP 141


10.Problemas Tratables e Intratables 143
10.1. Algoritmos No Deterministicos . . . . . . . . . . . . . . . . . . 143
10.2. Interpretaciones . . . . . . . . . . . . . . . . . . . . . . . . . . 144
10.3. Problema de decisión del Clan . . . . . . . . . . . . . . . . . . 150
10.4. Problema de la cobertura de nodos . . . . . . . . . . . . . . . 151
10.5. Problema de la planeación . . . . . . . . . . . . . . . . . . . . 155
10.6. Generación de código óptimo . . . . . . . . . . . . . . . . . . . 156
10.7. Problemas NP Computables y No decidibles . . . . . . . . . . 157
10 ÍNDICE GENERAL
Parte I
Análisis de algoritmos

11
Capítulo 1
Introducción

En las Ciencias de la Computación cuando se dice que un problema tie-


ne solución, signica que existe un algoritmo susceptible de implantarse en
una computadora, capaz de producir la respuesta correcta para cualquier
instancia del problema en cuestión.
Para ciertos problemas es posible encontrar más de un algoritmo capaz
de resolverlos, lo cual nos enfrenta al problema de escoger alguno de ellos. La
tarea de decidir cuál de ellos es el mejor debe basarse en criterios acordes a
nuestros intereses. En la mayoría de los casos la elección de un buen algoritmo
está orientada hacia la disminución del costo que implica la solución del
problema; bajo este enfoque es posible dividir los criterios en dos clases:

a) Criterios orientados a minimizar el costo de desarrollo: claridad, senci-


llez y facilidad de implantación, depuración y mantenimiento.

b) Criterios orientados a disminuir el costo de ejecución: tiempo de pro-


cesador y cantidad de memoria utilizados.

Si el programa que implanta el algoritmo se va a ejecutar unas cuantas


veces, el costo de desarrollo es dominante, en este caso se debe dar más
peso a los primeros criterios. Por el contrario, si el programa se va a utilizar
frecuentemente, dominará el costo de ejecución, y por ende, se debe elegir un
algoritmo que haga uso eciente de los recursos de la computadora, es decir,
se le debe evaluar prioritariamente bajo los criterios de la segunda clase. En
cualquier caso, ninguno de los criterios de evaluación debe eliminarse por
completo; pues, por ejemplo, si se implanta un sistema con algoritmos muy

13
14 CAPÍTULO 1. INTRODUCCIÓN

ecientes pero confusos, puede suceder que después no sea posible darle el
mantenimiento adecuado.
Los recursos que consume un algoritmo pueden estimarse mediante he-
rramientas teóricas y constituyen, por lo tanto, una base conable para la
elección de un algoritmo. En las Ciencias de la Computación, la actividad
dedicada a determinar la cantidad de recursos que consumen los algoritmos
se le denomina análisis de algoritmos.
La primera sección de este capítulo presenta los conceptos que nos permi-
tirán introducir después, en secciones posteriores, las técnicas utilizadas para
la medición de los recursos que consume un algoritmo y las bases teóricas
sobre las que estas se fundamentan.

1.1. Conceptos Básicos


En esta sección presentamos los conceptos básicos del análisis de algorit-
mos.

1.1.1. Denición de algoritmo


Un algoritmo es un conjunto nito de instrucciones no ambiguas y efec-
tivas que indican cómo resolver un problema, producen al menos una salida,
reciben cero o más entradas y, para ejecutarse, necesitan una cantidad nita
de recursos.
Una instrucción es no ambigua cuando la acción a ejecutar está perfecta-
mente denida, por ejemplo, instrucciones del tipo:

x ← log(0)
o
x ← (10 o 35)/3
no pueden formar parte de un algoritmo.
Una instrucción es efectiva cuando se puede ejecutar en un intervalo nito
de tiempo, por ejemplo, las instrucciones

x ← 2 + 8, mensaje ← Une('Hola','Mundo')
son efectivas, mientras que

x ← cardinalidad(números naturales)
1.2. COMPLEJIDAD DE ALGORITMOS 15

no lo es.
Si un conjunto de instrucciones tiene todas las características de un al-
goritmo, excepto ser nito en tiempo se le denomina proceso computacional.
Los sistemas operativos son el mejor ejemplo de proceso computacional, pues
están diseñados para ejecutar tareas mientras las haya pendientes, y cuando
éstas se terminan, el sistema operativo entra en un estado de espera, hasta
que llegan más, pero nunca termina.
Se asume que un problema tiene solución algorítmica si además de que el
algoritmo existe, su tiempo de ejecución es razonablemente corto. Por ejem-
plo, es posible diseñar un algoritmo para jugar ajedrez que triunfe siempre: el
algoritmo elige la siguiente tirada examinando todas las posibles secuencias
de movimientos desde el tablero actual hasta uno donde sea claro el resultado
y elige la tirada que le asegure el triunfo; el pequeño inconveniente de este
algoritmo es que dicho espacio de búsqueda se ha estimado en 100040 tableros
por lo que puede tardarse miles de años en tomar una decisión. Por lo tanto,
para nes prácticos se considera que si un problema tiene una solución que
toma años en computar, dicha solución no existe.
Considérese ahora el problema de ordenar un conjunto de valores, si el
conjunto tiene 2 elementos es más fácil resolverlo que si tiene 20, análoga-
mente un algoritmo que resuelva el problema tardará más tiempo mientras
más grande sea el conjunto y requerirá una cantidad de memoria mayor para
almacenar los elementos del conjunto.
En general la cantidad de recursos que consume un algoritmo para re-
solver un problema se incrementa conforme crece el tamaño del problema.
Dependiendo del problema en particular, uno o varios de sus parámetros será
elegido como tamaño del problema. Haciendo una cuidadosa observación, es
relativamente fácil efectuar una elección atinada.
En la Figura 1 se presenta una serie de problemas y una sugerencia acerca
del parámetro que se puede elegir como tamaño del problema.

1.2. Complejidad de algoritmos


La función complejidad, f (n), donde n es el tamaño del problema, da una
medida de la cantidad de recursos que un algoritmo necesitará al implantarse
y ejecutarse en alguna computadora. Puesto que la cantidad de recursos que
consume un algoritmo crece conforme el tamaño del problema se incrementa,
la función complejidad es monótona creciente (f (n) > f (m) ⇔ n > m) con
16 CAPÍTULO 1. INTRODUCCIÓN

PROBLEMA TAMAÑO DEL PROBLEMA


Búsqueda de un elemento en un con- Número de elementos en el conjunto
junto
Multiplicar dos matrices Dimensión de las matrices
Recorrer un árbol Número de nodos en el árbol
Resolver un sistema de ecuaciones li- Número de ecuaciones y/o incógnitas
neales
Ordenar un conjunto de valores Número de elementos en el conjunto

Figura 1: Elección del tamaño del problema.

respecto al tamaño del problema.


La memoria y el tiempo de procesador son los recursos sobre los cuales
se concentra todo el interés en el análisis de un algoritmo, así pues distingui-
remos dos clases de función complejidad:

a) Función complejidad espacial. Mide la cantidad de memoria que nece-


sitará un algoritmo para resolver un problema de tamaño n.

b) Función complejidad temporal. Indica la cantidad de tiempo que re-


quiere un algoritmo para resolver un problema de tamaño n; viene a
ser una medida de la cantidad de CPU que requiere el algoritmo.

La cantidad de memoria que utiliza un algoritmo depende de la implanta-


ción, no obstante, es posible obtener una medida del espacio necesario con la
sola inspección del algoritmo. Para obtener esta cantidad es necesario sumar
todas las celdas de memoria que utiliza. En general se requerirán dos tipos
de celdas de memoria:

a) Celdas estáticas. Son las que se utilizan en todo el tiempo que dura la
ejecución del programa, por ejemplo, las variables globales.

b) Celdas dinámicas. Se emplean sólo durante un momento de la ejecución,


y por tanto pueden ser asignadas y devueltas conforme se ejecuta el
algoritmo, por ejemplo, el espacio de la pila utilizado por las llamadas
recursivas.

En este contexto, una celda de memoria no tiene una medida física en


correspondencia, se le utilizará sólo para especicar el espacio empleado por
1.2. COMPLEJIDAD DE ALGORITMOS 17

una variable simple, sin importar el tipo al que pertenezca. Las variables que
tengan valores de un tipo simple ocuparán una celda de memoria, mientras
que a las variables de tipos compuestos se les asignan tantas celdas como
requieran los elementos que las componen.
El tiempo que emplea un algoritmo en ejecutarse reeja la cantidad de
trabajo realizado, así, la complejidad temporal da una medida de la cantidad
de tiempo que requerirá la implantación de un algoritmo para resolver el
problema, por lo que se le puede determinar en forma experimental. Por
ejemplo, para encontrar el valor de la función complejidad de un algoritmo
A que se codica un lenguaje de programación L, se compila utilizando el
compilador C y se ejecuta en la máquina M, damos algunas entradas al
programa y medimos el tiempo de procesador empleado para resolver los
casos del problema.
Proceder de esta manera presenta, entre otros, el inconveniente de que
los resultados obtenidos dependen de:
a) las entradas proporcionadas,

b) la calidad del código generado por el compilador utilizado, y

c) de la máquina en que se hagan las corridas.


Para evitar que estos factores se reejen en el cálculo de la función com-
plejidad, el análisis temporal (y el espacial) se hará únicamente con base al
algoritmo escrito en pseudocódigo. Como el pseudocódigo no se puede ejecu-
tar para medir la cantidad de tiempo que consume, la complejidad temporal
no se expresará en unidades de tiempo, sino en términos de la cantidad de
operaciones que realiza. Cada operación requiere cierta cantidad constante
de tiempo para ser ejecutada, por esta razón si se cuenta el número de o-
peraciones realizadas por el algoritmo se obtiene una estimación del tiempo
que le tomará resolver el problema.
Dado un algoritmo, se puede determinar que tipos de operaciones utiliza
y cuantas veces las ejecuta para una entrada especíca. Observemos este
procedimiento en el siguiente ejemplo.

Ejemplo 1 Considérese el Algoritmo 1, que realiza búsqueda lineal sobre un


arreglo A con n elementos, y devuelve la posición en la que se encuentra el
elemento Valor; si Valor no se encuentra devuelve n + 1. El algoritmo realiza
sumas (+), asignaciones (←), comparaciones (≤ y 6=) y operaciones lógicas
(∧).
18 CAPÍTULO 1. INTRODUCCIÓN

Si el ciclo mientras se realiza k veces, el algoritmo ejecutará las operacio-


nes siguientes:

k sumas (una por cada iteración).


k + 2 asignaciones (las del ciclo y las realizadas fuera del ciclo).
k + 1 operaciones lógicas (la condición se debe probar k + 1 veces,
la última es para saber que el ciclo no debe volver a ejecutarse).
k + 1 comparaciones con el índice.
k + 1 comparaciones con elementos de A.
k + 1 accesos a elementos de A.
6k + 6 operaciones en total.

func BúsquedaLineal (V alor, A, n) ≡


comienza
i ← 1;
mientras (i ≤ n) ∧ (A[i] 6= V alor) haz
i ← i + 1;
zah;
BúsquedaLineal ← i;
termina.
Algoritmo 1: Búsqueda lineal

Del Ejemplo 1 notamos que el número de veces que se ejecutan algunas


operaciones, para valores sucesivamente mayores que k, presenta un modelo
de crecimiento similar al que tiene el número total de operaciones que eje-
cuta el algoritmo1 . Para hacer una estimación de la cantidad de tiempo que
tarda un algoritmo en ejecutarse, no es necesario contar el número total de
operaciones que realiza. Se puede elegir alguna, a la que se identicará como
operación básica que observe un comportamiento parecido al del número to-
tal de operaciones realizadas y que, por lo tanto, será proporcional al tiempo
1 Posteriormente,
cuando se introduzca el concepto de orden, se dirá que ambas canti-
dades son del mismo orden
1.2. COMPLEJIDAD DE ALGORITMOS 19

total de ejecución. Para el algoritmo anterior se puede contar cualquiera de


las operaciones mencionadas. En general, debe procurarse que la operación
básica, en la cual se basa el análisis, de alguna forma esté relacionada con el
tipo de problema que se intenta resolver, ignorando las asignaciones de valo-
res iniciales y las operaciones sobre variables para control de ciclos (índices).
La operación básica en el Algoritmo 1 (búsqueda lineal) es la comparación
entre los elementos del arreglo y el valor buscado, pues cumple con las con-
sideraciones anteriores.

Ejemplo 2 El Algoritmo 2 obtiene el producto de los dos valores más gran-


des contenidos en un arreglo A de n enteros. Tomaremos este problema para
ejemplicar la elección de la operación básica con miras a hacer el análisis
del algoritmo.
En este algoritmo se realizan las siguientes operaciones:
a) Comparación entre mayor1 , mayor2 y los elementos del arreglo.

b) Asignaciones a mayor1 y mayor2

c) Asignación al índice i

d) Asignación a la función

e) Producto de los mayores

f ) Incremento al índice i

g) Comparación entre el índice i y la longitud del arreglo n


Las operaciones (c), (f) y (g) no se consideran por realizarse entre índi-
ces, las operaciones (d) y (e) se ejecutan una sola vez y no son proporcio-
nales al número total de operaciones. Entonces, se tiene que las operaciones
que se pueden considerar para hacer el análisis son: las comparaciones entre
mayor1 , mayor2 y los elementos del arreglo y las asignaciones a los elementos
mayores.

Con el n de remarcar aún más la elección de la operación básica, la


Figura 2 ilustra la selección de operaciones básicas en algunos algoritmos
muy comunes.
Resumiendo, el análisis de un algoritmo se puede hacer considerando sólo
aquella operación que cumpla los siguientes criterios:
20 CAPÍTULO 1. INTRODUCCIÓN

func Producto2Mayores (A, n) ≡


comienza
si A[1] > A[2] entonces
mayor1 ← A[1];
mayor2 ← A[2];
otro
mayor1 ← A[2];
mayor2 ← A[1];
is
i ← 3;
mientras i ≤ n haz
si A[i] > mayor1 entonces
mayor2 ← mayor1;
mayor1 ← A[i];
otrosi A[i] > mayor2 entonces
mayor2 ← A[i];
is
i ← i + 1;
zah
Producto2Mayores ← mayor1 × mayor2;
termina.
Algoritmo 2: Producto de los valores más grandes de una lista de números.

PROBLEMA OPERACIÓN BÁSICA


Búsqueda de un elemento en un con- Comparación entre el valor y los ele-
junto mentos del conjunto
Multiplicar dos matrices Producto de los elementos de las ma-
trices
Recorrer un árbol Visitar un nodo
Resolver un sistema de ecuaciones li- Suma
neales
Ordenar un conjunto de valores Comparación entre valores

Figura 2: Elección de la operación básica para algunos problemas.


1.2. COMPLEJIDAD DE ALGORITMOS 21

a) Debe estar relacionada con el tipo de problema que se resuelve.

b) Debe ejecutarse un número de veces cuyo modelo de crecimiento sea


similar al del número total de operaciones que efectúa el algoritmo.
Si ninguna de las operaciones encontradas cumple con ambos criterios,
es posible declinar el primero. Si aun así no es posible encontrar una ope-
ración representativa, se debe hacer un análisis global, contando todas las
operaciones.
En la teoría de algoritmos es muy frecuente usar el término instancia para
indicar un caso especíco de un problema. Así, por ejemplo, si el problema
es la multiplicación de 2 enteros positivos una instancia es el par de números
a multiplicar, v.g. (1966, 24).
Con los conceptos enunciados hasta aquí es posible calcular la función
complejidad temporal ft (n) para un algoritmo simple. En el Ejemplo 3 se
calcula dicha función para el algoritmo 1.

Ejemplo 3 Considérese el Algoritmo 1, para hacer el análisis de su compor-


tamiento tomemos como operación básica las comparaciones con elementos
del arreglo y como caso muestra: A = [2, 7, 4, 1, 3] y n = 5.
Si Valor = 2, se hace una comparación, ft (5) = 1
Si Valor = 4, se hacen tres comparaciones, ft (5) = 3
Si Valor = 8, se hacen cinco comparaciones, y ft (5) = 5

Del Ejemplo 3 es posible descubrir que la función complejidad no es tal,


en realidad es una relación, ya que para un mismo tamaño de problema se
obtienen distintos valores de la función complejidad. Como éste, hay muchos
algoritmos en los que el número de operaciones depende, no sólo del tamaño
del problema, sino también de la instancia especíca que se presente, entonces
¾de qué manera se puede expresar el valor de la función complejidad? A
continuación damos una respuesta.
Sea I(n) = {I1 , I2 , I3 , . . . , Ik } el conjunto de instancias del problema cuyo
tamaño es n. Sea O(n) = {O1 , O2 , O3 , . . . , Ok } el conjunto formado por el
número de operaciones que un algoritmo realiza para resolver cada instancia.
Entonces, Oj es el número de operaciones ejecutadas para resolver la instan-
cia Ij , para 1 ≤ j ≤ k. Se distinguen tres casos en el valor de la función
complejidad temporal
22 CAPÍTULO 1. INTRODUCCIÓN

a) Peor caso
ft (n) = máx({O1 , O2 , O3 , . . . , Ok })

b) Mejor caso
ft (n) = mı́n({O1 , O2 , O3 , . . . , Ok })

c) Caso medio
k
X
ft (n) = Oi P (i)
i=1

donde P (i) es la probabilidad de que ocurra la instancia Ii .


En otras palabras, el mejor caso se presenta cuando para una entrada de
tamaño n, el algoritmo ejecuta el mínimo número posible de operaciones, el
peor caso cuando hace el máximo y en el caso medio se consideran todos
los casos posibles para calcular el promedio de las operaciones que se hacen
tomando en cuenta la probabilidad de que ocurra cada instancia.
Los casos en la función complejidad espacial, se pueden denir análoga-
mente, considerando ahora el conjunto C(n), como el conjunto formado por
el número de celdas de memoria utilizadas por el algoritmo para resolver cada
instancia del problema.
En los Ejemplos 4 y 5 se ilustra el análisis temporal de los Algoritmos 1
y 2. Para estos problemas el número de operaciones varía considerablemente
para instancias diferentes del mismo tamaño.

Ejemplo 4 Algoritmo de búsqueda lineal.


Problema: Búsqueda lineal de un valor en un arreglo de
tamaño n.
Tamaño del Problema: n = número de elementos en el arreglo.
Operación básica: Comparación del valor con los elementos del
arreglo.

Análisis Temporal
i) Mejor caso: ocurre cuando el valor es el primer elemento del arreglo.
ft (n) = 1

ii) Peor caso: sucede cuando el valor no se encuentra en el arreglo.


ft (n) = n + 1
1.2. COMPLEJIDAD DE ALGORITMOS 23

iii) Caso medio:

ft (n) = 1P (1) + 2P (2) + 3P (3) + 4P (4) + ... + nP (n) + nP (n + 1)

donde P (i) es la probabilidad de que el valor se encuentre en la localidad


i, (1 ≤ i ≤ n) y P (n + 1) es la probabilidad de que no esté en el arreglo. Si
se supone que todos los casos son igualmente probables
1
P (i) =
n+1
n+1
1 X (n + 1)(n + 2) n+2
ft (n) = i= =
n + 1 i=1 2(n + 1) 2

Ejemplo 5 Algoritmo producto mayores.


Problema: Dado un arreglo de valores, encontrar el
producto de los dos números mayores.
Tamaño del Problema: n =número de elementos en el arreglo.
Operación básica: Comparación entre los elementos del arreglo
y los mayores.

Análisis Temporal
i) Mejor caso: ocurre cuando el arreglo está ordenado ascendentemente
(en cada iteración sólo hace la primera comparación)

ft (n) = 1 + (1 × (n − 2)) = n − 1

ii) Peor caso: el arreglo está ordenado de manera descendente (se hacen
dos comparaciones por iteración en el ciclo)

ft (n) = 1 + (2 × (n − 2)) = 2n − 3
µ ¶
|U |
iii) Caso medio: en este problema se tienen n! casos, donde U es
n
el conjunto del que se extraen los elementos del arreglo.

Y para hacer el cálculo se deben contar las operaciones que se harían en


cada caso.
24 CAPÍTULO 1. INTRODUCCIÓN

La tarea de conteo bajo esta metodología resulta excesivamente laboriosa,


por lo que se recurrirá a otro mecanismo. El algoritmo hace siempre una
comparación al inicio y en el interior del ciclo puede ser que se realice una
comparación o dos; obsérvese que para cada A[i] puede ser cierta una de tres
aseveraciones:

i) A[i] > mayor1 se hace una comparación

ii) mayor1 ≥ A[i] > mayor2 se hacen dos comparaciones

iii) mayor2 ≥ A[i] se hacen dos comparaciones.

Si cada caso tiene la misma probabilidad de ocurrencia, entonces en promedio,


se harán
1 5
(1 + 2 + 2) =
3 3
comparaciones por iteración, por lo tanto el algoritmo realizará en promedio

5 (5n − 7)
ft (n) = 1 + (n − 2) =
3 3

comparaciones.

Es importante mencionar que no todos los algoritmos presentan casos, y


resulta interesante tener una herramienta para detectar cuándo se particio-
nará el análisis en casos; por el momento la única ayuda con la que se cuenta
es la intuición y preguntarse: ¾se puede resolver el problema de manera trivial
para alguna instancia especíca? Si la respuesta es armativa el algoritmo
tendrá casos, por lo que el problema de detección se reduce a contestar esta
simple pregunta.
Con lo expuesto en esta sección y con habilidades (formales e informales)
de conteo, se puede estimar la cantidad de recursos que un algoritmo consu-
me; pero el objetivo no sólo es conocer dicha cantidad, sino, más aún, poder
comparar el comportamiento de distintos algoritmos, con el n de decidir
cuál de ellos utilizar bajo circunstancias especícas. En la siguiente sección
se dan las bases teóricas para tomar de manera conable la decisión.
1.3. EJERCICIOS 25

1.3. Ejercicios
Ejercicio 1.1 La regla para multiplicar las matrices A y B de dimensión
2 × 2, y obtener C = AB, es:

c11 = a11 b11 + a12 b21 c12 = a11 b12 + a12 b22
c21 = a21 b11 + a22 b21 c22 = a21 b12 + a22 b22

ésta involucra 8 multiplicaciones y 4 sumas.


Diseñe un algoritmo que use solamente 7 multiplicaciones. ¾Cuántas su-
mas usa su algoritmo?
[Sugerencia. Exprese cada entrada de la matriz C, como una suma de las
fórmulas de Strassen:

m1 = (a11 + a22 )(b11 + b22 ) m5 = (a11 + a12 )b22


m2 = (a21 + a22 )b11 m6 = (a21 − a11 )(b11 + b12 )
m3 = a11 (b12 − b22 ) m7 = (a12 − a22 )(b21 + b22 )
m4 = a22 (b21 − b11 )

Ejercicio 1.2 El algoritmo 3 encuentra la representación binaria de un en-


tero decimal positivo. El algoritmo se basa en divisiones sucesivas por 2. Así,
para un entero positivo N se calcula:

N = 2q0 + r0
q0 = 2q1 + r1
q1 = 2q2 + r2
..
.
qk−1 = 2qk + rk

donde cada residuo ri es 0 o 1. El algoritmo se detiene cuando qk = 0. La


representación binaria es entonces N 2 = rk rk−1 . . . r1 r0 .

i) Denir uno de los parámetros como tamaño del problema, justique su


elección.
26 CAPÍTULO 1. INTRODUCCIÓN

proc RepresentaciónBinaria(N,N2) ≡
comienza
y ← N;
i ← 0;
mientras (y 6= 0) haz
si par (y) entonces
r[i] ← 0
otro
r[i] ← 1;
y ←y−1
is
y ← y/2;
i ← i + 1;
zah
k ← i − 1;
N 2 ← r[0...k];
termina.
Algoritmo 3: Representación binaria.

ii) Elija la operación básica para basar el análisis temporal y calcule la


complejidad temporal.

iii) Muestre que si elige el valor del número N como el tamaño del proble-
ma, el número de operaciones que requiere el algoritmo es b 10
3
log N c+1.

Ejercicio 1.3 Suponer que se desea encontrar el elemento menor de una


secuencia nita de enteros x1 , x2 , . . . , xn , usando el algoritmo 4.
i) Sugerir una medida razonable, para el tamaño del problema.

ii) Realice el análisis temporal y espacial para el algoritmo.

Ejercicio 1.4 Para el algoritmo de ordenamiento denominado Burbuja, efec-


túe el análisis temporal y espacial en todos los casos.

Ejercicio 1.5 Para el algoritmo 2 muestre que el modelo de crecimiento del


total de operaciones, conforme el tamaño del problema se incrementa, es
similar al que observa la operación básica.
1.3. EJERCICIOS 27

proc BuscaMínimo (X) ≡


comienza
b ← X[1];
para j ← 2 a n haz
si (X[j] < b) entonces
b ← X[j]; (* b = min{x1 , x2 , . . . , xn } *)
is
zah
termina.
Algoritmo 4: Valor mínimo.

Ejercicio 1.6 Se tiene un archivo de n registros y cada registro contiene m


datos que deben actualizarse periódicamente.

i) Diseñe un algoritmo que actualice tal archivo. Dicho algoritmo recibe


como entrada el número de registros en el archivo y una lista que con-
tiene los número de registro que se han de actualizar. Si un número de
registro está en la lista, todos sus datos se actualizan.

ii) Denir uno de los parámetros como tamaño del problema.

iii) Elegir una operación básica.

Calcular la complejidad temporal en el mejor caso, en el peor caso y en el


caso medio, asumiendo que P (0) = 0, P (1) = P (2) = P (3) = · · · = P (n),
y después asumiendo que P (k) = ( n2k2 + n); 0 ≤ k ≤ n, donde P (k) es la
probabilidad de que deban actualizarse los datos de k registros.

Ejercicio 1.7 Dada una lista ordenada con representación ligada sencilla,
escriba un algoritmo para insertar un elemento y obtenga la complejidad tem-
poral del mismo.

Ejercicio 1.8 Dado un arreglo con n elementos, escribir un algoritmo para


obtener el k−ésimo elemento más pequeño, encontrando los k menores si
k ≤ n2 y los n − k + 1 mayores si k > n2 . Para este algoritmo, obténgase la
complejidad temporal en todos los casos.
28 CAPÍTULO 1. INTRODUCCIÓN
Capítulo 2
Comportamiento Asintótico de
Funciones

2.1. Dominio asintótico


Cuando se implanta un algoritmo para resolver problemas pequeños, el
costo adicional generado por el grado de ineciencia del algoritmo elegido es
poco signicativo. Por el contrario, cuando el tamaño del problema es grande,
la cantidad de recursos que el algoritmo necesite puede crecer tanto que lo
haga impráctico. Por esta razón, para elegir entre dos algoritmos es necesario
saber cómo se comportan con problemas grandes. En atención a esta necesi-
dad se estudiará la velocidad de crecimiento de la cantidad de recursos que un
algoritmo requiere conforme el tamaño del problema se incrementa, es decir,
se estudiará el comportamiento asintótico de las funciones complejidad. La
Denición 1 introduce el concepto fundamental.

Denición 1 Sean f y g funciones de N a R. Se dice que f domina asintó-


ticamente a g o que g es dominada asintóticamente por f, si ∃k ≥ 0 y m ≥ 0
tales que
|g(n)| ≤ m|f (n)|, ∀n ≥ k

El hecho de que una función domine asintóticamente a otra se interpreta


como se muestra en la Figura 3. En ella se representan las grácas de las
funciones m|f (n)| y |g(n)|, donde k es el valor a partir del cual m|f (n)| es
mayor que |g(n)|, y esta relación de magnitud se conserva conforme n crece.

29
30 CAPÍTULO 2. COMPORTAMIENTO...

En otros términos, podemos decir que si una función domina a otra, su velo-
cidad de crecimiento es mayor o igual. Si esas funciones están representando
el consumo de recursos de la computadora, obviamente la función dominada
crecerá a lo más a la misma velocidad de la otra.

5
m|f (n)|
|g(n)|
4

0
0 1 2 4 5
k
Figura 3: Representación gráca del concepto de dominio asintótico.

Puesto que las funciones complejidad son funciones con dominio N, los
números naturales, y contradominio R, los números reales; los conceptos y
las propiedades de dominio asintótico proporcionan una manera conveniente
de expresarlas y manipularlas.
Con la intención de aclarar la denición, a continuación se presentan
algunos ejemplos.

Ejemplo 6 Sean f (n) = n y g(n) = n3


i) Demostrar que g domina asintóticamente a f. Esto es, demostrar que

∃m ≥ 0, k ≥ 0 tales que |f (n)| ≤ m|g(n)| ∀n ≥ k.

Substituyendo f (n) y g(n) da

|n| ≤ m|n3 |, ∀n ≥ k,
2.1. DOMINIO ASINTÓTICO 31

si se toma m = 1 y k = 0, las desigualdades anteriores se cumplen, por


lo tanto, m y k existen, y en consecuencia g domina asintóticamente a
f.

ii) Demostrar que f no domina asintóticamente a g. Expresado matemá-


ticamente se tiene

¬(∃m ≥ 0, k ≥ 0 tales que |g(n)| ≤ m|f (n)|, ∀n ≥ k)

aplicando la negación se tiene

∀m ≥ 0, k ≥ 0, ∃n ≥ k tal que |g(n)| > m|f (n)|

sustituyendo g y f en cada lado de la desigualdad

|n3 | > m|n|

simplicando

|n2 | > m
n2 > m

n > m para al menos una n ≥ k


si se toma n > máx ( m, k) ambas desigualdades se cumplen,
∴ f no domina asintóticamente a g.

En lo subsecuente se utilizarán las iniciales d.a. como sinónimo de domina


asintóticamente a.

Ejemplo 7 Sea g una función de N a R y f (n) = cg(n) con c > 0 y c ∈ R.


Demostrar que f y g se dominan asintóticamente.

i) Por demostrar, f d.a. g.

∃m ≥ 0, k ≥ 0 tales que |g(n)| ≤ m|cg(n)|, ∀n ≤ k

pero |ab| = |a||b| por lo tanto

|g(n)| ≤ m|c||g(n)|, ∀n ≤ k
32 CAPÍTULO 2. COMPORTAMIENTO...

Como c > 0, entonces

|g(n)| ≤ mc|g(n)|,

tomando m = 1c ,
1
|g(n)| ≤ c|g(n)|∀, n ≤ k
c
y k = 0,
|g(n)| ≤ |g(n)|, ∀n ≤ 0
∴ f d.a. g.

ii) Igualmente se tiene que demostrar que g d.a. f, esto es:


∃m ≥ 0, k ≥ 0 tales que

|cg(n)| ≤ m|g(n)|, ∀n ≥ k

esto es,
c|g(n)| ≤ m|g(n)|, ∀n ≥ k
tomando m = c y k = 0,

c|g(n)| ≤ c|g(n)|, ∀n ≥ 0

∴ g d.a. f.

En el siguiente ejemplo se consideran funciones que no se dominan en el


sentido asintótico.

Ejemplo 8 Sean
½
1 si n es par
f (n) = n y g(n) =
n3 si n es impar

Demostrar que f y g no se dominan asintóticamente.

i) Primero se debe demostrar que f no-d.a. g. Utilizando la negación de


la denición se expresa que

∀m ≥ 0, k ≥ 0; ∃n ≥ k tal que |g(n)| > m|f (n)|.


2.1. DOMINIO ASINTÓTICO 33

Considerando el caso cuando n es impar y substituyendo f y g en la


desigualdad, se tiene
|n3 | > m|n|
n2 > m
1
n > m2

1
Entonces para toda n impar > máx (m 2 , k) las desigualdades se cum-
plen,
∴ f no-d.a. g.
ii) También se debe demostrar que g no-d.a. f. Esto es
∀m ≥ 0, k ≥ 0; ∃n ≥ k tal que |f (n)| > m|g(n)|
Tomando el caso para el cual n es par, se obtiene
|n| > m|1| para alguna n ≥ k
|n| > m

Entonces para toda n > máx(m, k) se satisfacen los requisitos,


∴ g no-d.a. f.
Cuando se hace el análisis teórico para obtener la función complejidad
f (n) que caracterice a un algoritmo, se está obteniendo un modelo de com-
portamiento para la demanda de recursos en función del parámetro n, de tal
forma que si t(n) es la cantidad real del recurso que se consume para una
implantación especíca del algoritmo se tiene que
t(n) ∝ f (n)
t(n) = kf (n)
|t(n)| ≤ k|f (n)|

i.e. f (n) domina asintóticamente a cualquier t(n), dicho de otra manera la


demanda de recursos se va a regir por el modelo de crecimiento que observe
f (n).
A continuación se expone una serie de teoremas que establecen propieda-
des y relaciones basadas en el concepto de dominio asintótico.
34 CAPÍTULO 2. COMPORTAMIENTO...

Teorema 1 Sea M el conjunto de funciones de N a R. La relación binaria


sobre este conjunto denida como

{< f, g > |f, g ∈ M y g domina asintóticamente a f }

y es reexiva y transitiva.

Demostración 1 La demostración la realizamos por partes


i) P.D. La relación denida es reexiva, es decir,

f ∈ M ⇒ f d.a.f.

Si tomamos m = 1 y k = 0 la desigualdad |f (n)| ≤ m|f (n)|, ∀n ≥ k se


cumplen, por lo tanto f d.a. f y
∴ la relación es reexiva.

ii) P.D. La relación es transitiva. Debemos mostrar que si f, g, h ∈ M tal


que f d.a. g y g d.a. h ⇒ f d.a. h.

Sabemos que ∃m1 , m2 , k1 y k2 ≥ 0 tales que:

|g(n)| ≤ m1 |f (n)|, ∀n ≥ k1

|h(n)| ≤ m2 |g(n)|, ∀n ≥ k2

por lo que

|h(n)| ≤ m1 m2 |g(n)|, ∀n ≤ max(k1 , k2 )

con m = m1 m2 y k = max(k1 , k2 ) se satisface la denición, por lo


tanto f d.a. h y
∴ la relación es transitiva.

El siguiente ejemplo ilustra la aplicación de la propiedad de transitividad.


2.1. DOMINIO ASINTÓTICO 35

Ejemplo 9 Sean f (n) = cn, g(n) = n y h(n) = c + n funciones de N a R,


con c > 0 y c ∈ R. Demostrar que f y h se dominan asintóticamente.
Sabemos, por la demostración del ejemplo 7, que f (n) = cn d.a. g(n) = n
y viceversa.

i) Demostrar que f d.a. h. Demostraremos que g d.a. h, es decir, que


∃m ≥ 0 y k ≥ 0 tales que

|h(n)| ≤ |g(n)|, ∀n ≥ k
|c + n| ≤ m|n|, ∀n ≥ k

Si m = 2 y k = c, se cumple que

|c + n| ≤ 2|n|, ∀n ≥ c

de lo anterior se tiene que g d.a. h y f d.a. g


∴ f d.a. h.

ii) Demostrar que h d.a. f.


Demostraremos que h d.a. g, es decir, ∃m ≥ 0, k ≥ 0 tales que |n| ≤
m|n + c|, ∀n ≥ k.
Para m = 1,
|n| ≤ |n + c|, ∀n ≥ 0.

Por lo tanto h d.a. g y sabemos que g d.a. f ⇒ h d.a. f,


∴ f y h se dominan asintóticamente.

El Teorema 2 induce a concebir el conjunto de funciones de N a R como


la unión de distintos subconjuntos ajenos, de tal manera que dos funciones
están en el mismo subconjunto si se dominan asintóticamente. Se sugiere
consultar los conceptos de relación de equivalencia y partición [?].

Teorema 2 Sean f y g funciones en M, la relación binaria f ≡ g ⇔ f y g


se dominan asintóticamente es una relación de equivalencia sobre M.

Demostración 2 P.D. que la relación descrita es simétrica, reexiva y tran-


sitiva.
36 CAPÍTULO 2. COMPORTAMIENTO...

i) La relación es simétrica, es decir, f ≡ g ⇔ g ≡ f.


Puesto que f ≡ g ⇒ f d.a. g y g d.a. f entonces por denición g ≡ f
y
∴ La relación es simétrica.

ii) La relación es reexiva. Se sigue del teorema 1.

iii) La relación es transitiva. Es decir, si f, g, h ∈ M y f ≡ g y g ≡ h ⇒


f ≡ h,
Sabemos que f d.a. g y g d.a. h ⇒ f d.a. h.
Además h d.a. g y g d.a. f ⇒ h d.a. f por lo tanto f ≡ g,
∴ La relación es transitiva.

Dado que toda relación de equivalencia induce una partición, la relación


descrita particiona el conjunto de funciones de N a R en subconjuntos tales
que todos los elementos de un mismo subconjunto se dominan asintótica-
mente entre sí. Se ha demostrado que las funciones n, n + c y cn(c ∈ R) se
encuentran en un mismo subconjunto de la partición.
Los conceptos hasta aquí citados son parte de la descripción de una me-
todología que se desarrollará en esta sección para estimar, aproximadamen-
te, los costos (en espacio y tiempo) de las soluciones que proporcionan los
algoritmos y para comparar algoritmos con base en tales costos. La caracte-
rística principal de esta propuesta es que deben ignorarse factores constantes
y concentrarse en el comportamiento del algoritmo cuando los tamaños del
problema son grandes.

2.2. Notación de orden


Cuando describimos cómo es que el número de operaciones f (n) depende
del tamaño n, lo que nos interesa es encontrar el patrón de crecimiento para
la función complejidad y así caracterizar al algoritmo; una vez hecha esta
caracterización podremos agrupar las funciones de acuerdo al número de
operaciones que realizan. Para tal propósito se da la siguiente denición.

Denición 2 El conjunto de todas las funciones que son asintóticamente


dominadas por una función g es denotado O(g) y se lee orden de g , o bien
O-grande de g .
2.2. NOTACIÓN DE ORDEN 37

Si f ∈ O(g), entonces f se dice ser O(g).


Por ejemplo:

i) f (n) = n, g(n) = 2n3 + 50


f es O(g) y g no es O(f ).

ii) f (n) = n, g(n) = cn


f es O(g) y g es O(f ).
½
1 si n es par
iii) f (n) = n, g(n) =
n3 si n es impar
f no es O(g) y g tampoco es O(f ).
La notación O-grande nos permite ignorar constantes convenientemente,
así siempre podremos escribir O(n3 ) en vez de O(2n3 + 50).
El siguiente teorema establece algunas relaciones importantes entre una
función f y el conjunto O(f ).

Teorema 3 Sean f, g y h ∈ M, entonces:


a) f es O(f ).

b) Si f es O(g) ⇒ cf es O(g) para c ∈ R. Es decir, el conjunto O(g) es


cerrado bajo la multiplicación por una constante.

c) Si f es O(g) y h es O(g) ⇒ (f + h) es O(g) donde (f + h)(n) =


f (n) + g(n), i.e. O(g) es cerrado bajo la suma de funciones.

Demostración 3 Tenemos
a) f es O(f ); dado que f d.a. f ⇒ f es O(f ).

b) f es O(g) ⇒ ∃m1 ≥ 0 y k1 ≥ 0 tales que

|f (n)| ≤ m1 |g(n)|, ∀n ≥ k1 .

P.D. cf es O(g) i.e., ∃m ≥ 0, k ≥ 0, tales que

|cf (n)| ≤ m|g(n)|, n ≥ k

con c ∈ R.
38 CAPÍTULO 2. COMPORTAMIENTO...

Si tomamos m = |c|m1

|cf (n)| ≤ |c|m1 |g(n)|, ∀n ≥ k1


|c||f (n)| ≤ |c|m1 |g(n)|, ∀n ≥ k1
|f (n)| ≤ m1 |g(n)|, ∀n ≥ k1

entonces con m = |c|m1 y k = k1 la desigualdad |f (n)| ≤ m1 |g(n)| se


cumple y en consecuencia cf es O(g).
c) f es O(g) y h es O(g) ⇒ (f + h) es O(g)
Sabemos que existen m1 , m2 , k1 , k2 ≥ 0 tales que:

|f (n)| ≤ m1 |g(n)|, ∀n ≥ k1
|h(n)| ≤ m2 |g(n)|, ∀n ≥ k2

Sumando las desigualdades término a término

|f (n)| + |h(n)| ≤ m1 |g(n)| + m2 |g(n)|, ∀n ≥ max(k1 , k2 )

Por la desigualdad del triángulo

|f (n) + h(n)| ≤ |f (n)| + |h(n)|

Sustituyendo en la suma

|f (n) + h(n)| ≤ (m1 + m2 )|g(n)|, ∀n ≥ max(k1 , k2 )

Con m = m1 + m2 y k = máx(k1 , k2 ) se cumple que

|f (n) + h(n)| ≤ m|g(n)|, ∀n ≥ k

y por ende g d.a. (f + h),


∴ (f + h) es O(g).

El siguiente teorema establece que f es O(g) si y sólo si todas las funciones


dominadas asintóticamente por f son también dominadas asintóticamente
por g.
2.2. NOTACIÓN DE ORDEN 39

Teorema 4 Sean f y g funciones de N a R, f es O(g) ⇔ O(f ) ⊂ O(g).


Demostración 4

(⇒) Si f es O(g) entonces O(f ) ⊂ O(g).


Si f es O(g), por denición g d.a. f. Sea h una función arbitraria tal que
f d.a. h, por transitividad g d.a. h, luego entonces h es O(g). Dado que h
puede ser cualquier elemento de O(f ) podemos concluir que O(f ) ⊂ O(g).
(⇐) Si O(f ) ⊂ O(g) entonces f es O(g).
f es O(f ) (ver teo. 2.3), y como O(f ) ⊂ O(g) ⇒ f es O(g).

Dado que las funciones complejidad están en M, es posible clasicar


los algoritmos según el orden de su función complejidad. Gran parte de
los algoritmos tienen complejidad que cae en uno de los siguientes casos:
O(1) Complejidad constante
O(log n) Complejidad logarítmica
O(n) Complejidad lineal
O(n log n) Complejidad n log n
2
O(n ) Complejidad cuadrática
n
O(c ), c > 1 Complejidad exponencial
O(n!) Complejidad factorial
Y es posible relacionarlos atendiendo a su orden mediante el resultado
del siguiente teorema.

Teorema 5 Sea c ∈ R y c > 1, entonces


O(1) ⊂ O(log n) ⊂ O(n) ⊂ O(n log n) ⊂ O(n2 ) ⊂ O(cn ) ⊂ O(n!)

y las contenciones son propias.

Interpretando este teorema de acuerdo a nuestros intereses podemos decir


que un algoritmo con complejidad factorial consume más recursos que uno
con complejidad exponencial y uno con complejidad exponencial consume
más que uno con complejidad cuadrática, etc. La demostración se deja al
lector.
Con frecuencia encontraremos que al analizar un algoritmo, una sección de
código es de un orden y otra parte es de otro orden, entonces nos preguntamos
¾cuál es el orden del algoritmo?, la respuesta se establece a continuación.
40 CAPÍTULO 2. COMPORTAMIENTO...

Teorema 6 Sean f, g, h1 , h2 funciones de N a R. Si f es O(h1 ) y g es O(h2 )


entonces f + g es O(máx(h1 , h2 )), donde una función es mayor que otra si
la domina asintóticamente.

Demostración 5 Tenemos,
f esO(h1 ), i.e. ∃m1 ≥ 0 y k1 ≥ 0 tales

|f (n)| ≤ m1 |h1 (n)|, ∀n ≥ k1

además g es O(h2 ), es decir ∃m2 ≥ 0 y k2 ≥ 0 tales que

|g(n)| ≤ m2 |h2 (n)|, ∀n ≥ k2

Sumando las ecuaciones obtenemos

|f (n)| + |g(n)| ≤ m1 |h1 (n)| + m2 |h2 (n)|, ∀n ≥ max(k1 , k2 )

Por la desigualdad del triángulo, se tiene

|f (n) + g(n)| ≤ |f (n)| + |g(n)|

Por el teorema 3 y sustituyendo lo anterior

|f (n)| + |g(n)| ≤ (m1 + m2 ) máx(h1 (n), h2 (n)), ∀n ≥ max(k1 , k2 )

Si |h(n)| = máx(h1 (n), h2 (n)) y k = máx(k1 , k2 )

|f (n) + g(n)| ≤ (m1 + m2 )|h(n)|, ∀n ≥ k

∴ (f + g) es O(máx(h1 , h2 ))

Frecuentemente se tiene más de un algoritmo que expresa la solución de un


problema, si en particular se tiene un algoritmo que hace O(n2 ) operaciones
y otro que hace O(n3 ), ¾qué algoritmo se preferiría? Para hacer una elección
razonable debe considerarse el resultado del teorema 7.

Teorema 7 Sean c, d ∈ R y 0 < c < d, entonces O(nc ) ⊂ O(nd ) y la


contención es propia.

Como consecuencia del teorema anterior tenemos un resultado similar al


que se presenta con el álgebra elemental : para determinar el grado de un
polinomio se elige el término de mayor grado, para estimar el orden de una
expresión asintótica se usa el término más representativo.
2.3. INTERPRETACIÓN DE LOS CONCEPTOS ENUNCIADOS 41

Teorema 8 Si P (n) es un polinomio en n de grado k, entonces P (n) es


O(nk ).

Suponga que las siguientes funciones representan funciones complejidad


para tres algoritmos que resuelven un mismo problema:

1
f1 (n) = + 63 es O(1).
n2
1
f2 (n) = rn + + kn log n es O(n log n).
n
f3 (n) = .6n3 + 31n + 468 es O(n3 ).

Los órdenes de las mismas se obtienen fácilmente aplicando una y otra


vez los resultados enunciados y en base a éstos es posible hacer una elección
si seguimos el criterio que se describe en la siguiente sección.

2.3. Interpretación de los conceptos enunciados


Utilizando las propiedades establecidas por los teoremas, podemos re-
presentar el conjunto de funciones de N a R como se muestra en la Figura 4
donde todas las funciones que están en la corona O(f ) dominan asintótica-
mente a f y son dominadas por f. Equivalentemente podemos decir que cada
corona representa un subconjunto de la partición inducida por la relación de
equivalencia denida en el Teorema 5.
Ahora bien, si tenemos 2 algoritmos A1 y A2 que resuelven el mismo
problema y deseamos elegir el que requiera la menor cantidad de recursos,
haremos la elección calculando primero sus complejidades y luego el orden
de las mismas. Pueden ocurrir dos casos:

1. Que uno de los algoritmos, digamos A1 , tenga una función complejidad


f1 (n) que domine a f2 (n), pero que f2 (n) no domine a f1 (n), i.e. se
encuentran en particiones diferentes, expresado de otra manera:

∀m, n0 ≥ 0 ∃n ≥ n0 tal que |f1 (n)| > m|f2 (n)|.

Dado que las funciones complejidad son monótonas crecientes la desigual-


dad se cumple para n ≥ n0 , esto signica que para problemas de tamaño n0
42 CAPÍTULO 2. COMPORTAMIENTO...

n
O(c )

O(n log n)

O(log n)

O(1)

O(n)

c
O(n )

O(n!)

Figura 4: El conjunto de funciones de N a R.


2.4. DOMINIO ASINTÓTICO CON LÍMITES 43

en adelante el algoritmo A1 consume más recursos que A2 , por lo tanto si


queremos optimizar los recursos de la máquina debemos elegir el de la com-
plejidad dominada.
El otro caso posible es que el orden de ambas funciones complejidad sea
el mismo, en ésta situación, las cantidades de los recursos que consumen los
algoritmos crecen de manera similar, y la elección del algoritmo debe basarse
en la comparación de las funciones complejidad más que en su orden.
Comparar los algoritmos en base al comportamiento asintótico de sus
funciones complejidad es una herramienta de elección muy poderosa que, sin
embargo debe ser utilizada con cuidado pues, por ejemplo, si tenemos dos
algoritmos con funciones complejidad

f (n) = cn, es O(n) y


g(n) = an2 , es O(n2 ) con a y c constantes

en base a lo dicho, elegiríamos el algoritmo cuya función complejidad es


f (n), sin embargo, si c = 25 y a = 1, g(n) < f (n) para n < 25, por lo tanto
también es importante tomar en cuenta el tamaño del problema especico.

2.4. Dominio asintótico con límites


Existen varias formas para mostrar que una función es o no, del mismo
orden que otra. A continuación se da una denición que se basa, principal-
f (n)
mente, en el comportamiento del cociente cuando n tiende a ser innito,
g(n)
tal denición implica el uso de límites.

Denición 3 Sean f y g funciones de N a R.


f (n)
a) Si lı́m = c, para alguna constante c 6= 0 entonces f y g son del
n→∞ g(n)
mismo orden.
f (n)
b) Si lı́m = 0, f es O(g), pero g no es O(f ); i.e., f es de orden
n→∞ g(n)
menor que g.
f (n)
c) Si lı́m = ∞, entonces f es de mayor orden que g.
n→∞ g(n)
44 CAPÍTULO 2. COMPORTAMIENTO...

Esta denición resulta clara, si estudiamos el comportamiento de las fun-


ciones f (n) = 1 y g(n) = n, cuando se toma el límite del cociente n1 si
n → ∞. Armamos entonces que si los valores de n son grandes, g(n) crece
más rápido que f (n) y el valor del límite es 0, luego por el caso (b) de la
denición anterior podemos armar que f (n) es O(g(n)).
n2
Ejemplo 10 Sean f (n) = , y g(n) = 300n2 funciones de N a R. Mostrar,
2
usando la denición de límite, que O(f ) = O(g).
n2
2 1
lı́m = ,
n→∞ 300n2 600
De la Denición 3(a) se tiene que son del mismo orden, pues el límite es
distinto de cero (en general dos funciones que dieren por un factor constante
son del mismo orden).
Frecuentemente se usa la regla de L'Hôpital para calcular el límite, pues
encontrar el límite para la derivada en ocasiones es más sencillo que para la
función misma. La regla de L'Hôpital establece que si
lı́m f (n) = ∞ y lı́m g(n) = ∞
n→∞ n→∞

entonces
f (n) f 0 (n)
lı́m = lı́m 0
n→∞ g(n) n→∞ g (n)

donde f 0 y g 0 son las derivadas de f y g respectivamente. Lo anterior también


es válido con n 6∈ ∃ ** revisar <- **y solo si f (n) y g(n) son diferenciables.
Si se desea revisar el concepto de límite y derivada sugerimos ver [?].
Los siguientes ejemplos aplican el resultado anterior.
Ejemplo 11 Mostrar que f (n) = log n es O(n), pero g(n) = n no es O(log n).
Usando limites, la identidad log n = ln n log e y la regla de LHôpital
f (n) (ln n log e)
lı́m = lı́m
n→∞ g(n) n→∞ n
1
n log e
= lı́m
n→∞ 1
1
= lı́m log e
n→∞ n
= 0,
De la Denición 3(b) se tiene que f es O(n) [ y g no es O(f ) ].
2.4. DOMINIO ASINTÓTICO CON LÍMITES 45

Ejemplo 12 Sean f (n) = n2 y g(n) = en las funciones complejidad para


los algoritmos A y B respectivamente, que resuelven un mismo problema.
Determinar cual de los algoritmos es óptimo asintóticamente.
Usando LHôpital da
n2 2n
lı́m n
= lı́m n
n→∞ e n→∞ e
2
= lı́m n
n→∞ e
= 0,
así n2 es O(en ), [ y en no es O(n2 )], por tanto A es óptimo en el sentido
asintótico.
Mostraremos ahora la equivalencia entre las deniciones que se han citado
(vea las Deniciones 1 y 3), para determinar la complejidad de un algoritmo.
Para hacer esto partimos de la expresión
f (n)
lı́m = c, (2.1)
n→∞ g(n)

y con esta obtener una expresión de la forma indicada por la def. 1.


La ecuación 2.1 dice que para toda e > 0, ∃k ≥ 0 tal que ∀n ≥ k, se tiene
la desigualdad ¯ ¯
¯ f (n) ¯
¯ − c ¯<e
¯ g(n) ¯
Para valores grandes de k y con e = 1 obtenemos
¯ ¯
¯ f (n) ¯
¯ − c¯ < 1, ∀n ≥ k
¯ g(n) ¯
¯ ¯
¯ f (n) ¯
¯ ¯
¯ g(n) ¯ − c < 1, ∀n ≥ k
|f (n)| < (c + 1)|g(n)|, ∀n ≥ k
Con m = (c + 1), se tiene
|f (n)| < m|g(n)|, ∀n ≥ k
De esta manera se da una cota superior, en términos de la función g(n),
de los valores de f (n) para valores grandes de n, que es precisamente lo que
dice la Denición 1.
46 CAPÍTULO 2. COMPORTAMIENTO...

Como ya se mencionó la anterior Denición 3 es otra herramienta para


estudiar el comportamiento de la función complejidad asociada a un algorit-
mo, el inconveniente con ella es que implica conceptos matemáticos que no
son elementales como los de la Denición 1.

2.5. Ejercicios
Ejercicio 2.1 Determine cuales de las siguientes funciones dominan asin-
tóticamente a las otras.
½
n, si n es par
i) f1 (n) =
−n, si n es impar
n+1
ii) f2 (n) =
n+1
1
iii) f3 (n) = 3n2 log n + n2

iv) f4 (n) = 4n3

v) f5 (n) = log n2

Ejercicio 2.2 ¾Para que valores de n se satisface la expresión 10n log n >
2n2 ?

Ejercicio 2.3
1. Agrupe las siguientes funciones; dos funciones f y g pertenecen al mis-
mo grupo si y sólo si se dominan asintóticamente.

f1 (n) = n f7 (n) = 2n
f2 (n) = n3 f8 (n) = log n
f3 (n) = n log n f9 (n) = n − n3 + 9n5
f4 (n) = n2 + log n f10 (n) = n2
1
f5 (n) = n 2 + log n f11 (n) = ln n
f6 (n) = 3n! f12 (n) = (log n)2

2. Ordene los grupos formados en el inciso 1 en forma creciente según su


orden.
2.5. EJERCICIOS 47

Ejercicio 2.4 Encuentre una función g(n) (de la forma AB ), tal que f (n)
sea O(g(n)), para cada uno de los siguientes casos:
i) f (n) = C(n, 2)
5n3 + 6
ii) f (n) =
n+2
(n2 + 7)3n
iii) f (n) =
2n
iv) f (n) = n!
Ejercicio 2.5 Muestre que para cualesquiera constantes positivas c0 , c1 , c2 , . . . , ck
la expresión c0 + c1 n + c2 n2 + · · · + ck nk es O(nk ). Pruebe que la expresión
no es O(nk−1 ).
Ejercicio 2.6 Cuando decimos que una expresión f (n) es O(log n), ¾porqué
no es necesario especicar la base del logaritmo?
Ejercicio 2.7 Probar las siguientes aseveraciones, y vericar que cada una
de las contenciones es propia.
i) O(n) ⊂ O(n log n)
ii) O(n log n) ⊂ O(nd ), ∀d > 1
iii) O(cn ) ⊂ O(n!), ∀c > 1
Ejercicio 2.8 Demostrar los siguientes resultados.
i) Sean f, g funciones con dominio N. Entonces
a) f es O(g) y g es O(f ) ⇔ O(f ) = O(g).
b) Si f es O(g) y g es O(h) ⇒ f es O(h).
ii) Sean c, d ∈ R, donde 0 < c < d, entonces O(cn ) ⊂ O(dn ) y la conten-
ción es propia.
Ejercicio 2.9 Pruebe que O(log n) = O(log(n + k)), ∀n, k > 0.
Ejercicio 2.10 Pruebe que R, 0 ≤ i ≤ n, ik es O(nk+1 ), si k ∈ N; si no es
el caso, demuestre lo contrario.
Ejercicio 2.11 Mostrar que para todas constantes a, b > 0 (log2 n)a es O(nb ).
48 CAPÍTULO 2. COMPORTAMIENTO...
Capítulo 3
Análisis de Algoritmos Iterativos

3.1. Introducción
De acuerdo a los conceptos enunciados, debe resultar claro que el análisis
de un algoritmo precisa de un conteo de los recursos consumidos. En un algo-
ritmo sin llamadas recursivas, una vez seleccionados el tamaño del problema
y la operación básica, el análisis se realiza utilizando técnicas tradicionales
de conteo (sucesiones, progresiones aritméticas, sumas, etc.) y el concepto de
orden de una función.
En el ejemplo siguiente se ilustra el análisis temporal de un algoritmo
iterativo de ordenación.

Ejemplo 13 El algoritmo 5, realiza una ordenación ascendente de los va-


lores del arreglo A. Estimar su complejidad temporal.
Tamaño del problema: n, la dimensión del arreglo A.
Operación básica: Asignaciones.

Análisis Temporal
a) Mejor Caso: Ocurre cuando el arreglo está ordenado,

ft (n) = 2(n − 1), es O(n).

b) Peor Caso: Sucede cuando el arreglo está ordenado en forma decrecien-

49
50 CAPÍTULO 3. ANÁLISIS DE ALGORITMOS ITERATIVOS

te,

n
X
ft (n) = n (i + 1)
i=2
Xn
=n i + (n − 1)
i=2
n+1
=n − 1 + (n − 1)
2
n2 + 3n − 4
ft (n) = , es O(n2 ).
2

c) Caso Medio: Para colocar un elemento A[i] en su lugar correcto, nece-


sitamos

i+1
1 1X
(2 + 3 + 4 + · · · + (i + 1)) = j
i i j=2
µ ¶
1 (i + 1)(i + 2)
= −1
i 2
(i + 1)(i + 2) − 2
=
2i
i2 + 3i
=
2i
i+3
= asignaciones.
2
3.1. INTRODUCCIÓN 51

Así, tenemos que

n
X i+3
ft (n) =
i=2
2
1X
= n(i + 3)
2 ·µ ¶ ¸
1 n(n + 1)
= − 1 + 3(n − 1)
2 2
1
= [n(n + 1) − 2 + 6(n − 1)]
4
1 2
= (n + n − 2 + 6n − 6)
4
n2 7n
= + − 2 es O(n2 ).
4 4

proc InserciónDirecta(ini, n, A) ≡
comienza
para i = ini + 1 a n haz
comienza
x ← A[i];
j ← i − 1;
mientras (j ≥ ini) ∧ (A[j] > x) haz
A[j + 1] ← A[j];
j ← j − 1;
zah
A[j + 1] ← x;
termina
zah
termina.
Algoritmo 5: Ordenación por Inserción Directa
52 CAPÍTULO 3. ANÁLISIS DE ALGORITMOS ITERATIVOS

3.2. Análisis por bloques


En el capítulo anterior se desarrolló un modelo matemático para comparar
y evaluar el costo (en espacio y tiempo) de los algoritmos, que considera el
comportamiento de las funciones complejidad cuando el tamaño del problema
es grande. Esta herramienta proporciona un importante medio para hacer
un análisis más dinámico, que se basa principalmente en el conocimiento
del orden de los bloques de instrucciones; aquí cabe señalar que un bloque
de instrucciones puede ser un algoritmo del cual conocemos su complejidad
computacional. Para determinar el orden de un bloque, es necesario tener en
mente el orden de las estructuras de control más usuales, con este objeto a
continuación se presenta el análisis de las estructuras de control.

3.2.1. Secuencia
Sea {I1 , I2 } una secuencia de instrucciones, con complejidades O(f ) y
O(g) respectivamente, entonces el orden de la secuencia es, por el Teorema
6, O(máx(f, g)). Ver Figura 5.

?
I1
O(f )
?
I2
O(g)

Figura 5: Complejidad de la secuenciación

3.2.2. Selección condicional


En el constructor selección condicional se evalúa la condición C, si es
verdadera, se ejecuta el bloque I1 que tiene complejidad O(f ) y si evalúa a
falso se ejecuta I2 que es O(g) como se ve en la Figura 6; tomando en cuenta
ésto, la complejidad de la selección es:
3.2. ANÁLISIS POR BLOQUES 53

i) En el peor caso, esta determinada por el orden de la instrucción que


implica más operaciones, esto es O(máx(f, g)).
ii) Para el mejor caso, se considera la instrucción con menor número de
operaciones, de esta manera la complejidad será O(mı́n(f, g)).
iii) Se toma en cuenta la probabilidad P de que la condición sea verdadera
y entonces la complejidad en el caso promedio es O(P f ) + O((1 − P )g).

?
¡@
¡ @
V ¡ C @ F
@ ¡
? @ ¡ ?

I1 I2
O(f ) O(g)

Figura 6: Complejidad de la selección condicional

3.2.3. Iteración
Si el bloque de complejidad O(f ) se realiza n veces, deducimos que la ite-
ración es O(nf (n)). En muchas ocasiones la complejidad del bloque depende
de algún índice. Si la complejidad es O(f (i)) para 1 ≤ i ≤ n la complejidad
Xn
de la iteración es f (i). Ver gura 7.
i=1
Se hizo ya el análisis de tres algoritmos iterativos, ahora utilizando las
técnicas que hemos descrito analizaremos paso a paso un algoritmo sencillo
que requiere el conocimiento de conceptos elementales de algebra lineal.
Ejemplo 14 Sean A y B dos matrices de n×n con entradas reales. Calcular
la matriz producto C = AB. El algoritmo que se utilizará, es implicado por
la denición del producto de matrices, donde la matriz C tiene la forma :
n
X
cij = aik bkj , i, j ∈ [1, n].
k=1
54 CAPÍTULO 3. ANÁLISIS DE ALGORITMOS ITERATIVOS

-
¡@
¡ @
I1 ¾V ¡ @
O(f ) @
C ¡
@ ¡

F
?

Figura 7: Complejidad de la iteración

proc Matriz-Producto (n, A, B) ≡


comienza
para i = 1 a n haz I1
para j = 1 a n haz I2
C[i, j] ← 0; I3
para k = 1 a n haz I4
C[i, j] ← C[i, j] + A[i, k] ∗ B[k, j]; I5
zah
zah
zah
termina.
Algoritmo 6: Producto de matrices
3.2. ANÁLISIS POR BLOQUES 55

Tamaño del problema: n, la dimensión de las matrices.


Operación básica: la multiplicación entre las entradas de las ma-
trices.
Caso : El algoritmo hace el mismo número de opera-
ciones en todos los casos.

Análisis Temporal
De manera informal podemos decir que para obtener una entrada de cij
se hacen n multiplicaciones, como la matriz C tiene n × n entradas se tiene
que:
ft (n) = n(n2 ) = n3 es O(n3 ).
Nosotros podemos obtener el resultado anterior, si asignamos un orden
(según su comportamiento asintótico) a cada bloque de instrucciones según
las estructuras de control:

a) I5 tiene orden constante, entonces es O(1)

b) Como I5 está dentro del ciclo I4 entonces el orden es O(n × 1) = O(n).

c) El ciclo I2 contiene al conjunto de instrucciones I3 e I4 , si I3 es O(1)


se tiene que la secuencia tiene orden O(n), pero ésta se realiza n veces,
así el orden para este bloque es O(n × n) = O(n2 ).

d) Por último, el ciclo I1 contiene a la instrucción I2 que se realiza n veces


de ésta forma el orden para I1 , y en consecuencia para el Algoritmo 6,
es:
O(n × n2 ) = O(n3 ).1

Análisis Espacial

fe (n) = espacio de (n) + espacio de (A, B, C),


fe (n) = 1 + 3n2 es O(n2 ).
n
X n
X n
X
1 Note que el análisis es identico si hacemos b) 1 = n, c) n = n2 y d) n2 = n3 .
k=1 j=1 i=1
56 CAPÍTULO 3. ANÁLISIS DE ALGORITMOS ITERATIVOS

A continuación se realiza el análisis de un algoritmo numérico omitiendo


los detalles de su funcionamiento para estimar el orden de la función com-
plejidad temporal.

n
X
Ejemplo 15 Sea A un vector de coecientes y sea Pn (z) = A[i]z i un po-
i=0
linomio de grado n, evaluado para un argumento real z. Encontrar la función
complejidad, temporal y espacial, para el algoritmo que evalúa Pn (z).

proc Horner (n, z, A) ≡


comienza
polinomio ← 0; I1
para i = 0 a n haz I2
polinomio ← polinomio ∗ z + A[n − i]; I3
zah
termina.
Algoritmo 7: Método de Horner

Tamaño del problema: n, el grado del polinomio.


Operación básica: la multiplicación en I3 , pues se realiza un nú-
mero de veces del mismo orden al tamaño del
problema (el grado del polinomio).
Caso : El algoritmo hace el mismo número de opera-
ciones en todos los casos.

Análisis Temporal
a) I3 es de orden O(1),

b) I2 contiene a la instrucción de orden O(1), que se ejecuta n veces, de


esta manera el orden de I2 es O(n),

c) El orden de ft (n) para el Algoritmo 7, está dado por la secuencia I1 ,


de orden O(1), e I2 ; tomando máx(O(I1 ), O(I2 )) se tiene:

ft (n) es O(n).
3.3. EJERCICIOS 57

Análisis Espacial

fe (n) = espacio de (n) + espacio de (z, polinomio) + espacio de (A),


fe (n) = 1 + (1 + 1) + (n + 1) = 4 + n, i.e. es O(n).

3.3. Ejercicios
Ejercicio 3.1 Escriba un algoritmo que dado un conjunto de números en-
teros diferentes y ordenados, devuelva el primer valor x tal que A[x] = x.
Calcule la complejidad espacial y temporal del algoritmo.

Ejercicio 3.2 El máximo común divisor de dos enteros positivos n y m,


denotado por M CD(n, m), es el único entero positivo k tal que

1. k divide a m y n,

2. todos los demás enteros que dividen a m y n son menores que k.

El Algoritmo 8 encuentra el M CD de dos enteros positivos, obtenga las


expresiones para su complejidad espacial y temporal.

func Máximo-Común-Divisor (m, n) ≡


comienza
a ← máx(n, m);
b ← mı́n(n, m);
r ← 1; r es el residuo
mientras (r > 0) haz
r ← a mod b;
a ← b;
b ← r;
zah
Máximo-Común-Divisor ← a;
termina.
Algoritmo 8: Máximo común divisor de dos enteros
58 CAPÍTULO 3. ANÁLISIS DE ALGORITMOS ITERATIVOS

Ejercicio 3.3 Diseñe un algoritmo iterativo para los algoritmos de ordena-


ción Quicksort y Mergesort. Encuentre la complejidad temporal y espacial
para cada uno de ellos.

Ejercicio 3.4 El mínimo común múltiplo (MCM) de dos enteros positivos m


y n, es el entero más pequeño que es un múltiplo de ambos números. Diseñe
un algoritmo iterativo para encontrar el MCM de dos enteros. Determine el
orden de su complejidad temporal.
Capítulo 4
Análisis de Algoritmos Recursivos

4.1. Introducción
La recursividad es un fenómeno que se presenta en muchos problemas de
forma natural, delegando la solución de un problema en la solución de otro
más pequeño. Los siguientes son dos ejemplos clásicos:

El costo de mover n piezas de equipo pesado depende del costo


por el movimiento de n − 1 piezas más un costo adicional por el
costo de mover la enésima pieza

El tiempo requerido por cierto algoritmo para ordenar n elemen-


tos depende del tiempo que a éste le toma ordenar n − 1 elemen-
tos, más el tiempo adicional requerido para colocar al enésimo
elemento en su lugar correcto

Como se observa existen situaciones en las cuales las cantidades que que-
remos estudiar pueden ser modeladas matemáticamente por funciones de-
nidas en forma recursiva. Una ecuación que expresa una función de este tipo
recibe el nombre de ecuación recurrente, si además están dados uno o más
valores de n especícos, lo que se tiene es un sistema recurrente.
Los algoritmos, al igual que las funciones, también pueden ser denidos
recursivamente, de hecho tienen mucho en común pues ambos aceptan un
conjunto de entradas, generan salidas correspondientes a las entradas y tie-
nen una regla o conjunto de reglas para obtener las salidas a partir de las
entradas. Así que muchos conceptos que se aplican a funciones se aplicarán,
frecuentemente, también a los algoritmos.

59
60 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

Cuando se quiere calcular la demanda de recursos de un algoritmo denido


recursivamente, la función complejidad que resulta no está denida sólo en
términos del tamaño del problema y algunas constantes, sino en términos de
la función complejidad misma. Además no es una sola ecuación, dado que
existen otras (al menos una) que determinan la cantidad de recursos para
los casos base de los algoritmos recursivos. Dada esta situación, para poder
obtener el comportamiento del algoritmo, es necesario resolver el sistema
recurrente obtenido. En esta sección se muestra el principio de análisis de
los algoritmos recursivos y la obtención de las funciones complejidad a partir
de la solución de los sistemas recurrentes mediante la técnica expansión de
recurrencias.

4.2. Sistemas recurrentes


Consideremos el Algoritmo recursivo 9 que calcula el factorial de un nú-
mero entero n,

func Factorial (n) ≡


comienza
si (n = 0) entonces
Factorial ← 1;
otro
Factorial ← n × F actorial(n − 1);
is
termina.
Algoritmo 9: Factorial de un número

Para encontrar la complejidad temporal debemos elegir una operación tal


que el orden del número de veces que se realiza sea igual al orden del total
de operaciones que hace el algoritmo, puesto que la función es recursiva, la
parte más costosa es la recursión y tenemos que elegir una operación que
se efectúe en todas las llamadas que hace el algoritmo o en un número de
ellas que sea del mismo orden que el orden del número total de llamadas.
De las operaciones que realiza el algoritmo podemos contar multiplicaciones,
veamos que dicha operación cumple con las condiciones estipuladas.
El algoritmo para resolver un problema de tamaño n, hace una multipli-
cación y resuelve un problema de tamaño n − 1 hace una multiplicación y
4.2. SISTEMAS RECURRENTES 61

resuelve un problema de tamaño n − 2 . . . y resuelve un problema de tamaño


1 hace una multiplicación y resuelve un problema de tamaño 0, para el que
ya no ejecuta dicha operación.
Puesto que la función tiene 2 partes, el caso base y la parte recursiva, la
función complejidad se puede expresar como un sistema de 2 ecuaciones:

f (0) = 0, para el caso base


f (n) = f (n − 1) + 1, para el caso general

Nuestras ecuaciones forman lo que se conoce como un sistema recurrente.


En un sistema de esta naturaleza se distinguen 2 partes:

i) La ecuación recurrente, que da el valor de una función en un punto n


en términos de su valor en otros puntos m con n > m. En nuestro caso,
la ecuación
f (n) = f (n − 1) + 1
da el valor de f (n) en términos del valor de f (n − 1).

ii) Un conjunto de valores de frontera o condiciones iniciales (los valores


de una función en puntos especícos) que determinan, junto con la
ecuación recurrente, completamente a una función. En nuestro ejemplo

f (0) = 0.

Si se tiene una condición inicial, tal como f (0) = 0, entonces podemos


calcular f (n), usando la ecuación recurrente, para otros valores de n. Así

f (1) = f (0) + 1 = 1
f (2) = f (1) + 1 = 2
f (3) = f (2) + 1 = 3
f (4) = f (3) + 1 = 4

Una solución a un sistema recurrente es una función que satisface la ecua-


ción recurrente y las condiciones iniciales.
La solución a nuestro sistema es f (n) = n.
Comprobémoslo:
62 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

Si n = 0 entonces f (0) =0 satisface la condición ini-


cial, y
Si n > 0 entonces f (n) = f (n − 1) + 1
= (n − 1) + 1
=n satisface la ecuación recu-
rrente, por lo tanto es una
solución al sistema.
Para encontrar la solución a un sistema recurrente existe toda una teoría
que expone diversos métodos. En el análisis de algortimos recursivos, los
sistemas recurrentes que resultan son de baja complejidad y en la mayoría
de los casos basta la aplicación de una técnica muy simple para resolverlos.
En esta sección se expondrá el método de expansión de recurrencias que será
utilizado siempre que sea necesario resolver un sistema de éste tipo.

4.2.1. Expansión de recurrencias


Este método de solución, consiste en utilizar la recurrencia misma para
substituir f (n) por su valor en términos de f (m), donde m < n, iterativa-
mente hasta encontrar una condición de frontera f (r) que tendrá un valor
constante. En la ecuación recurrente se ponen todos los terminos con f en el
lado izquierdo y se realiza la expansión. Al sumar todas las ecuaciones ob-
tendremos una expresión para f (n) en términos de n y algunas constantes.
A ésta expresión nal se le denomina forma cerrada para f (n).

Ejemplo 16 Utilicemos el sistema obtenido para la función factorial:

f (n) = f (n − 1) + 1

ponemos los terminos de f en el lado izquierdo

f (n) − f (n − 1) = 1
4.2. SISTEMAS RECURRENTES 63

expandemos la recurrencia

f (n) − f (n − 1) = 1
f (n − 1) − f (n − 2) = 1
f (n − 2) − f (n − 3) = 1
..
.
f (2) − f (1) = 1
f (1) − f (0) = 1
f (0) = 0

sumando todas las ecuaciones

f (n) = 1| + 1 +{z· · · + 1}
n veces
∴ f (n) = n

Ejemplo 17 El número de permutaciones de n objetos se puede expresar


usando el siguiente sistema recurrente:
P (0) = 1, pues sólo hay una permutación de 0 elemen-
tos, y
P (n) = nP (n − 1), porque podemos elegir un elemento de los n
posibles y después cualquier secuencia que sea
una permutación de los n − 1 elementos res-
tantes.
Solución:

P (n) − nP (n − 1) = 0
nP (n − 1) − n(n − 1)P (n − 2) = 0
..
.
n(n − 1)(n − 2) . . . 2 × P (1) − n(n − 1)(n − 2) · · · × 1 × P (0) = 0
P (0) = 1
64 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

sustituyendo P (0) y sumando ecuaciones

P (n) − n(n − 1)(n − 2) · · · × 2 × 1 = 0


P (n) = n!

Los sistemas recurrentes obtenidos cuando se intenta calcular la comple-


jidad de los algoritmos recursivos con frecuencia se ajustan a alguna forma
estándar y si conocemos la solución genérica para ésa forma, nos ahorraremos
el tener que hallar la solución cada vez que se presente un caso así. El sistema
de nuestro ejemplo inicial es de la siguiente forma:

f (1) = a,
f (n) = cf (n − 1) + b

donde a, b y c son enteros no negativos y f : N → R.


Encontraremos la solución genérica aplicando el método expuesto.

f (n) − cf (n − 1) = b
cf (n − 1) − c2 f (n − 2) = cb
c2 f (n − 2) − c3 f (n − 3) = c2 b
..
.
c f (2) − c f (1) = cn−2 b
n−2 n−1

cn−1 f (1) = cn−1 a

sumando todas las ecuaciones tenemos

f (n) = cn−1 a + b + cb + c2 b + · · · + cn−2 b


n−2
X
= cn−1 a + b ci
i=0
4.2. SISTEMAS RECURRENTES 65

sabemos que
n
X
ci = n + 1, si c = 1
i=0
n
X cn+1 − 1
ci = , si c 6= 1
i=0
c−1

entonces si c = 1

f (n) = a + b(n − 1)

y si c 6= 1

b(cn−1 − 1)
f (n) = cn−1 +
c−1
A continuación presentamos un teorema basado en lo anterior.

Teorema 9 Sean a, b y c enteros no negativos y f : N → R. tal que


f (1) = a
f (n) = cf (n − 1) + b

entonces
i) Si c = 1 entonces f (n) = a + b(n − 1)
b(cn−1 − 1)
ii) Si c > 1 entonces f (n) = cn−1 a + .
c−1
A continuación ilustramos el uso del teorema anterior para resolver proble-
mas donde el sistema recurrente resultante se ajusta a la forma especicada
por el teorema.

Ejemplo 18 Encontrar el mayor número de hojas que puede tener un árbol


de altura h, donde cada nodo tiene grado k ≥ 2.
f (1) = k
f (h) = kf (h − 1)
66 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

del teorema anterior, a = k, b = 0 y c = k por lo tanto


f (h) = kk h−1 = k h .
Ejemplo 19 Encontrar la función complejidad temporal para el algoritmo
que resuelve el problema de las torres de Hanoi, que consiste en lo siguiente:
se tienen n discos apilados de mayor a menor, en la torre fuente, y se tienen
que mover a la torre destino utilizando una torre auxiliar; los discos se tienen
que mover uno a uno y apilarlos de tal forma que nunca quede un disco bajo
otro mayor que él. La solución recursiva al problema se ilustra en el Algoritmo
10.

proc Hanoi (n, ini, med, f in) ≡


comienza
si (n = 1) entonces escribe (ini, →, f in);
otro
comienza
Hanoi (n − 1, ini, f in, med);
escribe (ini, →, f in);
Hanoi (n − 1, med, ini, f in);
termina
is
termina.
Algoritmo 10: Torres de Hanoi

Encontraremos ahora la función complejidad temporal del Algoritmo 10.


Tamaño del problema: n, número de discos a mover.
Operación básica: movimientos de disco, pues se hace en todas
las llamadas recursivas. Un movimiento de dis-
co es una escritura de ini, →, n.
Caso : El algoritmo hace el mismo número de opera-
ciones en todos los casos.

Análisis Temporal
La relación de recurrencia esta dada por
ft (1) = 1
ft (n) = 2f (n − 1) + 1
4.2. SISTEMAS RECURRENTES 67

Para aplicar el teorema anterior hacemos a = 1, b = 1, c = 2 :

n−1 2n−1 − 1
ft (n) = 2 + = 2n−1 + (2n−1 − 1) = 2n − 1 es O(2n ).
1

Hasta ahora sólo hemos analizado algoritmos que utilizaban memoria es-
tática, los Algoritmos 10 y 11, por ser recursivos, requieren espacio para al-
macenar los registros de activación de las diferentes llamadas, de tal manera
que mientras más anidación de ambientes exista, mayor cantidad de memo-
ria consumirán. Los siguientes ejemplos ilustran el cálculo de la complejidad
espacial dinámica para algoritmos recursivos.

Ejemplo 20 Encontrar la complejidad espacial dinámica para el algoritmo


que resuelve el problema de las torres de Hanoi.

Análisis Espacial
El algoritmo recibe 4 parámetros y necesita una localidad de memoria
adicional para almacenar la dirección de retorno. Para resolver un problema
de tamaño n > 1, el algoritmo debe almacenar los valores de las variables y su
dirección de retorno, por lo tanto necesitará 5 celdas de memoria dinámica,
más las que consuma para resolver un problema de tamaño n−1. Observamos
que hace 2 llamadas recursivas con argumento n−1, pero cuando el primero de
ellos termina, la memoria consumida se libera y es reutilizada por la segunda
llamada, entonces podemos expresar la función complejidad espacial por el
siguiente sistema:
fe (1) = 5, pues para n = 1 no se hacen llamadas recursivas
fe (n) = fe (n − 1) + 5
Para aplicar el Teorema 9, hacemos a = 5, b = 5, c = 1 y obtenemos

fe (n) = 5 + 5(n − 1) = 5n, que es O(n).

Ejemplo 21 Encontrar la complejidad, espacial y temporal, para el algorit-


mo que evalúa la potencia de un número baseexp (Algoritmo 11).

Tamaño del problema: n, el exponente.


68 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

func Potencia(base, exp) ≡


comienza
si (exp = 1) entonces
P otencia ← base
otro
P otencia ← base × Potencia(base, exp − 1);
is
termina.
Algoritmo 11: Potencia de un número

Análisis Espacial

fe (1) = espacio(exp) + espacio(base) + espacio(dirección de retorno)


fe (1) = 3
fe (n) = fe (n − 1) + 3

Para aplicar el Teorema 9, hacemos a = 3, b = 3 y c = 1 :

fe (n) = 3(n − 1) + 3 = 3n que es O(n)

El sistema recurrente de nuestro siguiente teorema, aparece con mucha


frecuencia en algoritmos que dividen el problema inicial en a problemas de
tamaño nb efectuando a lo más c operaciones básicas en cada llamada al
algoritmo. El siguiente teorema describe la forma cerrada del sistema para
los diferentes valores que puedan tomar las constantes involucradas.

Teorema 10 Sean a, b, c enteros tales que a ≥ 1, b > 1 y c > 0, y sea f una


función arbitraria de N a R cuyos valores siguen el sistema:

f (1) = c
n
f (n) = af ( ) + c
b
para argumentos de la forma n = bk , donde k ∈ N y k > 0, se tienen las
siguientes soluciones al sistema
i) Si a = 1 entonces f (n) = c(logb n + 1)
4.2. SISTEMAS RECURRENTES 69

c(anlogb a − 1)
ii) Si a 6= 1 entonces f (n) =
a−1
Demostración 6 Sea el sistema
f (1) = c
n
f (n) = af ( ) + c
b

con n = bk y k ≥ 1 se tiene

f (bk ) − af (bk−1 ) = c
af (bk−1 ) − a2 f (bk−2 ) = ac
..
.
ak−1 f (b1 ) − ak f (b0 ) = ak−1 c

sumando las ecuaciones

f (bk ) = c + ac + · · · + ak−1 c + ak c
k
X
=c ai
i=0

Tenemos dos casos

i) Si a = 1, con n = bk
f (bk ) = c(k + 1)
y notamos que logb n = logb bk = k, luego entonces

f (n) = c(logb n + 1).

ii) En el segundo caso a 6= 1. Tendremos

c(ak+1 − 1)
f (bk ) =
a−1
c
= (aak − 1)
a−1
70 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

pero
logb n
alogb n = blogb a
= blogb n logb a
= blogb a logb n
logb a
= blogb n
= nlogb a

entonces
c
f (n) = (aalogb n − 1)
a−1
c
f (n) = (anlogb a − 1)
a−1
La forma cerrada para el sistema recurrente que enuncia el teorema sólo
es válida para argumentos de la forma bk , con b > 1 y k > 0. ¾Qué orden
podemos asociarle a la función en su forma cerrada?, dado que una función es
de orden g si g la domina asintóticamente y la denición de d.a. implica que
el dominio se dé en todos los naturales a partir de algún valor k especíco, y
el teorema no asegura el dominio en un rango continuo de los naturales, no
es posible asociarle un orden a la función. Sin embargo, podemos enunciar
la siguiente denición que nos caracteriza el comportamiento de una función
en un subconjunto de los naturales.
Denición 4 Sean f y g funciones de N a R y S un subconjunto innito de
N. Se dice que f es O(g) en S si existen m ≥ 0, k ≥ 0 tales que
|f (n)| ≤ m|g(n)|, ∀n ∈ S y n ≥ k.
En el marco de la denición anterior diremos que si f cumple con las
restricciones planteadas en el Teorema 10, entonces:
i) Si a = 1, f es O(log n) en S
ii) Si a 6= 1, f es O(nlogb a ) en S, donde S = {bk |b, k ∈ N ∧ b > 1, k > 0.}
Ahora bien, cuando el sistema recurrente que resulte de un algoritmo no
tenga exactamente la forma del teorema, pero se puede escribir en esa forma
cambiando la igualdad por una desigualdad, se puede asegurar un orden
para la función, pero no una expresión exacta para el sistema recurrente,
esta aseveración está respaldada por el siguiente teorema.
4.2. SISTEMAS RECURRENTES 71

Teorema 11 Sean a, b, c enteros tales que a, b y c > 0 y f una función de


N a R tal que

f (1) ≤ c
n
f (n) ≤ af ( ) + c, para n = bk donde k > 0,
b

Con S{bk |b, k ∈ N ∧ b > 1, k > 0.} se tienen las soluciones:

1. si a = 1 entonces f es O(log n) en S

2. si a 6= 1 entonces f es O(nlogb a ) en S

Demostración 7 Sea g una solución al sistema recurrente que plantea el


teorema (cuando se tiene la igualdad en las dos condiciones), así g tiene la
forma

g(1) = c
n
g(n) = ag( ) + c, para n = bk y k > 0
b
Por el Teorema 10 es fácil ver que la función g es O(log n) en S si a = 1, y
O(nlogb a ) en S si a 6= 1.
Así tenemos que la función g es una cota para la función f en S, es decir

f (n) ≤ g(n), si n ∈ S.

Por la Denición 4 la función f es O(log n) en S cuando a = 1 y f es


O(nlogb a ) en S si a 6= 1.

El siguiente ejemplo ilustra la aplicación del teorema anterior.

Ejemplo 22 El Algoritmo 12 cuenta el número de nodos en un árbol binario


con todos los niveles llenos. Realice su análisis temporal.

Tamaño del problema: n, el número de nodos en el árbol.


Operación básica: las asignaciones.
Casos: el algoritmo no tiene casos.
72 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

func Cuenta(raiz) ≡
comienza
si (tipo (raiz) = hoja)) entonces
Cuenta ← 1
otro
Cuenta ← 1 + Cuenta(hijoizq(raiz))
+Cuenta(hijoder (raiz));
is
termina.
Algoritmo 12: Cuenta el número de nodos en un árbol binario

Análisis Temporal

ft (1) = 1, caso base


n−1
ft (n) = 2f ( ) + 1, caso general
2

n n−1
Como ft (n) es monótona creciente y 2
> 2
entonces ft ( n2 ) > ft ( n−1
2
),
de esta forma se tiene el sistema

ft (1) ≤ 1
n
ft (n) ≤ 2f ( ) + 1
2

Por el Teorema 11(2), con a = 2, b = 2 y c = 1, podemos concluir que ft


es O(n) en S, donde S = {2k |k ∈ N, k > 0}.
En el siguiente ejemplo, se analiza un algoritmo para encontrar el elemento
máximo en un arreglo utilizando recursividad; en él se aplica también el
resultado del Teorema 11.

Ejemplo 23 Encontrar la complejidad del Algoritmo 13, el cual encuentra


el elemento máximo en un arreglo A con n elementos.
4.2. SISTEMAS RECURRENTES 73

proc Máximo (ini, f in, max) ≡


comienza
si (ini = f in) entonces
max ← A[ini]
otro
comienza
mitad ← bini+f
2
inc
;
Máximo (ini, mitad, max);
Máximo (mitad + 1, f in, max_aux );
si (max_aux > max)
entonces max ← max_aux ;
is
termina
is
termina.
Algoritmo 13: Búsqueda del máximo en un arreglo de elementos

Tamaño del problema: n, el número de elementos en el arreglo.


Operación básica: las comparaciones entre elementos, pues el nú-
mero total de ellas y el número total de opera-
ciones son del mismo orden (véase el Ejemplo
4.11).
Casos: el algoritmo no tiene casos.

Análisis Temporal

ft (1) = 0
n
ft (n) = 2ft ( ) + 1
2

Este sistema recurrente cumple con las desigualdades,

ft (1) ≤ 1
n
ft (n) ≤ 2ft ( ) + 1
2
74 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

Aplicando el Teorema 11, con a = 2, b = 2 y c = 1 obtenemos

ft (n) es O(nlogb a ) en S

Como log2 2 = 1, entonces

ft (n) es O(n) en S, donde S = {2k |k ∈ N, k > 0}

Análisis Espacial
Como es claro la complejidad espacial es de dos tipos:
1. Estática
fee (n) = espacio(A)

entonces:

fee (n) = n

2. Dinámica
Se tiene el sistema recurrente:
fed (1) = espacio(ini) + espacio(f in) + espacio(mitad) + espacio(max)
+ espacio(max_aux) + espacio(dirección de retorno)
fed (1) = 6
n
fed (n) = fed ( ) + 6
2

Aplicando el Teorema 10, con a = 1, b = 2 y c = 6 obtenemos


fed (n) = 6(log2 n+1) que es O(log n) en S, donde S = {2k |k ∈ N, k > 0}

De lo anterior se tiene que la complejidad espacial es, por el Teorema 6,


máx(fee (n), fed (n)), por lo tanto fe (n) es O(n).
Al aplicar los resultados de los Teoremas 10 y 11, sólo podemos garantizar
la convergencia para el subconjunto S, en el siguiente teorema se establece
que, si la función cumple con ciertas restricciones, el comportamiento del
algoritmo es el mismo en todos los naturales.
4.2. SISTEMAS RECURRENTES 75

Teorema 12 Sea f : N → R+ una función monótona creciente tal que f es


O(g) en S, donde S = {bk |b, k ∈ N, k > 0, b > 1}, entonces
1. si g es O(log n) entonces f es O(log n) para toda n
2. si g es O(n log n) entonces f es O(n log n) para toda n
3. si g es O(nd ) entonces f es O(nd ), d ≥ 0 para toda n
Demostración 8 Para el primer inciso tenemos
1. Hipótesis: f es O(g) en S, f : N → R+ , f monótona creciente y g es
O(log n).
Dado que f es O(g) en S, y g es O(log n) por transitividad f es O(log n)
en S, ahora bien, si mostramos que para todo j fuera de S, |f (j)| ≤
m| log j|, para algún m ≥ 0 y k ≥ 0 especícos, esto implicaría que f
es O(log n) para toda n.

Sea j tal que: bi < j < bi+1 , como f es monótona creciente,

f (bi ) < f (j) < f (bi+1 ) y dado que f es O(log n) en S,


|f (bi )| ≤ m1 | log bi+1 |, ∀n ≥ k1

como f : N → R+ y la función logaritmo es creciente

f (bi+1 ) ≤ m1 log bi+1


f (bi+1 ) ≤ m1 log bbi
f (bi+1 ) ≤ m1 (log b + log bi )
log b
f (bi+1 ) ≤ m1 log bi (1 + )
log bi
log b
como log bi
≤ 1 podemos sustituirlo sin alterar la desigualdad

f (bi+1 ) ≤ m1 log bi (1 + 1)
f (bi+1 ) ≤ 2m − 1 log bi
⇒ f (j) < f (bi+1 ) ≤ 2m1 log bi ≤ 2m1 log j
⇒ f (j) ≤ 2m1 log j
76 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

como j es cualquier número que no esté en S, con m = 2m1 y k = k1

|f (j)| ≤ m| log j|, ∀j ≥ k y j fuera de S ⇒ f es O(log n).

las demostraciones para los incisos b) y c) siguen la misma metodología y se


dejan como ejercicios al lector.

Para resolver un sistema recurrente, a menudo es necesario calcular la


ecuación a la que converge alguna sucesión. Cuando surgen sumatorias para
las que no se conoce la solución exacta, se suele acotar por arriba mediante el
uso del cálculo integral, el apartado siguiente describe e ilustra brevemente
esta técnica.

4.3. Aproximando por integrales


Recordemos que nuestro objetivo es conocer la cantidad de recursos que
un algoritmo consume para determinar, entre otras cosas, si es conveniente
codicarlo.
Cuando al expandir una recurrencia nos resulte en una sumatoria que no
sepamos a que converge, el cálculo integral constituye una herramienta útil,
pues nos dá la posibilidad de obtener una cota superior para la sumatoria.
Describiremos el procedimiento mediante un ejemplo, suponga que tiene la
sumatoria
Xn
1
i=1
i

para la que no se conoce una fórmula cerrada, la escribimos como

n
X Z n
1 1
= ≤1+ dx = 1 + (ln x|x=n − ln x|x=2 )
i=1
i 2 x
≤ 1 + ln n − ln 2

por lo que podemos utilizar este valor para acotar la sumatoria sin perder de
vista que la igualdad es ahora una desigualdad.
4.4. ELECCIÓN DE LA OPERACIÓN BÁSICA 77

4.4. Elección de la operación básica


La elección de la operación básica para un algoritmo recursivo se debe
justicar dando una demostración constructiva de que la cantidad de veces
que se ejecuta esta operación observa un modelo de crecimiento similar al
del total de operaciones conforme el tamaño del problema crece; es decir se
debe demostrar que el orden del total de operaciones que hace el algoritmo
es igual al orden del total de operaciones básicas.
Podemos sintetizar el procedimiento en los siguientes pasos:

1. Se visualiza el árbol de llamadas recursivas del algoritmo, que obvia-


mente estará en función del tamaño del problema, por lo tanto, pode-
mos decir que hay un número de nodos g(n).

2. Se obtiene una cota máxima para la cantidad de operaciones que el


algoritmo hace en cada llamada recursiva.

3. Con la funcion g(n) y la cota obtenida en el paso anterior podemos


obtener el orden del total de operaciones en términos de esos 2 datos.

4. Visualizar la cantidad de llamadas en las que se hacen operaciones


básicas y encontrar una cota para la cantidad de operaciones básicas
que se efectúa en cada nodo. Expresarlo en términos de la g(n) obtenida
en el paso 1, y establecer el orden del total de operaciones básicas.

5. Si el orden del total de operaciones es igual al orden de operaciones


básicas, entonces la operación elegida es válida, en caso contrario, se
debe intentar con otra.

El siguiente par de ejemplos ilustran la elección de la operación básica


justicada mediante el proceso anterior.

Ejemplo 24 El Algoritmo 14 encuentra el elemento máximo y el mínimo de


un arreglo A con n enteros, donde n es de la forma n = 2k , k ≥ 0.

En este algoritmo se tienen las siguientes operaciones:

a) comparaciones entre índices

b) asignaciones a max y min


78 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

proc Maxmin(ini, f in, max, min) ≡


comienza
si (ini = f in) entonces
max ← A[ini];
min ← A[ini];
otro
comienza
mitad ← [(ini + f in)/2];
Maxmin(ini, mitad, max, min);
Maxmin(mitad + 1, f in, max1 , min1 );
si max1 > max entonces max ← max1 ; is
si min1 < min entonces min ← min1 ; is
termina
termina.
Algoritmo 14: Búsqueda del máximo y el mínimo

c) comparaciones entre los máximos y los mínimos

d) operaciones involucradas en la obtención del índice mitad


las operaciones a y d las dejamos de lado por estar asociadas a índices,
las operaciones b y c son buenos candidatos para hacer el análisis. Conside-
raremos la operación c.
Demostramos que O(número de comparaciones) = O(total de operaciones).
Dado que el arreglo es siempre de la forma 2k , el árbol de llamadas recur-
sivas se puede representar por el árbol de la Figura 8, en donde cada nodo
está etiquetado con el tamaño de problema que recibe.
En el árbol hay
g(n) nodos internos, y
+
g(n) + 1 nodos externos por lo que hay
2g(n) + 1 nodos en total
en cada llamada recursiva se hacen a lo más

c = máx(Op. en las hojas, Op. en los nodos internos)

lo que implica entonces que se hacen en total

c(2g(n) + 1) operaciones, que es O(g(n)).


4.4. ELECCIÓN DE LA OPERACIÓN BÁSICA 79

n n
2 2

n n n n
4 4 4 4

. . . . . . . .

Figura 8: Árbol de llamadas recursivas para un problema de tamaño n

La operación básica (o.b. en lo subsecuente) se efectúa sólo en los nodos


con tamaño de problema mayor que 1, es decir, en los nodos internos, po-
demos decir entonces que si tenemos g(n) nodos internos y en cada nodo se
hacen 2 o.b., entonces

2(g(n) + 1) es O(g(n)) o.b.

dado que O(número de o.b.) = O(número total de operaciónes), las compa-


raciones pueden ser elegidas como o.b.

Ejemplo 25 La elección de la operación básica para el algoritmo del factorial


se justica mediante el siguiente razonamiento.
Si llamamos  g(n) al número de llamadas recursivas que hace el algo-
ritmo para un problema de tamaño n tenemos que hacer en total c × g(n)
operaciones, donde la constante c representa las operaciones hechas en cada
llamada, por lo tanto el número total de llamadas recursivas y el número total
de operaciones son O(g(n)). En g(n) − 1 llamadas se hacen multiplicaciones,
por lo tanto el número de multiplicaciones es O(g(n)), y se puede considerar
la operación para el análisis dado que: O(total de operaciones) = O(total de
multiplicaciones).

Ejemplo 26 Muestre que la elección de la operación básica para el Algoritmo


13 es correcta.
80 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

Para ver que esto es cierto, considérese el árbol de llamadas recursivas


de la Figura 8, donde g(n) representa el total de nodos, lo que se obtiene
sumando los nodos internos h(n) más el total de nodos externos h(n) + 1. Si
llamamos g(n) al número de llamadas recursivas, que dependen del tamaño
del problema n, entonces se tiene

O(llamadas recursivas) = O(g(n)),

luego entonces se hacen c × g(n) operaciones, c es el número de operaciones


hechas en cada llamada; hasta aquí podemos armar que el número total de
operaciones es O(g(n)).
Observe que en cada nodo interno del árbol se hace una comparación, así

total de operaciones básicas = 1 × h(n)


g(n) − 1
=
2
es O(g(n))

de lo anterior conrmamos que la operación se puede usar para el análisis,


pues

O(total de operaciones) = O(total de operaciones básicas)

4.5. Ejercicios
Ejercicio 4.1 Resuelva los siguientes sistemas recurrentes donde f (1) = 1
y para n ≥ 2
n
f (n) = 8f ( ) + n2 (4.1)
2
n
f (n) = 2f ( ) + log n (4.2)
2
Ejercicio 4.2 Muestre que el número de funciones booleanas de n variables
está dado por la recurrencia:

f (1) = 4
f (n) = (f (n − 1))2
4.5. EJERCICIOS 81

Ejercicio 4.3 Resuélvanse las siguientes recurrencias. De una solución exac-


ta.
f (1) = 2 (4.3)
f (n) = 2f (n − 1) + 1
f (1) = 1 (4.4)
n
f (n) = f (n − 1) +
2
Ejercicio 4.4 Muestre que la solución al sistema recurrente
f (1) = c
n
f (n) = af ( ) + cn, para n = bk ∧ k > 0,
b
es:
1. si a > b entonces f es O(na)
2. si a = b entonces f es O(n log n)
3. si a < b entonces f es O(n)
donde a, b y c son enteros tales que a ≥ 1, b > 1 y c > 0, y f es una función
de N a R.

Ejercicio 4.5 Elabore algoritmos recursivos para resolver los siguientes pro-
blemas y calcule su complejidad temporal y espacial.
1. Encontrar una moneda falsa en un arreglo de n monedas con n = 2k
(el arreglo contiene los pesos de las monedas y la falsa pesa menos que
las otras).
2. Evaluar una expresión aritmética en notación inja que se encuentra
en un arreglo que contiene prioridades de aplicación para los elementos
de la expresión (la prioridad de un operando es innita).

Ejercicio 4.6 Sea n una potencia de 2, y sean A y B dos matrices n ×


n. El método de Strassen para obtener el producto C = AB, consiste en
particionar A y B en matrices componentes de n2 × n2 y multiplicarlas usando
las fórmulas mencionadas en el Ejercicio 1.1. Esto es, las fórmulas son usa-
das recursivamente para multiplicar las matrices componentes reduciendo el
problema a multiplicar matrices de dimensión 2X2.
82 CAPÍTULO 4. ANÁLISIS DE ALGORITMOS RECURSIVOS

1. Implemente un algoritmo basado en este método y muestre que el nú-


mero de operaciones básicas que hace es, aproximadamente, n2 .

2. Discuta la importancia del algoritmo para resolver otro tipo de proble-


mas que involucran matrices n × n (obtener la matriz inversa, solución
de sistemas de ecuaciones lineales simultáneas, etc.)

Ejercicio 4.7 La altura de un árbol es el nivel máximo del árbol (o la distan-


cia máxima entre la raíz y un nodo externo). Diseñe un algoritmo recursivo
para calcular la altura de un árbol binario. ¾Cuál es la complejidad temporal
de su algoritmo?

Ejercicio 4.8 La longitud de camino de un árbol es la suma de los nive-


les de todos los nodos en el árbol. Si en la suma se consideran únicamente
los niveles de los nodos externos, a ésta se llama la longitud de camino ex-
terno. Escriba un algoritmo recursivo para calcular tal longitud en un árbol
binario. Determine el orden del número de operaciones básicas que realiza su
algoritmo.

Ejercicio 4.9 Escriba una versión recursiva del Algoritmo 8 (vease el ejer-
cicio 3.2).

Ejercicio 4.10 Demuestre los incisos b y c del Teorema 12.

También podría gustarte