análisis de algoritmos y complejidad

21
Universidad Central de Venezuela Facultad de Ciencias Escuela de Computación Caracas, Septiembre 2001 ND 2001– 05 Análisis de Algoritmos y Complejidad Eugenio Scalise, Rhadamés Carmona e-mail: [email protected], [email protected]

Upload: lyhanh

Post on 12-Feb-2017

266 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Análisis de Algoritmos y Complejidad

Universidad Central de VenezuelaFacultad de Ciencias

Escuela de Computación

Caracas, Septiembre 2001

ND 2001– 05

Análisis de Algoritmos y Complejidad

Eugenio Scalise, Rhadamés Carmonae-mail: [email protected], [email protected]

Page 2: Análisis de Algoritmos y Complejidad

ND 2001–05

Análisis de Algoritmos y Complejidad

Eugenio Scalise1

Rhadamés Carmona 2

Septiembre 2001

RESUMEN

El presente trabajo presenta una recopilación de notas y apuntes dedocencia correspondientes al tema “Análisis de Algoritmos yComplejidad” que puede ser utilizado en diversos cursos de laLicenciatura en Computación e inclusive como materialintroductorio para algunos cursos de post-grado. Se presentan losconceptos y herramientas básicas para el cálculo de complejidadtanto en tiempo como en espacio, para algoritmos escritos enlenguajes imperativos (incluyendo algoritmos recursivos). Estasherramientas están acompañadas de ejemplos didácticos queilustran su utilización. Se presenta también una reseña sobre lastécnicas clásicas para la resolución de problemas y suscorrespondientes estudios de complejidad en tiempo. Finalmente,se presenta una breve introducción a la teoría de problemas NP-completos. Cabe destacar que estas notas de docencia estánacompañadas de un conjunto de referencias bibliográficas quecomplementan significativamente los conceptos aquí introducidos.

Palabras Clave: análisis de algoritmos, complejidad, complejidaden tiempo, complejidad en espacio, complejidad de algoritmosrecursivos, problemas NP, problemas NP-completos.

1 Laboratorio MeFIS - Centro ISYS

e-mail: [email protected] Laboratorio de Computación Gráfica

e-mail: [email protected]

Page 3: Análisis de Algoritmos y Complejidad

CONTENIDO

1 Introducción........................................................................................ 2

2 Herramientas para el Análisis de Complejidad .................................. 32.1 Tasas de crecimiento ............................................................................................ 3

2.2 Análisis de Complejidad de Tiempo ................................................................... 4− Regla de la suma............................................................................................... 5− Regla del producto............................................................................................ 5− Análisis de complejidad en tiempo de las instrucciones de un lenguaje.......... 6

2.3 Análisis de Complejidad en Espacio................................................................... 8− Requerimientos estáticos de espacio ................................................................ 8− Requerimientos dinámicos de espacio............................................................ 10

2.4 Ejemplo................................................................................................................ 10

2.5 Análisis de Complejidad en programas recursivos ......................................... 12− Cota Superior de T(n)..................................................................................... 13− Forma Cerrada de T(n) ................................................................................... 14− Solución General para una clase de recurrencias........................................... 14

3 Técnicas clásicas para la resolución de problemas ........................... 153.1 Divide y Conquista ............................................................................................. 15

3.2 Balanceo .............................................................................................................. 16

4 Modelos de Computación.................................................................. 174.1 RAM (Random Access Machine)...................................................................... 17

4.2 RASP (Random Access Stored Program Machine) ........................................ 17

4.3 Turing Machine (Máquina de Turing)............................................................. 17

5 Clasificación de los problemas según su complejidad....................... 18

Referencias .............................................................................................. 19

Page 4: Análisis de Algoritmos y Complejidad

2

1 IntroducciónUn algoritmo es "una secuencia finita de instrucciones, cada una de las cuales tiene unsignificado preciso y puede ejecutarse con una cantidad finita de esfuerzo en un tiempofinito" [AHU 83]. Un programa es un algoritmo expresado en un lenguaje de programaciónespecífico.

Los criterios para evaluar programas son diversos: eficiencia, portabilidad, eficacia,robustez, etc. El análisis de complejidad está relacionado con la eficiencia del programa. Laeficiencia mide el uso de los recursos del computador por un algoritmo. Por su parte, elanálisis de complejidad mide el tiempo de cálculo para ejecutar las operaciones(complejidad en tiempo) y el espacio de memoria para contener y manipular el programamás los datos (complejidad en espacio). Así, el objetivo del análisis de complejidad escuantificar las medidas físicas: "tiempo de ejecución y espacio de memoria" y comparardistintos algoritmos que resuelven un mismo problema.

El tiempo de ejecución de un programa depende de factores como [AHU 83]:

− los datos de entrada del programa

− la calidad del código objeto generado por el compilador

− la naturaleza y rapidez de las instrucciones de máquina utilizadas

− la complejidad en tiempo del algoritmo base del programa

El tiempo de ejecución debe definirse como una función que depende de la entrada; enparticular, de su tamaño. El tiempo requerido por un algoritmo expresado como unafunción del tamaño de la entrada del problema se denomina complejidad en tiempo delalgoritmo y se denota T(n). El “comportamiento límite” de la complejidad a medida quecrece el tamaño del problema se denomina complejidad en tiempo asintótica. De maneraanáloga se pueden establecer definiciones para la complejidad en espacio y la complejidaden espacio asintótica.

En muchos casos, la complejidad de tiempo de un algoritmo es igual para todas lasinstancias de tamaño n del problema. En otros casos, la complejidad de un algoritmo detamaño n es distinta dependiendo de las instancias de tamaño n del problema que resuelve.Esto nos lleva a estudiar la complejidad del peor caso, mejor caso, y caso promedio.

Para un tamaño dado (n), la complejidad del algoritmo en el peor caso resulta de tomar elmáximo tiempo (complejidad máxima) en que se ejecuta el algoritmo, entre todas lasinstancias del problema (que resuelve el algoritmo) de tamaño n; la complejidad en el casopromedio es la esperanza matemática del tiempo de ejecución del algoritmo para entradasde tamaño n, y la complejidad mejor caso es el menor tiempo en que se ejecuta elalgoritmo para entradas de tamaño n. Por defecto se toma la complejidad del peor casocomo medida de complejidad T(n) del algoritmo.

Aún cuando son los programas que consumen recursos computacionales (memoria, tiempo,etc.) sobre una máquina concreta, se realiza el análisis sobre el algoritmo de base,considerando que se ejecuta en una máquina hipotética.

Page 5: Análisis de Algoritmos y Complejidad

3

2 Herramientas para el análisis de complejidadEsta sección presenta un conjunto de herramientas utilizadas para el cálculo de complejidadde algoritmos en tiempo y espacio. Para mayor detalles se recomienda [AHU 74] y [AHU83].

2.1 Tasas de crecimientoDefinición 1: Sean f y g dos funciones, f,g: Ν→ℜ+-{0}. Se dice que f = Ο(g) (f es de ordeng) si y sólo si existe c∈ℜ+ y n0∈Ν tal que ∀n>n0 se cumpla f(n) ≤ c.g(n). (Ν no incluye el0).

La relación Ο denota una dominancia de funciones, en donde la función f está acotadasuperiormente por un múltiplo de la función g (f es dominada por c.g(n)). Así, la expresiónf=Ο(g) refleja que el orden de crecimiento asintótico de la función f es inferior o igual al dela función g.

Ejemplo:

f(n) = 3n3+2n2 y g(n) = n3. Aunque f(0) está definida, podemos restringir su dominio a f(n)con n>0. Así, que f,g:Ν→ℜ+-{0}, y además ∃c∈ℜ+ (c = 5) y n0∈Ν (n0 = 0) tal que ∀n>n0se cumple f(n) ≤ c.g(n). Así, 3n3+2n2 = Ο(n3).

A continuación se enumeran alguna propiedades de la relación O:

− Si c∈ℜ+, y f : Ν→ℜ+-{0}, entonces, c.f=Ο(f)

− Si c∈ℜ+, y f : Ν→ℜ+-{0}, entonces Ο(c.f)≡Ο(f)

− Si f1=Ο(g1) ∧ f2=Ο(g2) entonces f1+f2=Ο(g1+g2)

− Si f1=Ο(g1) ∧ f2=Ο(g2) entonces f1.f2=Ο(g1.g2)

− Si f1=Ο(g) ∧ f2=Ο(g) entonces f1+f2=Ο(g)

Definición 2: Sean f y g dos funciones, f,g: Ν→ℜ+-{0}. Se dice que f y g tienen igualorden de crecimiento (f=Θ(g)) si y sólo si existe c,d ∈ℜ+ y n0∈Ν tal que ∀n>n0 se cumplad.g(n)≤f(n)≤c.g(n).

Se puede demostrar también que Θ es una relación de orden (total) por ser reflexiva,simétrica y transitiva: f = Θ(f), f = Θ(g) ⇒ g = Θ(f), f = Θ(g) ∧ g = Θ(h) ⇒ f = Θ(h).Además, Θ satisface las siguientes propiedades:

− Sean f y g dos funciones, f,g: Ν→ℜ+-{0}. f=Θ(g)⇔f=Ο(g) ∧ g=Ο(f)

− Si c∈ℜ+, y f : Ν→ℜ+-{0}, entonces, c.f=Θ(f)

− Si c∈ℜ+, y f : Ν→ℜ+-{0}, entonces Θ(c.f)≡Θ(f)

− Si f1=Θ(g1) ∧ f2=Θ(g2) entonces f1+f2=Θ(Max{g1,g2})

− Si f1=Θ(g1) ∧ f2=Θ(g2) entonces f1.f2=Θ(g1.g2)

− Si f1=Θ(g) ∧ f2=Θ(g) entonces f1+f2=Θ(g)

− (f+g)k = Θ(fk+gk)

Page 6: Análisis de Algoritmos y Complejidad

4

En la siguiente figura [Bro 89], se presenta un ejemplo de dos funciones f y g con la mismatasa de crecimiento.

Fig. 1.- Dos funciones f y g con la misma tasa de crecimiento

2.2 Análisis de Complejidad de TiempoEl tiempo de ejecución depende de diversos factores. Se tomará como más relevante elrelacionado con la entrada de datos del programa, asociando a un problema un enterollamado tamaño del problema, el cual es una medida de la cantidad de datos de entrada.

De manera general, la complejidad T(n) de un algoritmo es de Ο(f(n)) si T,f : Ν→ℜ+-{0}, y ∃c∈ℜ+ y n0∈Ν tal que ∀n>n0 se cumpla T(n )≤ c.f(n). (ver Definición 1). Generalmente,nos interesa la tasa de crecimiento f(n) del tiempo requerido para resolver instancias másgrandes del problema, basándonos en el concepto de dominancia de funciones.

La tasa de crecimiento obtenida para hallar el orden de complejidad en tiempo de unalgoritmo, permite entre otras cosas:

• Determinar el comportamiento del algoritmo en función del tamaño del problema, yreflejar un tamaño razonable del mismo

• Determinar cuánto tiempo de cómputo aumenta al incrementar el tamaño del problemaen una unidad (f(n+1)-f(n))

• Facilita la comparación de algoritmos y permite determinar entre varios algoritmos,cuál será el más eficiente en teoría (el que tiene menor tasa de crecimiento).

Las tasas de crecimiento suelen ser logarítmicas, polinomiales, y exponenciales. Acontinuación tenemos una comparación entre las tasas de crecimiento más comunes:

log2n <n <n.log2n < n2 < p3(n) < ... < 2n < en < 3n <... < nn < ...

A medida que los computadores se hacen más rápidos, podemos manejar problemas detamaño superior y mediante la complejidad del algoritmo se puede determinar elincremento posible en el tamaño del problema. Suponga que tenemos 5 algoritmos A1-A5

con las complejidades en tiempo indicadas en la Tabla 1 [AHU 74]. La complejidad entiempo viene dada por el número de unidades de tiempo requeridas para procesar una

Page 7: Análisis de Algoritmos y Complejidad

5

entrada de tamaño n. En la tabla, se asume que una unidad de tiempo equivale a unmilisegundo.

Tamaño Máximo del ProblemaAlgoritmo Complejidad

en Tiempo 1 seg. 1 min. 1 horaA1 n 1000 6 x 104 3.6 x 106

A2 n log n 140 4893 2.0 x 103

A3 n2 31 244 1897A4 n3 10 39 153A5 2n 9 15 21

Tabla 1

Si se incrementa en 10 veces la velocidad de los computadores, los tamaños de problemasque se pueden manejar por cada algoritmo vienen dados por:

Algoritmo Complejidaden Tiempo

Tamaño Máx. del Problema(antes del speed-up )

Tamaño Máx. del Problema(después del speed-up)

A1 n s1 10 s1

A2 n log n s2 Aproximadamente 10 s2

A3 n2 s3 3.16 s3

A4 n3 s4 2.15 s4

A5 2n s5 s5 + 3.3Tabla 2

Nótese que para algoritmos con altos órdenes de complejidad, un incremento en 10 vecesen capacidad de cómputo no aumenta considerablemente los tamaños de problemas que sepueden tratar.

Regla de la sumaSean T1(n) y T2(n) las funciones de complejidad para ejecutar dos instrucciones P1 y P2respectivamente (dos instrucciones de un programa), con T1(n)=Ο(f(n)) y T2(n)=Ο(g(n)).La complejidad en tiempo de la secuencia P1;P2 es T1(n)+T2(n)=Ο(Max{f(n),g(n)}). Nóteseque T1,T2 :Ν→ℜ+-{0}, porque el tamaño de los problemas es entero positivo, y el tiempoTi(n) siempre es real positivo.

Regla del productoT1(n)=Ο(f(n)) ∧ T2(n)=Ο(g(n)) ⇒ T1(n).T2(n)=Ο(f(n).g(n))

Sean c,d constantes tan que c,d∈ℜ+-{0}. De las reglas del producto y suma y laspropiedades citadas anteriormente, se deriva:

− T(n)=c ⇒ T(n)=Ο(1)

− T(n)=c+f(n)⇒ T(n)=Ο(f(n))

− T1(n)=c.f(n) ⇒ T1(n)=Ο(f(n))

Page 8: Análisis de Algoritmos y Complejidad

6

− T1(n)=c.f(n)+d ⇒ T1(n)=Ο(f(n))

− T1(n)=Ο(nk) ∧ T2(n)=Ο(nk+1) ⇒ T1(n)+T2(n)=Ο(nk+1)

− T(n)=c.nd ⇒ T(n)=Ο(nd)

− T(n) = Pk(n) ⇒ T(n)=Ο(nk). (Pk(n) es un polinomio de grado k≥0).

− T1(n)=Ln(n) ∧ T2(n)=nk ∧ k>1 ⇒ T1(n)+T2(n)=Ο(nk)

− T1(n)=rn ∧ T2(n)=Pk(n) ∧ r>1 ⇒ T1(n)+T2(n)=Ο(rn)

Análisis de complejidad en tiempo de las instrucciones de un lenguajeA continuación, se presentan algunas reglas para evaluar la complejidad en tiempo de losprogramas, tomando como base, el análisis para un instrucciones en un pseudo-lenguaje.

Regla 1: La función de complejidad en tiempo de una instrucción de asignación simple esuna constante (que depende de la arquitectura donde se ejecutará el algoritmo),independientemente del tamaño de la entrada de datos, y es de orden Ο(1). No todas lasasignaciones son Ο(1). Por ejemplo, una asignación como f←factorial(n) involucra unalgoritmo de n pasos para hallar el factorial de n, lo cual no puede ser Ο(1). De aquí quenos referimos a sólo aquellas asignaciones que no involucran llamadas a funciones (queestudiaremos posteriormente).

Ejemplos:

b ← 100a ← a+1a ← (b div 5) mod 7 - 3

Regla 2: La complejidad en tiempo de una operación simple de entrada/salida es T(n)=c(constante), por lo tanto es de orden Ο(1).

Regla 3: De la regla de la suma se deriva que la complejidad en tiempo de una secuencia dek instrucciones cualesquiera P1;P2;...;Pk, siendo Ti(n)=Ο(fi(n)) la complejidad de lainstrucción Pi, viene dada por

)})(),...,(),({()( 211

nfnfnfmaxnTi k

k

i

Ο=∑=

Ejemplo:

Las tres instrucciones {b ← 100; a ← a+1; a ← (b div 5) mod 7 - 3} son de complejidadT1(n)=c1, T2(n)=c2, T3(n)=c3 respectivamente con c1≤c2≤c3. Así, la función decomplejidad de las tres instrucciones en conjunto es:

T(n)=T1(n)+T2(n)+T3(n)=max{c1,c2,c3}=c3 ⇒ T(n)=Ο(1).

Nótese que no es relevante cual instrucción se tarda más y cual menos, porque al final T(n)es una constante y por ende de orden Ο(1). Por esto, la complejidad de cada instrucciónsimple la tomaremos como la misma constante "c" para simplificar los razonamientos.

Page 9: Análisis de Algoritmos y Complejidad

7

Regla 4: La complejidad en tiempo de una instrucción selectiva condicional simple de laforma:

si <Condición> entonces <Instrucciones> fsi

está dado por complejidad T1(n) de <Condición> sumado a la complejidad T2(n) de<Instrucciones> (porque en el peor caso se ejecutan ambas). Por regla de la suma, si T1(n)=Ο(f(n)) y T2(n)=Ο(g(n)), entonces el tiempo requerido para ejecutar el condicional simplees la suma T1(n)+T2(n), lo cual es Ο(max{f(n),g(n)}).

Análogamente, para una instrucción selectiva de la forma:si <Condición> entonces { <Condición> es de complejidad T1(n)=Ο(f(n)) }

<Instrucciones1> { de complejidad T2(n)=Ο(g(n)) }

sino<Instrucciones2> { de complejidad T3(n)=Ο(h(n)) }

fsi

su complejidad está dado por: T1(n)+max{T2(n)+T3(n)}, lo cual, por regla de la suma es deorden Ο(max{f(n),max{g(n),h(n)}}) o sencillamente Ο(max{f(n),g(n),h(n)})

Regla 5: La complejidad en tiempo de un ciclo iterativo es la suma sobre todas lasiteraciones de:

− la complejidad en tiempo para ejecutar el cuerpo de la iteración, y

− la complejidad en tiempo para evaluar la condición de terminación del ciclo.

La estructuras iterativa más general es el iterar:iterar

<Instrucciones1>parada <Condición>

<Instrucciones2>fiterar

En ocasiones, la complejidad de <Instrucciones1> e <Instrucciones2> puede depender delnúmero de la iteración. De aquí que en general, la complejidad del ciclo iterativo vienedado por

T(n)= ∑ ∑=

=

++k

i

k

icondii nTknTnT

1

1

1,2,1 )(.)()(

en donde, k es el número de iteraciones del ciclo, T1,i(n) es la complejidad en tiempo de<instrucciones1> en la iteración i-ésima, T2,i(n) es la complejidad en tiempo de<instrucciones2> en la iteración i-ésima, Tcond(n) es la complejidad para evaluar lacondición de parada (generalmente de igual complejidad en cada iteración).

Si el cuerpo de <Instrucciones1> es vacío (∑=

=k

ii nT

1,1 0)( ), se acostumbra a utilizar la

estructura del mientras.

mientras <No Condición> hacer<Instrucciones 2> { se ejecuta k-1 veces }

Page 10: Análisis de Algoritmos y Complejidad

8

fmientras

La estructura del repetir se deriva del iterar cuando el cuerpo de <Instrucciones 2> es vacío.

Por lo tanto ∑−

=

=1

1,2 0)(

k

ii nT .

repetir<Instrucciones 1> { se ejcuta k veces }

hasta <Condición>

Se usa el para comúnmente cuando el cuerpo de <Instrucciones 2> es tan sólo unincremento de una variable entera (asociada al número de iteraciones) de complejidad "c".

para <Variable> ← <Inicio> hasta <Final> hacer<Instrucciones 1> { se ejecuta Final-Inicio+1 veces }

fpara

Evaluar la condición de parada es implícitamente chequear si <Variable> llegó a <Final>;por lo tanto, Tcond(n)=c. La complejidad de la instrucción para está dada por:

∑+−

=

+−+1

1,1 ).1.(2)(=T(n)

InicioFinal

ii cInicioFinalnT

donde T1,j(n) es la complejidad del cuerpo del ciclo, en la iteración i-ésima.

Regla 6: Si se tiene un programa con procedimientos (no recursivos), es posible calcular eltiempo de ejecución de los distintos procedimientos, uno a la vez, comenzando conaquellos que no llaman a otros. Luego, se calcula la complejidad en tiempo general usandolas reglas anteriores. Si existen procedimientos recursivos, la complejidad es obtenida poruna ecuación de recurrencia. El cálculo de complejidad de procedimientos y funcionesrecursivos será presentado en la Sección 2.5.

2.3 Análisis de Complejidad en EspacioEl espacio requerido por un algoritmo expresado como función del tamaño del problema esllamado la complejidad en espacio del algoritmo. El espacio requerido por un programacomprende diversos elementos; pero a efectos de análisis de algoritmos, se tomará comomás relevante el relacionado con la entrada de datos del programa.

El requerimiento de espacio para almacenar los datos puede ser estático o dinámico, y seránestudiados a continuación.

Requerimientos estáticos de espacioLos requerimientos estáticos de memoria se refieren al tamaño de los objetos que resuelvenel problema. El espacio requerido por los objetos de datos de los tipos primitivos y de lostipos estructurados intrínsecos de los lenguajes de programación, son fácilmentecalculables, dependiendo de la implementación del mismo. Se les conoce como objetos dedatos estáticos porque la cantidad de espacio que requieren es deducible en su declaración,por lo que ya en compilación (si el lenguaje es compilado), el compilador conoce cuántamemoria requerirán, que será invariante a lo largo del alcance del objeto.

Page 11: Análisis de Algoritmos y Complejidad

9

Se denota Cm(T) al costo en espacio para un tipo de datos T definido. Entonces todavariable X de tipo T tendrá el mismo costo. Por convención, este costo está dado enunidades de palabra de memoria (generalmente 2 bytes).

Tomando como referencia el lenguaje PASCAL, se asumirán los costos para estos objetos.Los valores que se representan dan un orden de magnitud válido para un gran número deimplementaciones:

1) Las instancias de los tipos elementales: Entero, Real, Lógico y Caracter requieren unapalabra de memoria. Luego, Cm(T)=1 si T es un tipo elemental.

2) Las instancias de los tipos definidos por enumeración requieren una palabra de memoria.Si T es un tipo definido por enumeración, Cm(T)=1.

3) Las instancias de los tipos estructurados (sólo arreglos y registros) requieren unacantidad de memoria determinada por la suma del espacio de sus componentes, más elespacio ocupado por el descriptor de la estructura.

3.1) En el caso de un arreglo (estructura homogénea) de longitud n con tipo base T0, elcosto en memoria esta dado por n.Cm(T0)+3, donde 3 es el tamaño en palabras deldescriptor por convención (para algunas implementaciones de PASCAL).

Ejemplo:

a) Sea la siguiente definición:

tipo Arr = arreglo[Li .. Ls] de T0

Entonces, cualquier instancia del tipo Arr requiere como espacio:

Cm(Arr) = 3+(Ls-Li+1).Cm(T0)

b) Sea la siguiente definición:

tipo Matriz = arreglo[1..N, 1..M] de T0

Entonces, cualquier instancia del tipo Matriz requiere como espacio:

Cm(Arr) = 6+N.M.Cm(T0)

3.2) En el caso de los registros (estructura heterogénea porque las componentes pueden serde distinto tipo) se requiere una cantidad de memoria equivalente a la suma del espaciorequerido para cada componente. Sea la siguiente definición:

tipo Reg = registro deC1: T1;C2: T2;. . .Cn: Tn;

fregistro

donde Ci es el campo i-ésimo del registro y de tipo Ti. Entonces, el requerimiento enespacio para cuaquier objeto de tipo Reg viene dado por la ecuación:

Cm(Reg)= ∑=

n

iiTCm

1

)(

Page 12: Análisis de Algoritmos y Complejidad

10

Para un registro variante, se realiza el cálculo de manera similar, sólo que entre los camposvariantes se considera sólo el que ocupa mayor cantidad de espacio.

4) Para una instancia de tipo referencia (apuntador o dirección), se asume el costo de unapalabra.

5) El costo en memoria para una constante es el mismo que para variables del tipo al quepertenece.

6) La definición de un tipo no tiene costo alguno. Esta definición sirve para declararinstancias con ese tipo que si ocuparán espacio de memoria por ser direccionables.

Requerimientos dinámicos de espacioEl análisis de los requerimientos dinámicos de memoria es relativo a los lenguajes queproveen mecanismos de asignación dinámica de la misma. En este caso, la complejidad enespacio viene dada por la cantidad de objetos existentes en un punto del programa, segúnlas reglas de alcance. Así, la cantidad de espacio requerido no será la sumatoria de todas lasdeclaraciones de datos, sino la máxima cantidad de memoria en uso en un momento dadode la ejecución del programa.

2.4 EjemploEn esta sección se realizará un análisis de complejidad en tiempo y espacio para elalgoritmo de clasificación por burbuja, que ordena ascedentemente un arreglo de registroscon campo clave.

Sea la siguiente definición de tipos:tipo Cadena = arreglo [1..40] de caracter;tipo Reg = registro de

NumCarnet: entero; {Campo clave }Nombre: CadenaSueldo: real;

fregistrotipo Empleados = arreglo[1..n] de Reg; {n = Número de empleados}

Se define además una variable A de tipo Empleados. La acción que ordena el arreglo A esel siguiente:

acción burbuja (var A: Empleados);var

i,j: enteroaux: Reg;para i:=1 hasta n-1 hacer

para j:=n hasta i+1 hacersi A[j-1].NumCarnet > A[j].NumCarnet entonces

aux := A[j-1];A[j-1] := A[j];A[j] := aux;

fsifpara

fparafacción

Para hacer el cálculo de complejidad del algoritmo de burbuja es necesario calcular enprimer lugar la complejidad en tiempo del ciclo interno. En cuanto al condicional que seencuentra dentro del ciclo interno, la prueba de la condición requiere un tiempo constante

Page 13: Análisis de Algoritmos y Complejidad

11

c1. Las acciones internas del condicional (3 asignaciones) son de orden constante (que sepueden asumir iguales, por ejemplo c2). Luego, por la regla 4 (regla del condicional) setiene Tcond(n) = O(max{c1, c2}) = c.

La complejidad del ciclo interno viene dada por:

TCicloInterno(n) = ∑+=

−+n

ij

incnTj1

).(.2)( = ∑+=

−+n

ij

incc1

).(.2 = c.(n-i)+2.c.(n-i) = 3.c.(n-i)

El ciclo externo se ejecuta n-1 veces; luego, su complejidad viene dada por:

TCicloExterno(n) = ∑−

=

−+1

1

)1.(.2)(n

inoCicloInter ncnT = ∑

=

−+−1

1

)1.(.2)(.3n

i

ncinc

= )1.(.2.31

1

1

1

−+

− ∑∑

=

=

ncincn

i

n

i

= )1.(.22

)1.()1.(.3 −+

−− ncnn

nnc

= )1.(.2)1.(..23

−+− ncnnc = cncnc .2.21

..23 2 −− = [ ]cncnc .4...3

21 2 −−

Luego, la complejidad en tiempo T(n) del algoritmo de la burbuja es: T(n) = TCicloExterno(n)que es O(n2); finalmente, T(n)= O(n2). Entonces, para ejecutar el algoritmo deordenamiento mostrado arriba, es necesario un tiempo proporcional al cuadrado del númerode elementos a clasificar.

La complejidad de un algoritmo también se puede contabilizar en base al número deoperaciones fundamentales que realiza (en este ejemplo, las comparaciones), ya que lasotras operaciones generarán un factor multiplicativo o aditivo en la función T(n), el cual esdespreciable al calcular el orden de complejidad. Así que para calcular el orden delalgoritmo de burbuja basta con hallar el número de comparaciones C(n) y determinar elorden a partir de éste.

Según esto, en la iteración i del ciclo interno se realizan: Ci(n) = n-i comparaciones. Elciclo externo se ejecuta n-1 veces; por lo tanto, el número de comparaciones C(n) vienedado por:

C(n) = ∑−

=

−=

−=−

1

1

2

22)1(

)(n

i

nnnnin

que es O(n2); luego, T(n)= O(n2).

Para calcular la complejidad en espacio del algoritmo, de manera ordenada, se determina lacomplejidad en espacio para cada tipo de objeto en el algoritmo, y luego se calcula la sumadel número de palabras que requiere cada variable involucrada en el algoritmo.

− El tipo Cadena es un arreglo de 40 caracteres, por lo que Cm(Cadena)=40+3=43palabras de memoria.

− El Tipo Reg require Cm(Reg)=1+43+1=45 palabras de memoria.

− El Tipo Empleados requiere Cm(Empleados)=n.Cm(Reg)+3=43.n+3 palabras dememoria.

Page 14: Análisis de Algoritmos y Complejidad

12

El algoritmo involucra:

− Tres enteros: i,j,n, cada uno ocupa Cm(Entero)=1 palabra de memoria.

− Una variable de tipo Reg: aux, que ocupa Cm(Reg)=45

− Un arreglo A de tipo Empleados que ocupa Cm(Empleados) palabras de memoria

Así, la complejidad en espacio requerido viene dado por:

3.Cm(Entero)+1.Cm(Reg)+1.Cm(Empleados)=3+45+43.n+3=43.n+51 palabras

lo cual es Ο(n).

2.5 Análisis de Complejidad en programas recursivosPara calcular la complejidad sobre programas recursivos se utilizan las herramientas citadasen la Sección 2.3 y adicionalmente se utilizan ecuaciones de recurrencia para expresar lafunción de complejidad en tiempo T(n). Esta sección presentará el análisis de complejidaden tiempo mediante un ejemplo intuitivo, se recomienda revisar [AHU 74], [AHU 83] y[GPK 94] para más detalle.

A continuación, se presenta el algoritmo correspondiente a la función MergeSort utilizadocomo ejemplo a lo largo de esta sección:

función MergeSort(n: entero; L: Lista) -> Listavar

L1, L2: Lista;comienzo

si (n=1) entoncesRetornar Lista;

sinoL1 := Lista de los primeros n/2 elementos de LL2 := Lista de los últimos n/2 elementos de Lretorna (Mezcla(MergeSort(n/2,L1),MergeSort(n/2,L2)));

fsiffunción

Esta función ordena una lista de elementos realizando particiones sucesivas hasta obtenerlistas de un elemento y aplicando el algoritmo tradicional de mezcla entre sub-listasordenadas. Por ejemplo, para la lista (5, 2, 4, 6) la función MergeSort divide la lista hastaobtener sub-listas unitarias y en los retornos realiza las siguientes mezclas (cada nivel delgráfico corresponde a un nivel de la recursion):

mezcla

5 2

2 5

4 6

4 6

mezcla

2 4 5 6

mezcla

La función de complejidad T(n) para el algoritmo mergeSort viene dada por una definiciónrecursiva. En particular, se distingue el tratamiento de listas unitarias y el tratamiento delistas de tamaño mayor o igual que dos.

Page 15: Análisis de Algoritmos y Complejidad

13

Para el caso de listas unitarias (condición n=1 en la función mergeSort) se tiene unacomplejidad constante que denominaremos c1. Para listas de tamaño superior a uno (parteelse de la función mergeSort) el problema es dividido en dos sub-problemas de tamañon/2 con un costo adicional para:

− construir dos listas de tamaño n/2, con costo c3.n

− mezclar dos listas de tamaño n/2, con costo c4.n

Luego, tomando c2 = c3 + c4, se tiene:

T(n) = 11

.)2(.2 2

1

>=

+ nsinsi

ncnTc

donde se asume que n es potencia de 2 (n = 2k, k ≥ 0). Para determinar el orden decomplejidad de la función mergeSort es necesario resolver la ecuación de recurrenciaasociada a T(n). A manera de ejemplo, se presentarán tres enfoques para determinar elorden de un algoritmo cuya función T(n) es una ecuación de recurrencia:

− Proponer una cota superior f(n) para T(n); o sea, demostrar que T(n) ≤ f(n) para un fdado

− Hallar una forma cerrada3 para T(n)

− Emplear soluciones conocidas para recurrencias

En lo que resta de esta sección se ilustrarán estos tres enfoques para resolver la función decomplejidad en tiempo T(n) asociada a la función mergeSort.

Cota Superior de T(n)Este enfoque persigue buscar una cota superior para T(n) proponiendo funciones f(n)candidatas y comprobando que efectivamente acotan superiormente a T(n). Para continuarcon el ejemplo de la función mergeSort, suponga que para algún a se cumple que:

f(n) = a.n.log2(n) + b.

Si tomamos n = 1, debe cumplirse b ≥ c1 [r1], para que se cumpla T(n) ≤ f(n).

Para realizar una prueba por inducción, se supone que:

T(k) ≤ a.k.log2(k) + b, para todo k < n [1]

Para iniciar la demostración, se supone que n ≥ 2, luego:

T(n) ≤ ncnT .)2(.2 2+

Aplicando [1], tenemos (suponiendo k = n/2):

T(n) ≤ [ ] ncbnna .)2(log.2..2 22 ++

≤ [ ] ncbnna ..2)2(log)(log.. 222 ++−

3 una forma cerrada es una función (no recursiva) en términos de n y algunas constantes

Page 16: Análisis de Algoritmos y Complejidad

14

≤ ncbnanna ..2.)(log.. 22 ++−

≤ bnna +)(log.. 2 (siempre y cuando a ≥ c2 + b [r2])

Luego, si se seleccionan a y b de manera que cumplan con las restricciones [r1] y [r2]citadas anteriormente, por ejemplo: b = c1 y a = c1 + c2, se cumple que para todo n:

T(n) ≤ (c1 + c2).n.log2(n) + c1

Luego, T(n) = O(n.log2(n)). Cabe destacar que esta no es la tasa exacta de T(n), pero secomprueba que T(n) no es peor que c.n.log2(n).

Forma Cerrada de T(n)Utilizando la hipótesis de que n = 2k, se puede hallar una fórmula no recurrente (cerrada)para T(n). Para esto, se desarrolla T(n) realizando las sustituciones para: n, n/2, n/4, ... , 1:

T(n) = 2.T(n/2) + c2.n

= 2.[2.T(n/4) + c2.n/2] + c2.n = 4.T(n/4) + 2.c2.n

= 4.[2.T(n/8) + c2.n/4] + 2.c2.n = 8.T(n/8) + 3.c2.n

= ...

(sust. i-ésima) = 2i.T(n/2i) + i.c2.n

Si n = 2k, la expansión termina cuando i = k. Luego:

T(n) = 2k.T(n/2k) + k.c2.n = 2k.T(1) + k.c2.n = 2k.c1 + k.c2.n

Sustituyendo k = log2(n), se tiene:

T(n) = 221)(log ).(log..2 2 cnncn + = 221

)2(log ).(log..2 cnncn + = 221 ).(log.. cnncn +

Luego, T(n) = O( )(log. 2 nn ).

Solución General para una clase de recurrenciasLa función mergeSort es un ejemplo típico de solución de un problema mediante ladivisión en sub-problemas. Para este tipo de problemas, existe un teorema [AHU 74] que secita a continuación.

Teorema: Sean a, b y c constantes no negativas. La solución de la recurrencia:

T(n) = 11

.)(. >=

+ nsinsi

nbcnTab

donde: a es el número de sub-problemas y c es el tamaño de cada uno. Si n es potencia de cse cumple que:

T(n) = casicasi

casi

nOnnO

nO

ac >=

<

)()log(

)(

log

Page 17: Análisis de Algoritmos y Complejidad

15

Por razones de espacio, no se cita la demostración del teorema; para mayor detalles de estademostración se recomienda [AHU 83]. Este tipo de soluciones generales pueden utilizarsepara hallar el orden de complejidad de funciones T(n). En particular, para la función decomplejidad de mergeSort:

T(n) = 11

.)2(.2 2

1

>=

+ nsinsi

ncnTc

se puede asumir (por simplicidad) que c1 = c2 y en consecuencia se tiene: b = c1 = c2, a = c= 2. Aplicando el teorema presentado se puede afirmar que T(n) = O(n.log n).

3 Técnicas clásicas para la resolución de problemasEn esta sección se discuten algunas técnicas de programación que son utilizadas confrecuencia para definir algoritmos eficientes. Entre estas técnicas se pueden citar: la técnicade divide y conquista (divide and conquer), balanceo, programación dinámica,backtracking y búsqueda local. En este trabajo sólo se discuten la técnica de “divide yconquista” y “balanceo”.

3.1 Divide y ConquistaUn enfoque común para resolver un problema consiste en particionar un problema en partesmas pequeñas y combinar la solución de las partes como la solución general al problema.Cuando se aplica este enfoque de manera recursiva ofrece soluciones eficientes en lascuales los sub-problemas constituyen versiones reducidas del problema original.

A manera de ejemplo [AHU 74], considere el problema de encontrar tanto el máximo comoel mínimo de un conjunto de n elementos enteros. Por simplicidad, se asume que n es unapotencia de 2. Una manera obvia de encontrar el máximo y el mínimo podría ser:

función max_min(C: Conjunto_Enteros) → tupla_enteros;var

max, min: enteromax := cualquier elemento de C;min := max;para todos los x elementos restantes en C hacer

si x > max entoncesmax:=x;

fsisi x < min entonces

min:=x;fsi

fparamax_min := (max, min);

ffuncion

Asumiendo n≥2, se puede calcular tanto el máximo como el mínimo de un conjuntoefectuando 2(n-1) = 2n-2 comparaciones. Luego, T(n) = 2n-2 comparaciones.

Utilizando un enfoque divide y conquista se puede dividir el conjunto C de entrada en dosconjuntos C1 y C2 de n/2 elementos; en cuyo caso, el algoritmo puede buscar tanto el

Page 18: Análisis de Algoritmos y Complejidad

16

máximo como el mínimo de cada sub-conjunto, mediante aplicaciones recursivas. Elalgoritmo basado en esta estrategia viene dado por:

función max_min(C: Conjunto_Enteros) → tupla_enteros;var

max1, max2, min1, min2: entero;si || C || = 2 entonces // sea C el conjunto {a,b}

return (max(a,b), min(a,b));sino

divide C en dos sub-conjuntos C1 y C2, cada uno con la mitad de los elementos(max1, min1) := max_min(C1);(max2, min2) := max_min(C2);return (max(max1,max2), min(min1,min2));

fsiffuncion

Sea T(n) el número total de comparaciones requeridas por max_min para encontrar elmáximo y el mínimo del conjunto C. Es obvio que T(2) = 1. Si n>2, T(n) es el número totalde comparaciones utilizadas en las dos llamadas de max_min sobre conjuntos de tamañon/2, más las dos comparaciones utilizadas para determinar el máximo y el mínimo de lossub-conjuntos. Luego:

22

,2)2/(21

)(>=

+=

nn

nTnT

La ecuación de recurrencia T(n) puede ser resuelta de manera general. La función

T(n) = 223

−n

es la solución de la ecuación de recurrencia presentada anteriormente. Por lo tanto, sedemuestra que la segunda solución es más eficiente que la primera.

3.2 BalanceoEn general, las estrategias de particionar un problema utilizan sub-problemas de igualtamaño, pero esto no es una condición necesaria. Por ejemplo, la clasificación uordenamiento por insersión puede considerarse como la división de un problema en dossub-problemas, uno de tamaño 1 y otro de tamaño n-1, con un costo máximo de n pasospara combinarlos. Esto se puede expresar la mediante la ecuación de recurrencia:

T(n) = T(1) + T(n-1) + n

que es de orden O(n2). Si se utiliza la clasificación por intercalación (mergesort), que divideel problema en dos sub-problemas de tamaño n/2 y combina las permutaciones obtenidas,se tiene una función de complejidad representada por:

11

,1)2/(20

)(>=

−+=

nn

nnTnT

que es de orden O(n.log n).

Como principio general, dividir un problema en sub-problemas iguales o casi iguales es unfactor crucial para la obtención de un buen rendimiento.

Page 19: Análisis de Algoritmos y Complejidad

17

4 Modelos de ComputaciónEn esta sección se definen diversos modelos teóricos de computación utilizados paraevaluar la complejidad de algoritmos. Estos modelos permiten hacer un estudio formal de lacomplejidad de un programa (asociado a un algoritmo) en términos de modelos decomputación concretos, cercanos a los modelos de cómputo reales. Esto permite hacerestudios comparativos independientes de la plataforma de hardware.

4.1 RAM (Random Access Machine)Una máquina de acceso aleatorio (RAM – Random Access Machine) modela uncomputador con un acumulador, cuyas instrucciones no pueden modificar el programafuente. Una RAM esta formada por una cinta de sólo lectura, una cinta de sólo escritura, unprograma y una memoria. Tanto la cinta de entrada como la cinta de salida almacenanvalores enteros. La cinta de salida está inicialmente en blanco. Cada vez que se lee/escribede una cinta, la cabeza de lectura/escritura correspondiente se desplaza un elemento hacia laderecha.

La memoria está formada por una secuencia de registros r0,...,rm; capaces de almacenar unentero. Todos los cálculos son realizados en el registro r0, el cual es denominado“acumulador”. Un programa de la RAM está formado por una secuencia de instrucciones(aritméticas, I/O, indirecciones, saltos, etc.); cada instrucción esta conformada por uncódigo de operación y una dirección.

Para realizar los cálculos de complejidad de un programa en una RAM se calcula el tiemponecesario para ejecutar cada instrucción y la cantidad de registros utilizados. En general,existen dos criterios para evaluar la complejidad: el criterio uniforme (en el cual se asumeque todas las operaciones tardan el mismo tiempo en ejecutarse) y el criterio logarítmico(en el cual se establece un costo a cada tipo de operación).

4.2 RASP (Random Access Stored Program Machine)Este modelo es similar a la RAM, con la diferencia de que el programa es almacenado enmemoria y en consecuencia, puede modificarse a si mismo. El programa es entoncesalmacenado en los registros de memoria de la RASP, tomando en cuenta que cadainstrucción de la RASP ocupa dos registros (uno para el tipo de operación y otro para ladirección).

Está demostrado que un programa que se ejecuta con una complejidad T(n) en una RAM,puede ser ejecutado en una RASP con una complejidad k.T(n).

4.3 Turing Machine (Máquina de Turing)Una máquina de turing multicintas (TM – Turing Machine) está compuesta por un númerok de cintas de capacidad infinita. Cada cinta almacena un conjunto de símbolos propios dela máquina y posee una cabeza lectora/escritora. La operación de una máquina de turingviene dada por un programa denominado control finito.

De acuerdo al estado actual del control finito y de los símbolos de las cintas, la máquina deTuring puede realizar cualquiera de las siguientes operaciones:

Page 20: Análisis de Algoritmos y Complejidad

18

− Cambiar el estado del control finito.

− Escribir nuevos símbolos sobre los actuales en cualquiera de las celdas de las diversascintas.

− Mover cualquiera de las cabezas de las cintas a la izquierda o a la derecha.

Una máquina de turing puede ser vista formalmente como una 7-tupla: (Q, T, I, δ, b, q0, qf),donde: Q es el conjunto de estados, T es el conjunto de símbolos de las cintas, I es elconjunto de símbolos de entrada, b es el símbolo blanco o nulo, q0 es el estado inicial, qf esel estado final y δ es la función de transición de estados.

Una máquina de turing constituye un modelo formal para reconocedores de lenguajes. Paraesto es necesario colocar en la primera cinta la cadena a ser reconocida; luego, la cadena esreconocida si y sólo si, partiendo del estado inicial, la máquina realiza una secuencia depasos tales que alcanza un estado de aceptación.

El modelo definido por una máquina de turing es utilizado para determinar cotas inferiorespara las funciones de complejidad tanto en espacio como en tiempo de la solución de unproblema determinado.

Este modelo general es conocido también como Máquina de Turing Determinísitica(MTD). Adicionalmente, se puede definir una Máquina de Turing No Determinística(MTND) como aquella en la cual la función de transición δ tiene más de un resultado parapares estado-símbolo. Desde otro punto de vista, puede afirmarse que un programa en unaMTND difiere de un programa de una MTD en que el primero se ejecuta en dos etapas:etapa de pronóstico (que consiste en obtener una secuencia de pronóstico o cadena inicialpara ejecutar el programa de la MT) y una etapa de chequeo (que opera bajo las mismasreglas que una MTD).

5 Clasificación de los problemas según su complejidadSi se estudia la complejidad desde el punto de vista de reconocimiento de lenguajes, seutilizan como modelos base las máquinas de turing. Según esto, pueden clasificarse loslenguajes en diversas clases:

− La clase P: es la clase de lenguajes que pueden ser reconocidos por una máquina deturing en tiempo polinómico.

− La clase NP: es la clase de lenguajes que pueden ser reconocidos por una máquina deturing no determinística en tiempo polinómico.

Estas definiciones están basadas en la teoría de lenguajes y en general, es convenientedestacar que para hacer un estudio de un algoritmo bajo la teoría de problemas P/NP esnecesario reescribir el problema como un problema de reconocimiento de lenguajes(específicamente como un problema de decisión). Esto permite que problemas de diversasdisciplinas sean tratados como problemas de reconocimiento de lenguajes, aplicando así unrazonamiento teórico general.

Dentro de la clase de problemas catalogados como NP se diferencia el conjunto NP-completo. Se dice que un lenguaje L0 es NP-completo si está en NP y todo lenguaje L en

Page 21: Análisis de Algoritmos y Complejidad

19

NP puede ser transformado en tiempo polinomial en L0. Cabe destacar que tanto L como L0representan problemas, expresados como lenguajes.

Las relaciones entre las clases de problemas se muestran en la siguiente gráfica:

NP-completo

P

NP

Para probar que un problema π es un problema NP-completo se debe:

− Expresar π como un lenguaje L; en particular como un problema de decisión.

− Mostrar que L ∈ NP, esto es, mostrar que L puede ser reconocido por una máquina deturing no determinísitica.

− Seleccionar un problema NP-completo conocido L’

− Definir una transformación f : L’ → L.

− Probar que f es polinomial

La teoría de problemas NP-completos permite utilizar una base formal para saber si unproblema es intratable o no. En general, los problemas clasificados dentro de la clase P seconsideran tratables o factibles computables, mientras que los problemas clasificadosdentro de la clase NP se consideran infactibles o intratables. Para mayor detalle de estetema, se recomienda [GJ 79].

Referencias[AHU 74] AHO A., HOPCROFT J., ULLMAN J. The Design and Analysis of Computer

Algorithms. Addison-Wesley Publishing Company. 1974.

[AHU 83] AHO A., HOPCROFT J., ULLMAN J. Data Structures and Algorithms.Addison-Wesley Publishing Company. 1983.

[Bro 89] BROOKSHEAR J. G., Theory of Computation. Formal Languages, Automataand Complexity. The Benjamin/Cummings Publishing Company, Inc.,Reedwood City, California, USA. 1989.

[GJ 79] GAREY M., JOHNSON D. Computers and Intractability: A Guide to theTheory of NP-Completeness. Series of Books in the Mathematical Sciences.W. H. Freeman & Co. 1979.

[GPK 94] GRAHAM R., PATASHNIK O., KNUTH D.E. Concrete Mathematics: AFoundation for Computer Science. Addison-Wesley Publishing Company. 2nd.Edition. March 1994.

[Mat 97] MATTEO A. Apuntes del curso "Estructuras de Datos y Análisis deAlgoritmos" . Postgrado en Ciencias de la Computación. 1997.