transparencias de análisis y diseño de algoritmos

Upload: perpetuo-mogudo

Post on 10-Jul-2015

60 views

Category:

Documents


0 download

TRANSCRIPT

Apuntes de Anlisis y Diseo de AlgoritmosConrado MartnezLSI-UPC

Septiembre 2006

1 2 3 4

Anlisis de Algoritmos Divide y Vencers Estructuras de Datos Algoritmos Voraces

El anlisis de algoritmos tiene como objetivo establecer propiedades sobre la eficiencia permitiendo la comparacin entre soluciones alternativas y predecir los recursos que usar un algoritmo o ED.

Consideremos el siguiente algoritmo para determinar de elementos: el mnimo de un vector

El nmero de operaciones elementales (comparaciones, asignaciones, sumas, etc.) que realiza es bsicamente proporcional al nmero de elementos del vector y el tiempo de ejecucin ser, consecuentemente, de la . forma

La forma del coste es independiente del hardware sobre el que se implementa, el sistema operativo, el lenguaje de programacin, etc.; slo las constantes y dependen de estos factores Para analizar algoritmos slo contaremos el nmero de operaciones elementales, asumiendo que cada una de ellas consume una unidad de tiempo En general, la eficiencia de un algoritmo depender de cada entrada concreta, no slo del tamao de la entrada

Dado un algoritmo cuyo conjunto de entradas es su eficiencia o coste (en tiempo, en espacio, en nmero de operaciones de E/S, etc.) es una funcin de en (o , segn el caso):

Caracterizar la funcin puede ser muy complicado y adems proporciona informacin inmanejable, difcilmente utilizable en la prctica.

Sea el conjunto de entradas de tamao la funcin restringida a . Coste en caso mejor:

y

mejor Coste en caso peor:

peorCoste promedio:

avg

1

Para todo

y para cualquier mejor peor avg peor

2

Para todo

mejor

Por lo general, estudiaremos el coste en caso peor de los algoritmos por dos razones: proporciona garantas sobre la eficiencia del algoritmo ya que el coste nunca exceder el coste en caso peor es ms fcil de calcular que el coste promedio

Una caracterstica esencial del coste (en caso peor, en caso mejor, promedio) es su tasa de crecimiento u orden de magnitud.

DefinicinDada una funcin de ) es la clase

(O-grande

Aunque es un conjunto de funciones por tradicin se escribe en vez de . Sin no tiene sentido. Algunas embargo, propiedades bsicas de la notacin :

1 2

Si

Es reflexiva: para toda funcin . Es transitiva: si .

entonces y

. ,

3

4

Para toda constante

entonces

, .

Adems de la notacin O-grande se utilizan las (omega) y (zita). La primera define un notaciones conjunto de funciones acotada inferiormente por una dada:

La notacin es reflexiva y transitiva; si entonces . Por otra parte, y viceversa. si entonces Se dice que es la clase de las funciones que crecen es la clase de no ms rpido que . Anlogamente, las funciones que crecen no ms despacio que . Finalmente,

es la clase de la funciones con la misma tasa de crecimiento que .

La notacin es reflexiva y transitiva, como las otras. Es adems simtrica: si y slo si . Si donde entonces . Otras propiedades adicionales de las notaciones asintticas son (las inclusiones son estrictas):

1

2

Para cualesquiera constantes creciente, . Para cualquier constante .

Para cualesquiera constantes , si funcin creciente entonces

y

, si

.

es una es

3

, si

es creciente,

Los operadores convencionales (sumas, restas, divisiones, etc.) sobre clases de funciones definidas mediante una notacin asinttica se extienden de la siguiente manera:

donde y son conjuntos de funciones. Expresiones de la forma donde es una funcin se entender . como Este convenio nos permite escribir de manera cmoda , , expresiones como .

Regla de las sumas:

Regla de los productos:

Reglas similares se cumplen para las notaciones

y .

Las dos ltimas reglas facilitan el anlisis del coste en caso peor de algoritmos iterativos.1 2

El coste de una operacin elemental es Si el coste de un fragmento es entonces el coste de es .

3

Si el coste de es , el de es y el coste de evaluar es entonces el coste en caso peor de

y el de

.

es

es

.

4

Si el coste de durante la -sima iteracin es es y el nmero de el coste de evaluar iteraciones es entonces el coste de

,

es

Si

entonces

.

Anlisis de algoritmos recursivos

El coste (en caso peor, medio, . . . ) de un algoritmo recursivo satisface, dada la naturaleza del algoritmo, una ecuacin recurrente: esto es, depender del valor de para tamaos menores. Frecuentemente, la recurrencia adopta una de las dos siguientes formas:

Sea el coste (en caso peor, en caso medio, ...) de un algoritmo recursivo que satisface la recurrencia si si , donde es una constante, , es una funcin arbitraria y para una cierta constante . Entonces si si si

Teorema

Sea el coste (en caso peor, en caso medio, . . . ) de un algoritmo recursivo que satisface la recurrencia

Teorema

si si , donde es una constante, , es una funcin arbitraria y para una cierta constante . . Entonces Sea si si si

Demostracin. Supondremos que repetidamente,

. Aplicando la recurrencia

Puesto que es una constante tenemos que el segundo trmino es ya que

Ahora tenemos tres casos diferentes a considerar: segn que sea menor, igual o mayor que . , segn que sea Alternativamente, ya que mayor, igual o menor que . Si (equivalentemente, ) entonces la suma que aparece en el primer trmino es una serie geomtrica acotada por una constante, es decir, el primer trmino es . Como asumimos que , es el primer . trmino el que domina y

y la suma vale . Ya , conclumos que . Finalmente, si entonces y el valor de la suma es Si que

,

tendremos que

Por tanto, es el segundo trmino el que en este caso domina y .

1 2 3 4

Anlisis de Algoritmos Divide y Vencers Estructuras de Datos Algoritmos Voraces

IntroduccinEl principio bsico de divide y vencers (en ingls, divide and conquer; en cataln, dividir per conquerir) es muy simple:1

Si el ejemplar (instancia) del problema a resolver es suficientemente simple, se encuentra la solucin mediante algn mtodo directo.

IntroduccinEl principio bsico de divide y vencers (en ingls, divide and conquer; en cataln, dividir per conquerir) es muy simple:1

Si el ejemplar (instancia) del problema a resolver es suficientemente simple, se encuentra la solucin mediante algn mtodo directo. En caso contrario, se divide o fragmenta el ejemplar dado en subejemplares y se resuelve, independiente y recursivamente, el problema para cada uno de ellos.

2

IntroduccinEl principio bsico de divide y vencers (en ingls, divide and conquer; en cataln, dividir per conquerir) es muy simple:1

Si el ejemplar (instancia) del problema a resolver es suficientemente simple, se encuentra la solucin mediante algn mtodo directo. En caso contrario, se divide o fragmenta el ejemplar dado en subejemplares y se resuelve, independiente y recursivamente, el problema para cada uno de ellos. Las soluciones obtenidas se combinan para obtener la solucin al ejemplar original.

2

3

Si

, se habla del esquema de reduccin.

El esquema de divide y vencers se caracteriza adicionalmente por las siguientes propiedades: No se resuelve ms de una vez un mismo subproblema

El esquema de divide y vencers se caracteriza adicionalmente por las siguientes propiedades: No se resuelve ms de una vez un mismo subproblema El tamao de los subejemplares es, en promedio, una fraccin del tamao del ejemplar original, es decir, si es de tamao , el tamao esperado de . un subejemplar cualquiera es donde Con frecuencia, esta condicin se cumplir siempre, no slo en promedio.

El anlisis de un algoritmo divide y vencers requiere, al igual que ocurre con otros algoritmos recursivos, la resolucin de recurrencias. La recurrencia tpica es

si si

, .

= tamao mximo de las instancias con solucin directa = tamao de los subejemplares resueltos recursivamente

= coste de dividir y combinar

= nmero de llamadas recursivas

Bsqueda dicotmicaProblema: Dado un vector de elementos, en orden creciente, y un elemento , determinar el valor , , tal que (convendremos que y ). fuese vaco la respuesta es sencilla, pues Si el vector no est en el vector. Pero para poder obtener una solucin recursiva efectiva, debemos realizar la siguiente generalizacin: dado un segmento o , , ordenado subvector , determinar el crecientemente, y tal que valor , , tal que . Este es un ejemplo clsico de inmersin de parmetros. , el segmento en cuestin no contiene Si elementos y .

La llamada inicial es , donde nos da , el nmero de elementos del vector . Prescindiendo de la pequea diferencia entre el tamao real de los subejemplares y el valor , el coste de la bsqueda binaria o dicotmica viene descrito por la recurrencia

y

y . Aplicamos el teorema 1 con por lo tanto .

Algoritmo de Karatsuba

El algoritmo tradicional de multiplicacin tiene coste para multiplicar dos enteros de bits, ya la suma bits tiene coste . de dos enteros de

El mismo coste tiene el algoritmo de multiplicacin a la rusa.

Supongamos que e son dos nmeros positivos de bits (si no es una potencia de 2 e no tienen ambos la misma longitud, podemos aadir 0s por la izquierda para que as sea). Una idea que no funciona:

Efectuada la descomposicin y , se calculan 4 productos ( , , y ) y se combinan las soluciones mediante sumas y desplazamientos (shifts). El coste de estas operaciones es . Por lo tanto, el coste de la multiplicacin mediante este algoritmo divide y vencers viene dado por la recurrencia

cuya solucin es . En este caso y , y .

,

El algoritmo de Karatsuba (1962) realiza la multiplicacin dividiendo los dos nmeros como antes pero se realizan las siguientes 3 multiplicaciones (recursivamente)

Y el resultado se calcula a partir de las tres soluciones obtenidas

El algoritmo requiere realizar 6 adiciones (una de ellas es de hecho una resta) frente a las 3 adiciones que se empleaban en el algoritmo anterior. El coste mejora pues el coste de las funciones de divisin y de combinacin del algoritmo de Karatsuba sigue siendo lineal y disminuye el nmero de llamadas recursivas.

La constante oculta en la notacin asinttica es grande y la ventaja de este algoritmo frente a los algoritmos bsicos no se manifiesta hasta que es relativamente grande (del orden de 200 a 250 bits por multiplicando).

Ordenacin por fusinEl algoritmo de ordenacin por fusin (mergesort) fue uno de los primeros algoritmos eficentes de ordenacin jams propuestos. Diversas variantes de este algoritmo son particularmente tiles para la ordenacin de datos residentes en memoria externa. El propio mergesort es un mtodo muy eficaz para la ordenacin de listas enlazadas. La idea bsica es simple: se divide la secuencia de datos a ordenar en dos subsecuencias de igual o similar tamao, se ordena recursivamente cada una de ellas, y finalmente se obtiene una secuencia ordenada fusionando las dos subsecuencias ordenadas. Si la secuencia es suficientemente pequea puede emplearse un mtodo ms simple (y eficaz para entradas de tamao reducido).

Supondremos que nuestra entrada es una lista enlazada conteniendo una secuencia de elementos . Cada elemento se almacena en un nodo con dos campos: contiene el elemento y es un apuntador al siguiente nodo de la lista (indicaremos que un nodo no tiene sucesor con el valor especial = 0). La propia lista es, de hecho, un apuntador a su primer nodo.

Cada elemento de y es visitado exactamente una vez, por lo que el coste de la operacin es proporcional a la suma de los tamaos de las listas y ; es decir, su coste es . El coste de mergesort viene descrito por la recurrencia

cuya solucin es caso del Teorema 1.

, aplicando el segundo

Algoritmo de Strassen

El algoritmo convencional de multiplicacin de matrices tiene coste para multiplicar dos matrices .

Para abordar una solucin con el esquema divide y vencers se descomponen las matrices en bloques:

donde , etc. requiere dos multiplicaciones de Cada bloque de bloques de tamao y dos sumas cuyo coste es . El coste del algoritmo divide y vencers as planteado tendra coste

es decir, . Para conseguir una solucin ms eficiente debemos reducir el nmero de llamadas recursivas.

Strassen (1969) propuso una forma de hacer justamente esto. En el libro de Brassard & Bratley se detalla una posible manera de hallar la serie de frmulas que producen el resultado deseado. Un factor que complica las cosas es que la multiplicacin de matrices no es conmutativa, a diferencia de lo que sucede con la multiplicacin de enteros. Se obtienen las siguientes 7 matrices , mediante 7 productos y 14 adiciones/sustracciones

Mediante 10 adiciones/sustracciones ms, podemos obtener los bloques de la matriz resultante :

Puesto que las operaciones aditivas tienen coste , el coste del algoritmo de Strassen viene dado por la recurrencia

cuya solucin es .

El algoritmo de Strassen tuvo un enorme impacto terico ya que fue el primer algoritmo de multiplicacin de matrices cuya complejidad era ; lo cual tena tambin implicaciones en el desarrollo de algoritmos ms eficientes para el clculo de matrices inversas, de determinantes, etc. Adems era uno de los primeros casos en que las tcnicas algortmicas superaban lo que hasta el momento pareca una barrera infranqueable. En aos posteriores se han obtenido algoritmos todava ms eficientes. El ms eficiente conocido es el de Coppersmith y Winograd (1986) cuyo coste es . El algoritmo de Strassen no es competitivo en la prctica, excepto si es muy grande ( ), ya que las constantes y trminos de orden inferior del coste son muy grandes.

QuicksortQuicksort (Hoare, 1962) es un algoritmo de ordenacin que usa el principio de divide y vencers, pero a diferencia de los ejemplos anteriores, no garantiza que cada subejemplar tendr un tamao que es fraccin del tamao original.

El procedimiento de particin sita a un elemento, el pivote, en su lugar apropiado. Luego no queda ms que ordenar los segmentos que quedan a su izquierda y a su derecha. Mientras que en mergesort la divisin es simple y el trabajo se realiza durante la fase de combinacin, en quicksort sucede lo contrario. Para ordenar el segmento el algoritmo queda as

En vez de usar un algoritmo de ordenacin simple (p.e. ordenacin por insercin) con cada segmento de o menos elementos, puede ordenarse mediante el algoritmo de insercin al final:

Puesto que el vector est quasi-ordenado tras aplicar , el ltimo paso se hace en tiempo . donde Se estima que la eleccin ptima para el umbral o corte de recursin oscila entre 20 y 25.

,

Existen muchas formas posibles de realizar la particin. En Bentley & McIlroy (1993) se discute un procedimiento de particin muy eficiente, incluso si hay elementos repetidos. Aqu examinamos un algoritmo bsico, pero razonablemente eficaz. Se mantienen dos ndices y de tal modo que contiene elementos menores o iguales que contiene elementos mayores o el pivote , y iguales. Los ndices barren el segmento (de izquierda a derecha, y de derecha a izquierda, respectivamente), y o se cruzan ( ). hasta que

El coste de quicksort en caso peor es y por lo tanto poco atractivo en trminos prcticos. Esto ocurre si en todos o la gran mayora de los casos uno de los subsegementos contiene muy pocos elementos y el otro casi todos, p.e. as sucede si el vector est ordenado creciente o decrecientemente. El coste de la particin es y entonces tenemos

Sin embargo, en promedio, el pivote quedar ms o menos centrado hacia la mitad del segmento como sera deseable justificando que quicksort sea considerado un algoritmo de divide y vencers.

Para analizar el comportamiento de quicksort slo importa el orden relativo de los elementos. Tambin podemos investigar exclusivamente el nmero de comparaciones entre elementos, ya que el coste total es proporcional a dicho nmero. Supongamos que cualquiera de los ordenes relativos posibles tiene idntica probabilidad, y sea el nmero medio de comparaciones.

nm. compar. pivote es -simo pivote es -simo

Multiplicamos por y sustraemos eliminar los factores y los sumatorios:

para

Por lo tanto

Pasando

al otro lado y desplegando

Tomando

,

y reorganizando la suma

Puesto que

tenemos

Usando

podemos expresar como

Luego

Quickselect

El problema de la seleccin consiste en hallar el -simo de entre elementos dados. En concreto, dado un vector con elementos y un rango , , un algoritmo de seleccin debe hallar el -simo elemento en orden ascendente. Si entonces hay que entonces hay que hallar el encontrar el mnimo, si mximo, si entonces debemos hallar la mediana, etc.

Es fcil resolver el problema con coste ordenando previamente el vector y con coste , recorriendo el vector y manteniendo los elementos menores de entre los ya examinados. Con las estructuras de datos apropiadas puede rebajarse el , lo cual no supone una mejora sobre la coste a primera alternativa si . Quickselect (Hoare, 1962), tambin llamado Find y one-sided quicksort, es una variante del algoritmo quicksort para la seleccin del -simo de entre elementos.

Supongamos que efectuamos una particin de un subvector , conteniendo los elementos -simo a -simo de , y tal que , respecto a un pivote . Una vez finalizada la particin, supongamos que el pivote acaba en la posicin . Por tanto, en estn los elementos -simo a -simo de y en estn los elementos -simo a -simo. Si hemos acabado ya que hemos encontrado el elemento solicitado. Si entonces procedemos recursivamente en el subvector y si entonces de la izquierda encontraremos el elemento buscado en el subvector .

Puesto que es recursiva final es muy simple obtener una versin iterativa eficiente que no necesita espacio auxiliar. En caso peor, el coste de es . Sin embargo, su coste promedio es donde la constante de proporcionalidad depende del cociente . Knuth (1971) ha demostrado que , el nmero medio de comparaciones necesarias para seleccionar el -simo de entre es:

El valor mximo se alcanza para .

; entonces

Algoritmo de seleccin de Rivest y FloydPodemos obtener un algoritmo cuyo coste en caso peor sea lineal si garantizamos que cada paso el pivote escogido divide el vector en dos subvectores cuya talla sea una fraccin de la talla del vector original, con coste (incluyendo la seleccin del pivote). Entonces, en caso peor,

Este es el principio del algoritmo de seleccin de Rivest y Floyd (1970).

donde . Puesto que conclumos que . Por otra parte, es bastante obvio que ; luego, .

La nica diferencia entre el algoritmo de seleccin de Hoare y el de Rivest y Floyd reside en la manera en que elegimos los pivotes. El algoritmo de Rivest y Floyd obtiene un pivote de calidad empleando el propio algoritmo, recursivamente, para seleccionar los pivotes!

El algoritmo de Rivest y Floyd calcula una pseudomediana y elige sta como pivote.

El algoritmo de Rivest y Floyd calcula una pseudomediana y elige sta como pivote. Se subdivide el subvector en bloques de elementos (excepto posiblemente el ltimo bloque), con constante e impar, y para cada uno de ellos se obtiene su mediana. El coste de esta fase es y como resultado se obtiene un vector con elementos.

El algoritmo de Rivest y Floyd calcula una pseudomediana y elige sta como pivote. Se subdivide el subvector en bloques de elementos (excepto posiblemente el ltimo bloque), con constante e impar, y para cada uno de ellos se obtiene su mediana. El coste de esta fase es y como resultado se obtiene un vector con elementos.

Sobre el vector de medianas se aplica el algoritmo de seleccin para hallar la mediana.

El algoritmo de Rivest y Floyd calcula una pseudomediana y elige sta como pivote. Se subdivide el subvector en bloques de elementos (excepto posiblemente el ltimo bloque), con constante e impar, y para cada uno de ellos se obtiene su mediana. El coste de esta fase es y como resultado se obtiene un vector con elementos.

Sobre el vector de medianas se aplica el algoritmo de seleccin para hallar la mediana. Como el pivote seleccionado es la pseudomediana de los elementos originales, ello garantiza que al menos elementos son mayores que el pivote.

en caso peor satisface Puede demostrarse que si Por lo tanto, el coste

Rivest y Floyd usaron el valor original de este algoritmo.

en su formulacin

Segmento de suma mxima Dado un vector de enteros, determinar cal es

el segmento de suma mxima en , es decir, determinar y , , tales que el segmento tiene suma mxima. Un segmento vaco ( ) es la respuesta vlida si todos los elementos de son negativos. la suma del segmento . Denotaremos : Una primera solucin ingenua tiene coste

Se puede obtener una mejora sustancial sobre la solucin previa observando que

Esta nueva solucin tiene coste . Usando divide y vencers podemos obtener la solucin de un modo ms eficiente.

Supongamos que dividimos el segmeto en curso (con ms de un elemento, en otro caso podemos dar la solucin directa) en dos mitades (o casi) y resolvemos el problema de manera independiente para cada una de ser uno ellas. El segmento de suma mxima en de los dos segmentos obtenidos recursivamente o bien un segmento no vaco que contiene elementos de las dos mitades en que se dividi , atravesando el punto de corte . . .

Para determinar el segmento de suma mxima en , siendo el punto de corte , , hemos de averiguar cal es el segmento de la forma , es decir, que necesariamente acaba en la posicin , de suma mxima en . Sea su suma y su extremo inferior (su otro extremo es, por definicin, ).

Anlogamente, deberemos averiguar cal es el segmento de la forma de suma mxima en , siendo su extremo superior y suma . Entonces el segmento de suma mxima en es el de mayor suma entre el segmento de suma mxima en , el segmento de suma mxima en , y el (su suma es ). segmento

Tanto y como sus extremos se pueden obtener tiene suma mediante dos sencillos bucles, pues mxima entre todos los segmentos de la forma , , y tiene suma mxima entre todos los que son de la forma , .

Puesto que el coste de dividir y combinar es coste del algoritmo divide y vencers es

, el

es decir, .

Pero podemos mejorar la eficiencia hasta conseguir que sea ptima, utilizando una inmersin de eficiencia. En concreto, la nueva versin de , aplicada a un segmento retornar:1

2

3

El extremo del segmento de suma mxima de entre aquellos que incluyen al extremo inferior , y su suma ; La suma

El extremo del segmento de suma mxima de entre aquellos que incluyen al extremo superior , y su suma ;

Los extremos del segmento de suma mxima y su valor ;

y

4

de

todos los elementos del segmento.

Si efectuamos las llamadas

podemos calcular los valores correspondientes a teniendo en cuenta lo siguiente: El segmento de suma mxima ser el correspondiente a

El segmento de suma mxima con extremo superior en ser el correspondiente a

El segmento de suma mxima con extremo inferior en ser el correspondiente a

. y se Los valores de los extremos , , calculan en consonancia: p.e. si es entonces , sino y por tanto .La suma del segmento es Gracias a la inmersin de eficiencia, el coste no y ya que recursivo es ahora

el coste del nuevo algoritmo es (usar elcaso 3 del Teorema 1).

1 2 3

Anlisis de Algoritmos Divide y Vencers Estructuras de Datos rboles binarios de bsqueda Colas de prioridad Algoritmos Voraces

4

rboles binarios de bsqueda

DefinicinUn rbol binario de bsqueda es un rbol binario tal que o es vaco o bien contiene un elemento y satisface1

Los subrboles izquierdo y derecho, y , respectivamente, son rboles binarios de bsqueda. Para todo elemento de , para todo elemento de ,

2

, y .

LemaUn recorrido en inorden de un rbol binario de bsqueda visita los elementos de por orden creciente de clave.

Bsqueda en BSTs

Bsqueda en BSTs (versin recursiva)

Puesto que el algoritmo es recursivo final es inmediato obtener una versin iterativa.

Bsqueda en BSTs (versin iterativa)

Insercin en BSTs (versin recursiva)

Insercin en BSTs (versin recursiva)

La versin iterativa es ms compleja, ya que adems de localizar la hoja en la que se ha de realizar la insercin, deber mantenerse un apuntador al que ser padre del nuevo nodo.

Insercin en BSTs (versin iterativa)

Slo nos queda por considerar la eliminacin de elementos en BSTs. Si el elemento ha eliminar se encuentra en un nodo cuyos dos subrboles son vacos basta eliminar el nodo en cuestin. Otro tanto sucede si el nodo a eliminar slo tiene un subrbol no vaco: basta hacer que la raz del subrbol no vaco quede como hijo del padre de .

El problema surge si hay que eliminar un nodo que contiene dos subrboles no vacos. Podemos reformular el problema de la siguiente forma: dados dos BSTs y tales que todas las claves de son menores que las claves de obtener un nuevo BST . que contenga todas las claves: Obviamente:

En particular,

.

Borrado en BSTs

Sea la mayor clave de . Puesto que es mayor que todas las dems en pero al mismo tiempo menor que cualquier clave de podemos construir colocando un nodo raz que contenga al elemento de clave , a como subrbol derecho y al resultado de eliminar de llammosle como subrbol izquierdo. Adems puesto que es la mayor clave de el correspondiente nodo no tiene subrbol derecho, y es el nodo ms a la derecha en , lo que nos permite desarrollar un procedimiento ad-hoc para eliminarlo.

Borrado en BSTs

Un razonamiento anlogo nos lleva a una versin de en la que se emplea la clave mnima del rbol para que ocupe la raz del resultado, en el caso en que y no son vacos. Se ha evidenciado experimentalmente que conviene alternar entre el predecesor y el sucesor del nodo a eliminar en los borrados (por ejemplo, mediante una decisin aleatoria o con bit que va pasando alternativamente de 0 a 1 y de 1 a 0) y no utilzar sistemticamente una de las versiones. Existen otros algoritmos de borrado pero no se estudiarn en la asignatura.

Anlisis de la eficiencia de BSTsUn BST de elementos puede ser equivalente a una lista, pues puede contener un nodo sin hijos y con slo un hijo (p.e. si insertamos una secuencia de elementos con claves crecientes en un BST inicialmente vaco). Un BST con estas caractersticas tiene altura . En caso peor, el coste en caso peor de una bsqueda, insercin o borrado en dicho rbol es por lo tanto . En general, una bsqueda, insercin en o borrado en un BST de altura tendr coste caso peor. Como funcin de , la altura de un rbol puede llegar a ser y de ah que el coste de las diversas . operaciones es, en caso peor,

Pero normalmente el coste de estas operaciones ser menor. Supongamos que cualquier orden de insercin de los elementos es equiprobable. Para bsquedas con xito supondremos que buscamos cualquiera de las claves con probabilidad . Para bsquedas sin xito o inserciones supondremos que finalizamos en cualquiera de las hojas con igual probabilidad. El coste de una de estas operaciones va a ser proporcional al nmero de comparaciones que habr que efectuar entre la clave dada y las claves de los el nmero medio de nodos examinados. Sea el nmero medio de comparaciones y comparaciones si la raz est ocupada por la -sima clave.

raz es

-sima

Otra forma de plantear esto es calcular , el valor medio de la longitud de caminos internos (IPL). Dado un BST su longitud de caminos internos es la suma de las distancias desde la raz a cada uno de los nodos.

La longitud media de caminos internos satisface la recurrencia

Esto es as porque cada nodo que no sea la raz contribuye al IPL total 1 ms su contribucin al IPL del subrbol en que se encuentre. Otra razn por la que resulta interesante estudiar el IPL medio es porque el coste de construir un BST de tamao mediante inserciones es proporcional al IPL. La recurrencia satisfecha por es idntica a la recurrencia del coste esperado de quicksort. Por lo tanto

Podemos asociar a cada ejecucin de quicksort un BST como sigue. En la raz se coloca el pivote de la fase inicial; los subrboles izquierdo y derecho corresponden a las ejecuciones recursivas de quicksort sobre los subvectores a la izquierda y a la derecha del pivote. Consideremos un subrbol cualquiera de este BST. Todos los elementos del subrbol, salvo la raz, son comparados con el nodo raz durante la fase a la que corresponde ese subrbol. Y recprocamente el pivote de una determinada fase ha sido comparado con los elementos (pivotes) que son sus antecesores en el rbol y ya no se comparar con ningn otro pivote. Por lo tanto, el nmero de comparaciones en las que interviene un cierto elemento no siendo el pivote es igual a su distancia a la raz del BST. Por lo tanto, el IPL medio coincide con el nmero medio de comparaciones que hace quicksort

Otras operacionesLos BSTs permiten otras varias operaciones siendo los algoritmos correspondientes simples y con costes (promedio) razonables. Algunas operaciones como las de bsqueda o borrado por rango (e.g. busca el elemento) o de clculo del rango de un elemento dado requieren una ligera modificacin de la implementacin estndar, de tal forma que cada nodo contenga informacin relativa a tamao del subrbol en l enraizado. Concluimos esta parte con un ejemplo concreto: dadas dos claves y , se precisa una operacin que devuelve una lista ordenada de todos los elementos cuya clave est comprendida entre las dos dadas, esto es, .

Si el BST es vaco, la lista a devolver es tambin vaca. Supongamos que el BST no es vaco y que la clave en la raz es . Si entonces todas las claves buscadas deben encontrarse, si las hay, en el subrbol derecho. Anlogamente, si se proseguir la bsqueda recursivamente en el subrbol izquierdo. Finalmente, si entonces puede haber claves que caen dentro del intervalo tanto en el subrbol izquierdo como en el derecho. Para respetar el orden creciente en la lista, deber buscarse recursivamente a la izquierda, luego listar la raz y finalmente buscarse recursivamente a la derecha.

Bsqueda por rango

Colas de prioridad

Una cola de prioridad (cat: cua de prioritat; ing: priority queue) es una coleccin de elementos donde cada elemento tiene asociado un valor susceptible de ordenacin denominado prioridad. Insercin de un elementos

Colas de prioridad

Una cola de prioridad (cat: cua de prioritat; ing: priority queue) es una coleccin de elementos donde cada elemento tiene asociado un valor susceptible de ordenacin denominado prioridad. Insercin de un elementos Consulta del elemento de mnima (o mxima) prioridad

Colas de prioridad

Una cola de prioridad (cat: cua de prioritat; ing: priority queue) es una coleccin de elementos donde cada elemento tiene asociado un valor susceptible de ordenacin denominado prioridad. Insercin de un elementos Consulta del elemento de mnima (o mxima) prioridad Eliminacin del elemento de mnima (o mxima) prioridad

Algunas aplicaciones de las colas de prioridad: Algoritmos de Kruskal y Prim para el clculo del rbol de expansin mnimo de un grafo etiquetado. Algoritmo de Dijkstra para el clculo de caminos mnimos en un grafo etiquetado. Construccin de cdigos de Huffman (cdigos binarios de longitud media mnima). Otra tareas para las que obviamente podemos usar una cola de prioridad son la ordenacin y la seleccin del -simo de un conjunto.

Ordenacin mediante una cola de prioridad

Implementacin: Listas enlazadas ordenadas por prioridad Ventajas: Consulta y eliminacin del mnimo triviales, con coste Inconvenientes: Inserciones con coste lineal, tanto en caso peor como en promedio

Implementacin: Listas enlazadas ordenadas por prioridad Ventajas: Consulta y eliminacin del mnimo triviales, con coste Inconvenientes: Inserciones con coste lineal, tanto en caso peor como en promedio

rboles de bsqueda Ventaja: Inserciones y eliminacin del mnimo con en caso peor (AVLs) o en caso coste promedio (BST)

Si el conjunto de posibles prioridades es reducido entonces ser conveniente emplear una tabla de listas, correspondiendo cada lista a una prioridad o intervalo reducido de prioridades.

Si el conjunto de posibles prioridades es reducido entonces ser conveniente emplear una tabla de listas, correspondiendo cada lista a una prioridad o intervalo reducido de prioridades. En lo que resta estudiaremos una tcnica especfica para la implementacin de colas de prioridad basada en los denominados montculos.

DefinicinUn montculo (ing: heap) es un rbol binario tal que1

todos las hojas (subrboles son vacos) se sitan en los dos ltimos niveles del rbol. en el penltimo nivel existe a lo sumo un nodo interno con un slo hijo, que ser su hijo izquierdo, y todos los nodos a su derecha en el mismo nivel son nodos sin hijos. el elemento (su prioridad) almacenado en un nodo cualquiera es mayor (menor) o igual que los elementos almacenados en sus hijos izquierdo y derecho.

2

3

Se dice que un montculo es un rbol binario quasi-completo debido a las propiedades 1-2. La propiedad 3 se denomina orden de montculo, y se habla de max-heaps o min-heaps segn que los elementos sean ques sus hijos. En lo sucesivo slo consideraremos max-heaps.

De las propiedades 1-3 se desprenden dos consecuencias importantes:1 2

El elemento mximo se encuentra en la raz. Un heap de

La consulta del mximo es sencilla y eficiente pues basta examinar la raz.

.

elementos tiene altura

Cmo eliminar el mximo? Un procedimiento que se emplea a menudo consiste en ubicar al ltimo elemento del montculo (el del ltimo nivel ms a la derecha) en la raz, sustituyendo al mximo; ello garantiza que se preservan las propiedades 1-2. Pero como la propiedad 3 deja eventualmente de satisfacerse, debe reestablecerse el invariante para lo cual se emplea un procedimiento privado denominado . ste consiste en intercambiar un nodo con el mayor de sus dos hijos si el nodo es menor que alguno de ellos, y repetir este paso hasta que el invariante se haya reestablecido.

Cmo aadir un nuevo elemento? Una posibilidad consiste en colocar el nuevo elemento como ltimo elemento del montculo, justo a la derecha del ltimo o como primero de un nuevo nivel. Para ello hay que localizar al padre de la primera hoja y sustituirla por un nuevo nodo con el elemento a insertar. A continuacin hay que reestablecer el orden de montculo empleando para ello un procedimiento , que trabaja de manera similar pero a la inversa de : el nodo en curso se compara con su nodo padre y se realiza el intercambio si ste es mayor que el padre, iterando este paso mientras sea necesario.

Puesto que la altura del heap es el coste de inserciones y eliminaciones es . Se puede implementar un heap mediante memoria dinmica. La representacin elegida debe incluir apuntadores al hijo izquierdo y derecho y tambin al padre, y resolver de manera eficaz la localizacin del ltimo elemento y del padre de la primera hoja.

Una alternativa atractiva es la implementacin de heaps mediante un vector. No se desperdicia demasiado espacio ya que el heap es quasi-completo. Las reglas para representar los elementos del heap en un vector son simples:1 2

3

contiene la raz. Si entonces contiene al hijo izquierdo y si entonces del elemento en contiene al hijo derecho de . Si div entonces div contiene al padre de .

Implementacin mediante heaps

Implementacin mediante heaps

Implementacin mediante heaps (cont.)

Hundir (versin recursiva)

Flotar (versin iterativa)

HeapsortHeapsort (Williams, 1964) ordena un vector de elementos construyendo un heap con los elementos y extrayndolos, uno a uno del heap a continuacin. El propio vector que almacena a los elementos se emplea para construir el heap, de modo que heapsort acta in-situ y slo requiere un espacio auxiliar de memoria constante. El coste de este algoritmo es (incluso en caso mejor) si todos los elementos son diferentes. En la prctica su coste es superior al de quicksort, ya que el factor constante multiplicativo del trmino es mayor.

Heapsort

Crea_heap

Sea el coste en caso peor de y el coste de crear el heap inicial. El coste en caso peor de es y por lo tanto

Un anlisis grueso de indica que ya que hay llamadas a , cada una de las cuales tiene coste . Podemos concluir por tanto que . No es difcil construir una entrada de y por tanto tamao tal que en caso peor. La demostracin de que en caso mejor1 es tambin el coste de es bastante ms complicada.

1

Siendo todos los elementos distintos.

Por otra parte, la cota dada para refinarla ya que

podemos

Puesto que .

, podemos afirmar que

Otra forma de demostrar que es lineal consiste la en razonar del siguiente modo: Sea altura del heap. En el nivel hay como mucho

nodos y cada uno de ellos habr de hundirse en caso peor hasta el nivel ; eso tiene coste .

Por lo tanto,

ya que

En general, si

,

Aunque globalmente , es interesante el anlisis detallado de . Por ejemplo, utilizando un min-heap podemos hallar los menores elementos de un vector (y en particular el -simo) con coste:

y si

entonces .

1 2 3 4

Anlisis de Algoritmos Divide y Vencers Estructuras de Datos Algoritmos Voraces

Algoritmo de DijkstraDado un grafo dirigido etiquetado, el algoritmo de Dijkstra (1959) nos permite hallar los caminos mnimos desde un vrtice dado a todos los restantes vrtices del grafo. Si el grafo contuviera ciclos de peso negativo la nocin de camino mnimo no estara bien definida para todo par de vrtices, pero el algoritmo de Dijkstra puede fallar en presencia de arcos con peso negativo, incluso si no hay ciclos de peso negativo. Supondremos son por lo tanto que todos los pesos positivos. Sea el conjunto de caminos entre dos vrtices y de . Dado un camino su peso es la suma de los pesos de los arcos que lo forman:

Sea y un camino de cuyo peso es mnimo. Si entonces , por convenio. Consideraremos tomamos en primer lugar la versin del algoritmo de Dijkstra que calcula los pesos de los caminos mnimos desde un vrtice a todos los restantes, y ms tarde la versin que calcula adicionalmente los caminos propiamente dichos.

El algoritmo de Dijkstra acta en una serie de etapas. En todo momento el conjunto de vrtices se divide en dos partes: los vrtices vistos y los vrtices no vistos o candidatos. Al inicio de cada etapa, si es un ; si es un vrtice visto entonces candidato entonces es el peso del camino mnimo entre y que pasa exclusivamente por vrtices intermedios vistos. Este es el invariante del algoritmo de Dijkstra. En cada etapa un vrtice pasa de ser candidato a ser visto. Y cuando todos los vrtices son vistos entonces tenemos completamente resuelto el problema.

Qu vrtice candidato debe seleccionarse en cada etapa para pasar a ser visto? Intuitivamente, aqul cuya sea mnima de entre todos los candidatos. Sea dicho vrtice. Entonces no slo es el peso del camino mnimo entre y que slo pasa por vistos (segn el invariante), sino el peso del camino mnimo. En efecto, si el camino mnimo pasase por algn otro vrtice no visto , tendramos que el peso de dicho , pero como y camino es es mnimo llegamos a una contradiccin.

Ahora debemos pensar cmo mantener el resto del ya tiene el valor invariante. Para los vrtices vistos adecuado (includo , el vrtice que pasa de candidatos a vistos, como acabamos de ver). Pero si es un tal vez haya de cambiar ya que tenemos candidato, un vrtice adicional visto, el vrtice . Un sencillo razonamiento demuestra que si el camino mnimo entre y que pasa por vrtices vistos incluye a entonces es el inmediato antecesor de en dicho camino.

De ah se sigue que el valor de slo puede cambiar para los vrtices sucesores de . Esto ocurrir si y slo si

Si fuera sucesor de pero ya estuviera visto la condicin anterior no puede ser cierta.

CANDIDATOS

Para conseguir que el algoritmo compute los caminos mnimos propiamente dichos, empezamos observando que si el camino mnimo entre y pasa por entonces la parte de ese camino que nos lleva de a ha de ser necesariamente el camino mnimo entre y . Por lo tanto, bastar con computar un rbol de caminos mnimos que implcitamente se representa mediante una tabla tal que:

donde

si si si

, es

el ltimo arco de ,

,

indica que

no est definido.

Claramente, los nicos cambios que sern necesarios son:1 2

inicializar . incluir la actualizacin de

en el bucle interno:

Si queremos el camino completo de deshacer el recorrido a la inversa:

a

podemos

hasta que lleguemos a .

Sea el nmero de vrtices de y el nmero de arcos. Si el grafo se implementa mediante matriz de adyacencias, el coste del algoritmo de Dijkstra es ya que se hacen iteraciones del bucle principal y dentro de cada una de ellas se incurrira en un coste como mnimo para recorrer los sucesores del vrtice seleccionado. Descartaremos esta posibilidad, y supondremos que el grafo se implementa mediante listas de adyacencia que nos darn mejores resultados tanto en tiempo como en espacio.

Supongamos, para simplificar, que y que implementamos el conjunto y el diccionario mediante sencillas tablas indexadas de a ( si y slo si el vrtice es un candidato). Entonces el coste del algoritmo de Dijkstra es puesto que se hacen iteraciones del bucle principal, buscar el mnimo de la tabla en cada y actualizar la tabla tiene iteracin tiene coste coste proporcional al nmero de sucesores del vrtice seleccionado (combinando la obtencin de los vrtices sucesores y la etiqueta de los arcos correspondientes).

Si es un conjunto arbitrario, podemos conseguir el mismo rendimiento utilizando tablas de hash para implementar y . Y el problema sigue siendo la mnima. ineficiencia en la seleccin del vrtice con Para mejorar la eficiencia podemos convertir en una cola de prioridad cuyos elementos son vrtices y donde la prioridad de cada vrtice es su correspondiente valor de :

El problema es que dentro del bucle que recorre los (la sucesores de se puede modificar el valor de prioridad) de vrtices candidatos. Por ello deberemos dotar al TAD de una operacin adicional que, dado un elemento, nos permita decrementar su se modifican siempre a la prioridad (los valores de baja).

Si los costes de las operaciones sobre la cola de prioridad son entonces el coste del bucle principal es

# sucesores de # sucesores de

Por otra parte, el coste de las inicializaciones previas es . Por lo tanto el coste que nos queda es . Puede demostrarse que, de hecho, el coste en caso . Pero peor del algoritmo de Dijkstra es en muchos casos el coste es menor, porque no hay que usar para todos los sucesores del vrtice seleccionado y/o porque el coste de las operaciones sobre la cola de prioridad es frecuentemente menor . que

Al crear sabemos cuntos vrtices tiene el grafo y podemos crear dinmicamente una estructura de datos para ese tamao. Adicionalmente podemos evitar la redundancia de la tabla , ya que para cada elemento de tenemos su prioridad, que es su valor de .

Necesitamos por lo tanto un TAD con la funcionalidad combinada tpica de las colas de prioridad y de los diccionarios:

Pueden conseguirse costes logartmicos o inferiores en todas las operaciones sobre la cola de prioridad utilizando un heap implementado en vector. Adems necesitaremos una tabla de hash que nos permita traducir vrtices a ndices. Y una tabla de ndices y otra de posiciones en el heap.

Las tablas y , junto al contador representan al heap. Si entonces y , es decir, nos da la posicin en el heap del elemento e su ndice, siendo .

La operacin reclama a la memoria dinmica las tablas , , y con componentes y crea la tabla de hash. Cada vez que se inserta un nuevo , se inserta en elemento se le asigna el ndice , se coloca en el heap en la posicin , y se le hace flotar. La operacin se encarga de mantener actualizadas las tablas e . Para , se intercambia el ltimo nodo del heap con la raz

A continuacin se hunde la raz, cuidando de mantener actualizadas las tablas e .

La operacin requiere averiguar el ndice del elemento cuya prioridad se va a decrementar usando la tabla de hash, usar la tabla para obtener la posicin del nodo que le corresponde en el heap, y una vez modificada la prioridad, flotar el nodo. Es fcil comprobar que las operaciones del TAD, ( , , , exceptuando , tienen coste ( , , ). ) o