Preliminares del problema Sorting

Anuncio
Apuntes de Teórico
PROGRAMACIÓN 3
Complejidad
Versión 2.1
Apuntes de Teórico de Programación 3 – Complejidad
2
Apuntes de Teórico de Programación 3 – Complejidad
Índice
COMPLEJIDAD DE PROBLEMAS - Introducción.....................................4
Introducción a Árboles de decisión....................................................7
Presentación del árbol de decisión para el Selection Sort ............................................. 7
Una implementación.................................................................................................... 7
Árbol de decisión para Selection Sort .......................................................................... 9
Introducción ................................................................................................................ 9
Construcción ............................................................................................................... 9
Presentación del árbol de decisión para el Insertion Sort............................................ 15
Una implementación.................................................................................................. 15
Árbol de decisión para Insertion Sort......................................................................... 16
Conclusiones en base a los árboles de decisión construídos ...................................... 19
Comentarios sobre los ejemplos realizados ..............................................19
Árbol de decisión, árbol binario estricto .......................................... 20
Propiedades de Árbol Binario Estricto (ABE)............................................................ 21
3
Apuntes de Teórico de Programación 3 – Complejidad
COMPLEJIDAD DE PROBLEMAS - Introducción
En el Análisis de Algoritmos se estudian algoritmos y su costo en el PEOR CASO y
CASO MEDIO.
Dado un problema hay algoritmos que lo resuelven. Se observa en la realidad que para
cualquier problema, podrán buscarse algoritmos mejores (en costo) pero llegará un punto
en que no será posible mejorar más. Es a través del análisis de algoritmos que se
determina la respuesta a la pregunta ¿hasta dónde se pueden mejorar los algoritmos que
lo resuelven? También ante un problema dado es razonable plantearse antes que la
anterior, la pregunta ¿es posible saber de antemano cual sería el costo mínimo que
tendría cualquier algoritmo que pueda construirse para resolver el problema? Estas
preguntas son válidas porque todo problema tiene una complejidad mínima asociada.
El objetivo en lo que sigue es llegar a obtener expresiones para el costo que sirvan para
responder los planteos mencionados.
Como en el análisis de algoritmos, se utilizarán elementos como el tamaño de la entrada,
conteo de operaciones, que en este contexto se limitará a operaciones básicas, estudiando
Peor caso y Caso Medio.
En otras palabras, se buscará una función, que se denotará por F(n), que
inferiormente la cantidad de operaciones básicas para:
•
Peor Caso:
•
Caso Medio: FA(n)
acote
FW(n)
del problema considerado. F se denominará cota inferior en cada caso.
Reformulando
Sea el problema P del que se quiere estudiar la complejidad. Por el momento lo que se
indique vale tanto para el peor caso como para el caso medio:
Existe un conjunto A = {A1, A2, …, Am} de algoritmos que brindan una solución al
problema P. Sea T el conjunto de costos (en el peor caso o caso medio) para cada
algoritmo, T = {T1, T2, …, Tm} (siendo Ti el costo para el algoritmo Ai) .
Se deben encontrar expresiones F (n) tales que Ti(n) ≥ F(n), 1≤i≤m.
Lo anterior se debe cumplir para todo algoritmo que solucione el problema, tanto los
conocidos como los todavía desconocidos, el conjunto A contiene a todos, m es
arbitrario y se usa sólo a efectos ilustrativos. Esto sólo puede lograrse con un estudio
teórico independiente de alguna implementación particular, en base a las características
del problema.
Al buscar la función F, se debe procurar además que sea lo mayor posible (entre todas
las posibles cotas inferiores).
De manera grafica: Sea el problema P con algoritmos conocidos A = {A1, A2, A3, A4} y
costos T = {n, nlog(n), n2, 2n}. Entonces una cota mínima para todos estos algoritmos
4
Apuntes de Teórico de Programación 3 – Complejidad
podría ser log(n). Si se demuestra además que independientemente de los algoritmos,
esta cota se cumple para todo algoritmo que resuelva el problema, servirá como la
posible F que se busca. La demostración mencionada se hace en base a especificaciones
propias del problema que se considera.
Siguiendo con este ejemplo, si por otra vía se determina que otra función mayor que log
n también es cota inferior para el problema (p.e. F= log n2 o F=n), se determina que la
mayor será la cota buscada.Tiempos de ejecución
35
30
25
T(n)
20
15
10
5
0
1
2
3
4
5
n
Lineal
log(n)
n*log(n)
n^2
2^n
Ejemplo: Encontrar el mínimo de una secuencia de enteros mediante comparaciones de
elementos de la secuencia dos a dos.
Estudiar la complejidad del problema anterior seria equivalente a responder la pregunta:
¿Cuántas comparaciones deben realizarse como mínimo para encontrar el mínimo en la
secuencia de enteros mediante comparaciones dos a dos?
Replanteando el problema anterior: Encontrar el mínimo de una secuencia de enteros
mediante comparaciones dos a dos es equivalente a determinar los (n-1) elementos que
no son el mínimo.
Se tiene entonces la siguiente información del problema simétrico:
1. Para “descartar” un elemento, debe figurar al menos una comparación (para
este elemento)
2. Si un elemento x no es comparado nunca entonces, este podría ser el mínimo.
De lo anterior se concluye que si cada uno de los n-1 elementos que no son el mínimo
tienen que ser comparados por lo menos una vez para poder ser descartados, entonces
deben realizarse al menos n-1 comparaciones para descartarlos a todos.
Formalizando:
•
FW(n) = n-1
•
FA(n) = n-1
5
Apuntes de Teórico de Programación 3 – Complejidad
¿Cómo se puede determinar si la cota F(n) encontrada es suficientemente buena o se
puede mejorar?
Una manera es encontrar un algoritmo Ak cuyo costo coincida con la cota.
Informalmente hablando, al encontrar un algoritmo que resuelve el problema con ese
costo, no puede existir otra cota “mejor” porque la nueva función encontrada sería
mayor que Tk(n).
Para tratar estos temas se usará fundamentalmente notación asintótica.El caso anterior
se expresará como Tk(n) ∈ Θ(F(n)).
Entonces F(n) es la mejor cota inferior y también se dirá que el algoritmo Ak es
óptimo en el caso que se esté considerando (medio o peor).
Para analizar la complejidad de los problemas en lo que sigue se utilizará como caso de
estudio el problema de SORTING.
Como se ha mencionado más arriba, el estudio debe hacerse en forma teórica y
genérica sin atarse a algoritmos concretos y por lo tanto antes de entrar concretamente
al problema SORTING se presentará una herramienta que permitirá hacer deducciones
teóricas sobre su complejidad: el árbol de decisión (de los algoritmos).
6
Apuntes de Teórico de Programación 3 – Complejidad
Introducción a Árboles de decisión
Para proceder al análisis de un problema, donde se tratará de determinar la complejidad
mínima del mismo, se presentará previamente la herramienta teórica a usar con ese fin:
el árbol de decisión. El árbol de decisión será el modelo matemático que representará
las diferentes ejecuciones de los algoritmos en función de su entrada.
Se realizará en principio una presentación informal, ejemplificando la herramienta para
dos algoritmos conocidos y luego se formalizará.
Los siguientes algoritmos ya son conocidos del práctico 2 del curso y se tratan de la
ordenación por Selección (Selection Sort) y la ordenación por Inserción (Insertion Sort).
La idea es modelar todas las posibles ejecuciones de un algoritmo cualquiera sea la
secuencia de entrada. Su construcción será como una ejecución “a mano” del algoritmo
dibujando un árbol binario.
Presentación del árbol de decisión para el Selection Sort
Este algoritmo ordena una secuencia de n enteros en orden ascendente. Éste localiza el
valor más pequeño, lo coloca en el primer lugar, luego localiza el segundo valor más
pequeño y lo ubica en el segundo lugar y así sucesivamente.
La especificación queda:
Entrada: Secuencia S a ordenar
Salida: Secuencia R ordenada
Proceso
R = Crear()
Mientras NOT Vacia(S) hacer
R = InsBack(R, Minimo(S))
S = DeleteMin(S)
Fin Mientras
Devolver R
Una implementación1
Se utilizará la opción de implementar la secuencia y el resultado ordenado sobre un
único arreglo A de n elementos. El arreglo se considerará dividido en dos partes, la
izquierda contendrá la parte ordenada, la derecha contendrá la parte desordenada. En
cada paso se buscará el menor elemento de la desordenada y se intercambiará con el
primer elemento de ésta, creciendo así la parte ordenada y decreciendo la desordenada
(en tamaño).
1
Se revisará el algoritmo paso a paso como apoyo a la construcción del árbol de decisión a
posteriori. No se revisará así en la clase por ser un tema ya visto en el práctico.
7
Apuntes de Teórico de Programación 3 – Complejidad
void SelectionSort (int* A, int n){
int i, j, posmin, tmp;
(1) for (i = 0; i < n-1; i++){
posmin = i;
(2)
for (j = i+1; j < n; j++){
(3)
if (A[j] < A[posmin])
posmin = j;
}
// Intercambio de elementos
tmp = A[i];
A[i] = A[posmin];
A[posmin] = tmp;
}
}
En esta implementación las secuencias S y R se implementan sobre el mismo arreglo A,
el cual contiene al principio la secuencia S y al final la R.
En el desarrollo de la ejecución se da que:
• La secuencia R estará al comienzo del arreglo, empezando en la posición señalada por
la variable i.
• Inicialmente R es vacía, pero irá creciendo según el ciclo (1).
• En el ciclo (2) se busca el mínimo en S, notar que S comienza en la posición i+1 de A y
ocupa hasta la posición n-1 (final del arreglo).
• Al finalizar el ciclo (2) queda en posmin la posición donde se encuentra el mínimo en
cada iteración.
• Finalmente se intercambian las posiciones A[i] con A[posmin], comenzando otra
iteración de (1), creciendo así R y achicándose S.
Observar que, aprovechando la estrategia seguida, en el ciclo (1) sólo se llega hasta la
posición n-2, el restante elemento tiene, necesariamente, que ser el mayor y quedar al
final de la ejecución en la posición n-1.
Para analizar el algoritmo se quiere contar únicamente la cantidad de comparaciones
entre los elementos de la secuencia, o sea cuantas veces se evalúa la condición del if (3).
En el ciclo (2) dicha condición se evalúa (n - i - 1) veces. Éste ciclo se ejecuta (n-1)
veces (de 0 a n-2) debido al ciclo (1): Por tanto, el tiempo de ejecución está dado por la
siguiente sumatoria
i =n −2
i =n −2
 j =n−1  i=n−2


(
1
)
=
(
n
−
i
−
1
)
=
(
n
−
1
)
−
(i )
∑
∑
∑
∑  ∑
i =0  j =i +1
i =0
i =0
 i =0
(n − 1).(n − 2) 2n 2 − 4n + 2 − n 2 + 3n − 2 n 2 − n
2
= (n − 1) −
=
=
2
2
2
n(n − 1)
⇒ T ( n) =
⇒ T ( n) ∈ Ο( n 2 )
2
T ( n) =
i =n −2
8
Apuntes de Teórico de Programación 3 – Complejidad
Ambas expresiones obtenidas son válidas según el contexto:
Se tienen n(n-1)/2 comparaciones si lo que se quiere obtener es el conteo exacto. Sin
embargo, este valor es O(n2) si nos alcanza con una cota superior del mismo.
Este valor de T(n) se da para toda entrada debido a que se hace una búsqueda del
mínimo en cada iteración y eso hace que las comparaciones se hagan
independientemente de los valores que contenga la secuencia. Por lo tanto el T(n)
hallado vale para el peor caso, mejor caso y para el caso medio.
Árbol de decisión para Selection Sort
Introducción
Como ya se mencionó, la idea es modelar todas las posibles ejecuciones del algoritmo
cualquiera sea la secuencia de entrada. Su construcción será como una ejecución “a
mano” del algoritmo dibujando un árbol binario.
Cada nodo del árbol corresponderá a una comparación de elementos A[j] <
A[posmin]; esto se hará desde el comienzo de la ejecución del algoritmo, con los
valores iniciales de j y posmin. Se tendrán dos resultados posibles según la respuesta a
la comparación, en cada caso se adecuará el arreglo según lo que indica el algoritmo y se
volverá a realizar lo anterior con los nuevos valores de j y posmin, agregando los
nodos correspondientes a las comparaciones. Al terminar con las comparaciones se
colocarán como hojas del árbol (nodos externos) rectángulos con los elementos
ordenados.
Construcción 2
A continuación se verá el árbol de decisión para este algoritmo en un ejemplo para n=3.
Inicialmente se tiene que en el arreglo vienen dados 3 elementos con valores a, b y c en
las posiciones A0, A1 y A2 respectivamente.
En el árbol los óvalos representan las comparaciones, los rectángulos coloreados son las
diferentes variaciones de A, agregándose además los valores de i, posmin y j en
los distintos pasos.
2
Se realizará una descripción muy detallada de la construcción del árbol para ayudar a la
comprensión del proceso por parte del estudiante. No se verá con ese nivel de detalle en clase.
9
Apuntes de Teórico de Programación 3 – Complejidad
Esta primer figura muestra el árbol parcial con su rama de más a la derecha: inicialmente
se tiene que el primer nodo (raiz) realiza la comparación A[1] < A[0], esto se debe a que j
= 1 y posmin = i = 0. Notar que ésta comparación equivale a comparar los elementos de
la secuencia a y b: ¿ a < b ? ya que el arreglo inicial es [abc].
Si esta comparación da verdadero, se toma a la derecha en el árbol, el valor de posmin
cambia a 1 y el de j a 2 (ver algoritmo). En estas condiciones continúa el ciclo (2), la
comparación es entre los elementos 2 y 1 de A (¿c < b? ). Si la respuesta es afirmativa,
se cambia posmin al valor de j que es 2, termina el ciclo (2) y se realiza el
intercambio, ya se encontró el mínimo que está en la posición 2, o sea que c es el menor
elemento y se cambia al primer lugar (i = 0). Notar que c no se accederá más, es el
primer elemento de la parte ordenada del arreglo.
Comienza un nuevo ciclo (1) con i=1, entonces posmin=1 y j=2. La siguiente
comparación es entre los elementos 2 y 1 del arreglo (ya modificado) o sea ¿a < b?. Si
la comparación da verdadero, entonces posmin = 2, termina el ciclo (2), se hace el
intercambio quedando [cab] y el algoritmo termina, [cab] será la ordenación final. Si la
respuesta es falso, el intercambio mantiene el elemento en su lugar y la secuencia final
ordenada será [cba].
10
Apuntes de Teórico de Programación 3 – Complejidad
Yendo hacia “atrás” en la ejecución, esta segunda figura muestra el árbol resultado de
una evaluación negativa de la segunda comparación inmediata antes del intercambio.
Esta respuesta hace que posmin no cambie y por lo tanto que el mínimo del arreglo
sea el segundo elemento, A[1] o sea b. Como antes, éste será el primer elemento de la
parte ordenada y ya no cambiará, restando definir si le sigue a o c. Después del
intercambio, se inicia otro ciclo (1) (como en el primer caso), se tendrá posmin=i=1,
j=2 y el arreglo [bac]. Se comparan los elementos 2 y 1; si el resultado es afirmativo,
posmin pasa a valer 2, se intercambian elementos y termina con la ordenación final
[bca]. Por el contrario, si la respuesta es negativa, el intercambio se realiza pero no
mueve efectivamente elementos, quedando [bac].
11
Apuntes de Teórico de Programación 3 – Complejidad
Volviendo al inicio de la ejecución del algoritmo, se verán ahora los casos en que la
respuesta a la primera comparación (raíz) fue negativa. En ese caso no cambia posmin
y al continuar el ciclo (2) con j = 2, compara el elemento 2 con el elemento 0 (¿ c < a?
). Si da verdadero, posmin = 2 y termina el ciclo (2), se realiza el intercambio
quedando el arreglo [cba]: el mínimo es c y ya no cambiará. Se inicia un nuevo paso del
ciclo (1), posmin = i = 1, j = 2. La siguiente comparación es entre los elementos 2 y 1
(¿a < b?), si es verdadero, posmin = 2, termina el ciclo (2) y se hace el último
intercambio quedando [cab]. Si el resultado da negativo, queda [cba] sin cambios.
12
Apuntes de Teórico de Programación 3 – Complejidad
Esta cuarta figura muestra la rama de más a la izquierda del árbol que corresponde a
todas respuestas negativas. Con respecto al caso anterior implica volver un paso atrás y
considerar el caso en que la segunda comparación dio negativo. En ese caso no cambia
posmin (como siempre que la respuesta a la comparación sea negativa). El mínimo
entonces es a, que se cambiará al primer lugar (en realidad el intercambio lo deja en el
mismo lugar). El arreglo queda como el inicial [abc]. Comienza otra iteración de (1)
posmin=i=1, j=2 y se comparará el elemento 2 con el 1. Si la respuesta es afirmativa,
posmin=2, se intercambia, termina el algoritmo resultando la secuencia ordenada [acb].
Si la respuesta es negativa, posmin no cambia, el intercambio deja el elemento en el
mismo lugar quedando la secuencia ordenada [abc], la cual corresponde al caso en que la
secuencia inicial ya venía ordenada.
13
Apuntes de Teórico de Programación 3 – Complejidad
Esta figura muestra el árbol completo. No se muestran todos los valores intermedios por
simplicidad. Notar que aquí se representan TODAS las posibles ejecuciones del
algoritmo. Para una entrada particular, por ejemplo [3,1,2] el algoritmo recorrerá un
único camino de la raíz a una hoja. En el ejemplo terminará en el nodo externo (hoja) 6
que representa el caso en que el menor elemento es el que viene inicialmente en el
segundo lugar, el siguiente es el tercero y el último es el primero.
Nota:
El nodo 8 no puede darse nunca. La primer comparación (¿b < a?) resulta afirmativa,
entonces no puede resultar afirmativa la última comparación que lleva a que a sea menor
que b.
Los nodos externos 3 y 7 representan [cba] en ambos casos, pero en el nodo 7 se
representan los casos donde b<a y el caso del nodo 3 sólo puede representar los casos
donde a=b (por construcción). Si no se admiten repetidos, el caso del nodo 3 no puede
darse nunca (por las comparaciones anteriores como en el caso del nodo 8).
14
Apuntes de Teórico de Programación 3 – Complejidad
Presentación del árbol de decisión para el Insertion Sort
Este algoritmo ordena una secuencia de n enteros en orden ascendente mediante una
estrategia que considera dos secuencias: origen y resultado. En cada paso se toma el
primero de la secuencia a ordenar y se busca su ubicación en el resultado de forma que
éste se mantenga ordenado.
La especificación queda:
Entrada: Secuencia S a ordenar
Salida: Secuencia R ordenada
Proceso
R = Crear()
Mientras NOT Vacia(S) hacer
R = InsEnOrden(R, Primero(S))
S = Resto(S)
Fin Mientras
Devolver R
Una implementación
Como en el caso de Selection Sort, se utilizará un algoritmo que reciba la secuencia en
un arreglo de n elementos y proceda a ordenar con esta estrategia sobre el mismo
arreglo. Para ello se considerará el arreglo dividido conceptualmente en dos partes, una,
la de más a la izquierda (o de menores índices) ya ordenada y el resto será la secuencia
desordenada. La idea consiste en tomar el primer elemento de la parte desordenada e irlo
desplazando a la izquierda hasta insertarlo en su lugar. Para ello se deberá al mismo
tiempo desplazar a la derecha los elementos mayores a él que forman la parte ordenada
de manera de hacer lugar en el arreglo para ese elemento a insertar.
En el algoritmo siguiente se recibe el arreglo A, se usan las variables: First para ubicar
transitoriamente el primer elemento de la parte desordenada, o sea el elemento a insertar
en la parte ordenada, i para indicar la posición de comienzo de la parte desordenada y j
para buscar la ubicación de First en la parte ya ordenada (desde i-1 a la izquierda).
void InsertionSort (int* A, int n){
int i, j, First;
(1)for (i = 1; i < n; i++){
First = A[i];
j = i-1;
(2)
while (j >= 0 && First < A[j]){
A[j+1] = A[j];
j--;
}
A[j+1] = First;
}
}
15
Apuntes de Teórico de Programación 3 – Complejidad
Como se comentó, en esta implementación las secuencias S y R se implementan sobre el
mismo arreglo A, el cual contiene en su parte izquierda la secuencia S y en la derecha la
R.
En el desarrollo de la ejecución se da que:
• La secuencia R estará al comienzo del arreglo, inicialmente R es vacía, por lo tanto no
ocuparía lugar en el arreglo, sin embargo aprovechando la estrategia, puede considerarse
que contiene un elemento desde el comienzo, éste será A[0], luego irá creciendo según el
ciclo (1). La secuencia S empezará en la posición señalada por la variable i, inicialmente
con el valor 1, acorde con lo anterior.
• En el ciclo (2) se busca la posición de First en R: notar que S comienza en la posición i
de A y ocupa hasta la posición n-1 (final del arreglo), mientras que R comienza en 0 y
termina en la posición i-1.
• Al finalizar el ciclo (2) j se ha pasado de la posición donde debe insertarse First y por
eso termina el ciclo (puede ser porque haya encontrado un lugar intermedio o que sea el
menor de R y tenga que colocarse en la posición 0, por eso se admite que j llegue a
valer -1.
• Finalmente se coloca First en la posición j+1, de acuerdo a lo anterior.
Para analizar el algoritmo se quiere contar únicamente la cantidad de comparaciones
entre los elementos de la secuencia, o sea cuantas veces se evalúa la segunda parte de la
condición del while (2): First < A[j].
El Peor Caso viene dado cuando se tiene que colocar al principio del arreglo cada
elemento a ordenar; en ese caso se realizarán en cada paso i comparaciones.
n.(n + 1)
n 2 − n n(n − 1)
T ( n) = ∑ (i ) =
−n=
=
2
2
2
i =1
i = n −1
En este algoritmo se tiene también que el peor caso es Ο(n2).
Árbol de decisión para Insertion Sort
A continuación se verá el árbol de decisión para este algoritmo. Como antes, la idea es
modelar todas las posibles ejecuciones del algoritmo cualquiera sea la secuencia de
entrada. Se seguirá el mismo método de construcción antes utilizado.
En este caso, cada nodo interno del árbol corresponderá a una comparación de elementos
de la forma First < A[j]; esto se hará desde el comienzo de la ejecución del
algoritmo, con los valores iniciales de i, j y First. Como antes, de acuerdo al
resultado, se actualizarán las variables y se realizarán los cambios especificados en el
algoritmo. Notar que se compara con First, al que se abreviará como F en la figura
que almacena un valor fijo mientras varía j.
Otra vez se estudiará este ejemplo para n=3.
Inicialmente se tiene que en el array vienen dados 3 elementos con valores a, b y c en las
posiciones A0, A1 y A2 respectivamente.
16
Apuntes de Teórico de Programación 3 – Complejidad
En este caso no se verá en la forma detallada del caso del Selection Sort, en la figura se
muestra el árbol final que ilustra o modela todas las ejecuciones posibles del Insertion
Sort.
Se seguirá paso a paso sólo la rama derecha del árbol:
Inicialmente i=1, First=A[1]=b, j=0; el arreglo es [abc]. Eso hace que la comparación
que quede en el nodo raíz sea F<A[0] o sea b < a. Por la rama SI, se entra al while (2), se
mueve el primer elemento al segundo lugar quedando el arreglo [aac], j pasa a valer -1
por lo cual termina la iteración. Se coloca First en el lugar 0, entonces queda el arreglo
como [bac].
Comienza una nueva iteración (1) por lo tanto i=2, First = A[2] = c, j=1; el arreglo es el
que había quedado del paso anterior [bac]. La siguiente comparación es F < A[1] o sea c
< a. Por la rama SI, se entra al while (2), se mueve el segundo elemento al tercer lugar
quedando el arreglo como [baa], j pasa a valer 0 por lo que continúa la iteración (2). La
siguiente comparación es F < A[0] o sea c < b. Por la rama SI, se vuelve a entrar al ciclo
(2), se mueve el primer elemento al segundo lugar quedando el arreglo como [bba] y j
pasa a valer -1 con lo cual termina la iteración (2). Se coloca First en el lugar 0,
quedando el arreglo como [cba]. Se llegó así a un nodo externo (hoja) que representa el
final de la ordenación.
De manera similar se procederá con el resto del árbol, analizando las restantes
variaciones en la ejecución del algoritmo según el resultado de las comparaciones.El
árbol correspondiente a este algoritmo (mostrado en la figura) tiene 6 nodos externos.
Obsérvese que esta cantidad de hojas es la mínima que puede tener un árbol bien
construído ya que debe reflejar todas las posibles ejecuciones para todas las secuencias
17
Apuntes de Teórico de Programación 3 – Complejidad
de 3 elementos que se puedan formar con a, b y c, o sea todas las permutaciones de 3
elementos : 3! = 6.
18
Apuntes de Teórico de Programación 3 – Complejidad
Conclusiones en base a los árboles de decisión construidos
De acuerdo a lo visto, un Árbol de Decisión es un árbol binario estricto (cada nodo tiene
2 hijos o ninguno) donde los nodos internos tienen 2 hijos y los externos (hojas) no
tienen ninguno. Estos árboles pueden ser usados en general para modelar la ejecución de
algoritmos que resuelvan un problema dado. En este marco, los nodos internos
representan operaciones y los externos representan el final de la ejecución de un
algoritmo.
Como herramienta para modelar algoritmos de ordenación, los nodos internos
representan comparaciones entre elementos de la secuencia a ordenar y los nodos
externos representan el estado final de la ordenación.
Todo algoritmo de ordenación tendrá asociado su árbol de decisión particular. Una
ejecución del algoritmo, para una determinada entrada, está representado por el camino
de la raiz a un único nodo externo. El costo, en cantidad de comparaciones para dicha
entrada, estará dado por el largo de dicho camino. De esta forma el peor caso
corresponderá a la profundidad del árbol (camino más largo de la raiz a un nodo
externo).
Otro elemento a notar es que todo árbol de decisión para el problema de ordenamiento
tendrá como mínimo n! nodos externos y el máximo se dará cuando el árbol sea
completo. Esta propiedad se usará para el problema planteado de determinar la
complejidad del problema de ordenación (Sorting).
Se adoptará como convención que si la comparación resulta verdadera, se toma a la
derecha y de lo contrario a la izquierda.
Comentarios sobre los ejemplos realizados
Puede observarse que en los dos árboles vistos la profundidad es 3 que se corresponde
con la fórmula vista T(n) = n(n-1)/2 que era el costo en el peor caso en ambos
algoritmos.
En Selection Sort todas las entradas tienen el mismo costo (el que representa el peor
caso), esto se puede verificar gráficamente ya que el árbol correspondiente es completo.
En Insertion Sort sólo los nodos externos 2, 3, 4 y 6 representan el peor caso.
19
Apuntes de Teórico de Programación 3 – Complejidad
Árbol de decisión, árbol binario estricto
Un Árbol de Decisión (AD) es un árbol binario estricto (cada nodo tiene 2 hijos o ninguno)
donde los nodos internos tienen 2 hijos y los externos (hojas) no tienen ninguno.
Estos árboles, vistos como grafos, son una herramienta para modelar la ejecución de algoritmos
que resuelvan un problema dado. En este marco, los nodos internos representan operaciones y los
externos representan el final de la ejecución de un algoritmo, cada uno para una determinada
entrada.
Se adoptará como convención que si la comparación resulta verdadera, se toma a la derecha y de
lo contrario a la izquierda.
Cualquier algoritmo se puede modelar con un AD particular. Una ejecución del algoritmo, para
una determinada entrada, está representado por el camino de la raíz a un único nodo externo. El
costo, en cantidad de operaciones para dicha entrada, estará dado por el largo de dicho camino
(cantidad de aristas). De esta forma el peor caso corresponderá camino más largo de la raíz a un
nodo externo.
Para el problema de encontrar cotas inferiores, se trabajará entonces con la herramienta árbol de
decisión. De esta manera se logra un razonamiento genérico e independiente de cualquier
algoritmo particular.
En lo inmediato siguiente se verán propiedades de los árboles binarios estricos en general que
luego se aplicarán al caso de estudio.
20
Apuntes de Teórico de Programación 3 – Complejidad
Árbol Binario Estricto
Un árbol binario estricto es un árbol (binario) donde los nodos internos tienen 2 hijos y los
externos (hojas) no tienen ninguno.
Profundidad
Profundidad de un nodo i:
Prof(i) = Largo del camino (cantidad de aristas) desde la raíz al nodo i.
En particular si i es un nodo externo, también vale esta definición, además debe notarse en este
caso que es igual a la cantidad de nodos internos en el camino desde la raíz a i.
Profundidad del Arbol:
Prof(T) = Máxima Profundidad de nodo externo
Propiedades de Árbol Binario Estricto (ABE)
Sea T un ABE
Sea m la cantidad de nodos externos de T (ABE)
Sea k la Profundidad de T
1) Lema 1: m ≤ 2k
No se demostrará formalmente pero por ser un ABE cada nodo tiene 2 hijos salvo las hojas que
no tienen ninguno.
Si T es completo tendrá la máxima cantidad de nodos externos y en el nivel k esa cantidad será
igual a 2k, por lo tanto:
m ≤ 2k
2) Lema 2: La suma de las profundidades de los m nodos externos es mayor o igual a mlog m:
Es decir: Si T es un ABE cualquiera con m nodos externos, sea D(T)
profundidades de sus nodos externos:
Σ Prof(j)
j externo en T
D(T) =
⇒
D(T) ≥ m log m
Sea d (m) = min{D (T ) / T tiene m nodos externos
T
}
21
la suma de las
Apuntes de Teórico de Programación 3 – Complejidad
Demostración:
Se demostrará que d(m) ≥ m log m
como d(m) es el mínimo de los D(T), entonces quedará demostrado el lema.
Se usará inducción completa en m:
Paso base) m=1
d(1) es el mínimo de la suma de profundidades para todo T con 1 nodo externo, por lo tanto, por
ser un ABE, T tiene un solo nodo, su profundidad será 0.
d(1) = 0
Por otro lado m log m = 1 log 1 = 0
El paso base se cumple
HI) Si 1≤m< h ⇒ d(m) ≥ m log m
TI) Si m=h ⇒ d(h) ≥ h log h
dem: Sea T el ABE considerado
h>1, T tiene 2 subárboles no vacíos (si no, no sería un ABE):
r
rd
ri
Tizq
Tder
1 2 3…………..i
1 2 3……….…h-i
Sea i la cantidad de nodos externos del subárbol izquierdo Tizq, será h-i la cantidad de nodos
externos del subárbol derecho Tder
22
Apuntes de Teórico de Programación 3 – Complejidad
Como Tizq y Tder no son vacíos, serán
i<h y
h-i < h
por lo tanto en ambos subárboles aplica la hipótesis inductiva:
d(i) ≥ i log i en el subárbol Tizq de raíz ri
notar que:
•
la profundidad está medida desde ri
•
d(i) = min {D(T) / T tiene i nodos externos }
d(h-i) ≥ (h-i) log (h-i) en el subárbol Tder de raíz rd
notar que:
•
la profundidad está medida desde rd
•
d(h-i) = min {D(T) / T tiene (h-i) nodos externos }
Para la demostración de la tésis inductiva se deberá “reconstruir” el árbol T y calcular d(h)
midiendo la profundidad desde r (utilizando las conclusiones anteriores):
•
Notar que se agrega la arista <r,ri >en el lado izquierdo y la arista <r,rd >en el derecho,
además como se están sumando las profundidades de los nodos externos, esas aristas cuentan
i veces del lado izquierdo y h-i veces del lado derecho
•
Al descomponer T en dos subárboles y luego reconstruirlo deben considerarse todas las
variaciones posibles Tizq y Tder . Los casos posibles varían desde que el subárbol izquierdo
tenga 1 sólo nodo externo y el derecho h-2, luego 2 nodos externos en el izquierdo y h-3 en el
derecho, hasta h-2 en el izquiero y 1 en el derecho (recordar que para T los subárboles no
pueden ser vacíos).
•
De las variantes anteriores se deberá tomar la que arroje un mínimo para D(Tizq)+ D(Tder). De
esta forma se asegurará que la fórmula siguiente se cumple para todas las combinaciones.
Por lo antedicho:
d(h) ≥ h + min{i logi + (h − i) log(h − i)
1≤i≤h−1
}
El mínimo se da en i =h/2
d(h) ≥ h + 2(h/2 log h/2) = h + h(log h – log 2) = h + h log h – h = h log h
lqqd.
23
Descargar