Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Introduccin
Introduccin a los Algoritmos y Algoritmos y Estructuras
de Datos I el nfasis estaba puesto en qu hace un programa. Se especicaba con
En las materias
detalle las pre- y post- condiciones que el programa deba satisfacer y se derivaba un
programa que satisciera dicha especicacin.
En
tiempo de procesamiento,
Por ejemplo,
rpidamente concluir que el bibliotecario tardar dos das. Pero es correcto razonar
as?
expedientes?
Es el
doble? Para contestar estas preguntas tenemos que analizar la manera de ordenar. No
contamos -como en el caso de pintar- con una manera nica, habitual o universal de
ordenar expedientes.
Consideremos nuevamente problemas ms familiares, como los siguientes
(1) Un pintor demora una hora y media en pintar una pared cuadrada de 3 metros
de lado. Cunto demorar en pintar una de 5 metros de lado?
(2) Si lleva cinco horas inar un globo aerosttico esfrico de 2 metros de dimetro,
cunto llevar inar uno de 4 metros de dimetro?
El primero de ellos se parece al problema del pintor, salvo que ahora la pared es
cuadrada. Al pensarlo con detenimiento, descubrimos que el tiempo que lleva pintar la
pared cuadrada, no es
V = d6
23 = 8. Inar
),
el
Estos dos problemas sirven para mostrar que para resolverlos es fundamental determinar a qu es
que no es imprescindible conocer la frmula exacta. Por ejemplo, el problema del globo
i,
i1
elementos
i1
a[i,n] (inicialmente es la totalidad del arreglo pero decrece a cada paso) con el resto de
los elementos an no seleccionados.
a[1,i)
a[i,n]
mnimos ordenados
an no seleccionados
-
a
1
i -1
n-1
a = A}
{Inv: a es permutacin de A
do i
<
minp:= min_pos_from(a,i)
swap(a,i,minp)
i:= i+1
od
end proc
{Post: a est ordenado y es permutacin de A}
La precondicin denota por A el valor inicial del arreglo a. La postcondicin dice que
al nalizar la ejecucin, el arreglo a es una permutacin de su valor inicial, y que est
ordenado. Estas dos condiciones especican el problema de ordenacin: un algoritmo
de ordenacin es uno que devuelve una permutacin ordenada de su entrada.
El invariante es el mismo que se explic ms arriba.
Cuando un algoritmo realiza modicaciones (como en este caso al arreglo a) lo llamamos procedimiento (proc). Un procedimiento puede recibir parmetros con datos
todos sus parmetros son meramente datos, por consiguiente el prejo es innecesario.
El procedimiento selection_sort utiliza dos variables: la variable i, cuyo rol se advierte
ya en las explicaciones del algoritmo, y la variable minp que tiene como propsito
sealar la posicin donde se encuentra el nuevo elemento seleccionado. Para calcularlo
se utiliza la funcin min_pos_from que al aplicarse al arreglo a y a la variable i devuelve
la posicin del mnimo del segmento de arreglo a[i,n]. Luego de calcularlo se invoca al
procedimiento swap para intercambiarlo con el que se encuentra en la posicin i. Este
intercambio tiene dos propsitos: por un lado, coloca al elemento seleccionado en su
posicin correspondiente como elemento ya ordenado (la i), y por el otro, evita que el
elemento que ocupaba la celda i se pierda, consiguindole una nueva celda provisoria:
la que justamente se libera por haber estado ocupada por el elemento seleccionado.
{Pre: a = A
i,j
n }
end proc
{Post: a[i] = A[j]
a[j] = A[i]
k. k
6{i,j}
a[k]=A[k]}
La postcondicin del procedimiento swap implica que el mismo produce una permutacin del arreglo que recibe como parmetro.
slo modica el arreglo a invocando al procedimiento swap reiteradamente, la condicin que establece que el arreglo ordenado debe ser una permutacin del inicial queda
sta es una observacin interesante: si uno se limita a modicar
el arreglo slo invocando al procedimiento swap, necesariamente se tendr
siempre una permutacin del arreglo inicial.
garantizada.
Como el objetivo es
{Pre: 0
<i
n}
do j
if a[j]
<
a[minp]
then minp:= j
j:= j+1
od
end fun
{Post: a[minp] es el mnimo de a[i,n]}
ret.
<
n en vez de i
n en la condicin del
tion_sort?
Por qu se llama selection_sort?
El comando for. En el algoritmo presentado los dos ciclos do se utilizan para repetir
ciertas acciones para cada valor de las variables i (o j) desde un valor (cota inferior) a
otro (cota superior). La variable i (o j) recibe el nombre de ndice. Se observa que los
ndices slo son modicados al nal de cada ciclo, cuando se los incrementa. En estas
situaciones, utilizaremos una notacin ms compacta.
do k
C
k:= k+1
od
escribiremos
for k:= n to m do C od
Para que esta notacin tenga sentido es fundamental que k no sea modicado en el
cuerpo C del ciclo. El propsito de esta notacin es hacer ms evidente que C se ejecuta
una vez para cada valor de k desde n hasta m. Observar que si n es mayor que m, no
se ejecuta el cuerpo del ciclo
A veces es deseable que el primer valor del ndice sea la cota superior y que gradualmente se decremente hasta alcanzar la cota inferior. Para esos casos, reemplazamos
por
to
downto.
Por ejemplo, la funcin factorial puede programarse de cualquiera de las formas siguientes
{Pre: 0
n}
for i:= 1 to n do
r:= r * i
od
end fun
{Post: r = n!}
que realiza la multiplicacin
od
end fun
((((1 1) 2) 3) . . .) n
a = A}
od
end proc
{Post: a est ordenado y es permutacin de A}
Asimismo, la funcin de
{Pre: 0
<i
n}
Por supuesto
que para que el anlisis sea correcto, debemos ser criteriosos al elegir dicha operacin:
debe ser una que sea representativa del trabajo que realiza el algoritmo. Para ello se
requiere:
que sea constante (es decir, que el trabajo que implica realizar dicha operacin
debe ser el mismo, independientemente del nmero de celdas del arreglo),
El
salida implcitos luego de cada ejecucin. Adems, cada vez que se ejecuta el ciclo se
realizan accesos al arreglo, una comparacin entre celdas del mismo y posiblemente una
asignacin a minp. Todas ests operaciones (salvo la asignacin a minp que requiere
que la condicin del
for de la funcin min_pos_from, que a su vez se invoca desde el ciclo for del
procedimiento selection_sort.
ordenacin, la comparacin entre elementos del arreglo a[j] < a[minp] que se encuentra
en la condicin del
if.
{1. . . n-1}.
Entonces, el nmero total de veces que se realiza dicha comparacin es n-1 + n-2 +
n(n1)
n2
veces, o distribuyendo,
n2 veces.
. . . + 1. Esto es, un total de
2
2
Resolviendo el problema del bibliotecario. Este anlisis nos permite concluir que
el trabajo que realiza el procedimiento selection_sort para ordenar un arreglo de longin2
tud n es proporcional a
n2 . Para el problema del bibliotecario, entonces, tenemos que
2
ordenar 1000 expedientes con este algoritmo involucra 499500 comparaciones mientras
que ordenar 2000, involucra 1999000 comparaciones.
n2
n2 , es innecesariamente complicada
2
para resolver el problema. El trmino que predomina en esta ecuacin es el de grado
n2
2. Podramos decir que el trabajo de selection_sort es proporcional a
. Por lo tanto,
2
2
tambin es proporcional a n .
Puede observar que la frmula utilizada,
Repetimos el clculo con esta frmula ms sencilla: ordenar 1000 expedientes involucra 1 milln de comparaciones, y ordenar 2000 involucra 4 millones. Arribamos a la
misma conclusin (que ordenar 2000 expedientes es 4 veces ms trabajo que ordenar
1000) que con la frmula innecesariamente complicada.
o ms brevemente
ops(C1 ;C2 ;. . . ;Cn )
n
X
ops(Ci )
i=1
En particular, como
ejecucin del cuerpo del mismo. Podemos utilizar por ejemplo la frmula:
ops(for k:= n
to m do C(k) od)
m
X
ops(C(k))
k=n
Notar que en el lado derecho de la ecuacin hemos utilizado
para referirnos,
to m do C(k) od)
= ops(n) + ops(m) +
m
X
ops(C(k))
k=n
s tiene en cuenta las operaciones necesarias para evaluar n y m. Es importante tenerla
en cuenta, especialmente cuando n o m son expresiones complejas, como llamadas a
funciones tales como factorial o bonacci, por dar tan solo algunos ejemplos.
2
en el cdigo por C(n);C(n+1);. . . ;C(m). Por ejemplo, en el caso de la ordenacin por seleccin, cada
llamada a la funcin min_pos_from, es con un parmetro i diferente. Por lo tanto, el comando for en
el cuerpo de dicha funcin itera, para cada i, un nmero diferente de veces.
La frmula
ops(for k:= n
= (m n + 2) ops(k m) +
to m do C(k) od)
m
X
ops(C(k))
k=n
no tiene en cuenta las operaciones necesarias para evaluar n y m pero s las necesarias
para comparar k con m. Observar que se contabilizan
se utiliza
mn+2
comparaciones,
pero
el cuerpo del
for sino una sola vez para toda la ejecucin del comando for.
Se deja como ejercicio proponer una frmula que tenga en cuenta todas las operaciones
que se requieren para ejecutar el
ops(if b
then C else D )
b
b
ops(b)+ops(C)
ops(b)+ops(D)
= verdadero
= falso
do es
necesario establecer el nmero de veces que se ejecutar el cuerpo del ciclo. No siempre
es fcil obtener dicho nmero.
ops(do b
n,
se
for,
od)
= ops(b) +
n
X
dk
k=1
donde
dk
k -sima
Por qu se suma 2?
10
ops(selection_sort(a))
P
= n-1
ops(min_pos_from(a,i))
Pi=1
n-1 Pn
= i=1
ops(a[j] < a[minp])
j=i+1
Pn-1 Pn
= i=1
1
j=i+1
P
= n-1
(n-i)
i=1
Pn-1
= i=1 i
=
=
n*(n-1)
2
n2
n
2
2
n1
valores, son
n1
for de selection_sort.
intercambios.
P
= n-1
ops(swap(a,i,minp))
Pi=1
n-1
= i=1 1
= n-1
es del
a[i,n] = A[i,n]
ordenado
11
an no insertados
-
a
1
i -1
n-1
a = A}
a es permutacin de A}
insert(a,i)
od
end proc
{Post: a est ordenado y es permutacin de A}
El procedimiento insertion_sort es muy sencillo: simplemente llama repetidamente al
procedimiento insert, que se supone que inserta un elemento (el i-simo) en el segmento
de a[1,i) que ya est ordenado. Luego de esta insercin, el segmento ordenado tendr
un elemento ms, ser a[1,i].
El algoritmo de insercin que damos a continuacin arrastra al nuevo elemento hacia
la izquierda hasta que el mismo alcanza su posicin. Para arrastrarlo lo intercambia
con el de su izquierda tantas veces como sea necesario.
a = A}
j:= i
do
a es permutacin de A}
swap(a,j-1,j)
j:= j-1
od
end proc
{Post: a[1,i] est ordenado
a es permutacin de A}
j
Para escribir el invariante del procedimiento insert denotamos por a[1 , i] la secuencia
de celdas de a desde la posicin 1 hasta la i salteando la j-sima (para
1 j i).
Como no se sabe a priori cuntos lugares hacia la izquierda ser necesario arrastrar
do en vez de for.
for del procedimiento insertion_sort comienza desde 2?
Nmero de comparaciones de la ordenacin por insercin. Nuevamente contamos comparaciones entre elementos del arreglo a, que en este caso son de la
forma
Intuitivamente,
12
denado) cada llamada a insert realiza una sola comparacin (ya que la misma devuelve
ordenado al revs, de mayor a menor) cada llamada a insert con parmetros a e i realiza
i-1 comparaciones (ya que el nuevo elemento debe arrastrarse i-1 pasos hasta la celda
n2
1). Las comparaciones en este caso sumarn 1 + 2 + . . . + (n-1) =
n2 (es decir,
2
2
del orden de n comparaciones).
Obtener el nmero de comparaciones en el peor caso es importante pues establece
una
asumiendo que los valores en las celdas del arreglo fueron generados al azar es del orden
2
de n (no lo demostramos porque requiere conocimientos de probabilidades).
condicin del
swap. En el peor caso (cuando el arreglo inicial est ordenado al revs), la comparacin
a[j] < a[j-1] ser siempre verdadera y se realizarn i swaps por cada llamada a insert(a,i).
n2
En total sern
n2 (es decir, del orden de n2 ) swaps. El nmero de intercambios
2
en el caso promedio (asumiendo que los valores en las celdas del arreglo fueron ge2
nerados al azar) es del orden de n (nuevamente la prueba requiere conocimientos de
probabilidades).
Este anlisis demuestra que en el caso de la ordenacin por insercin, la operacin
de intercambio es representativa del comportamiento del algoritmo en el peor caso y en
el caso promedio. No as en el mejor caso en que las comparaciones son del orden de n
pero no hay ningn intercambio.
13
Sin embargo el enunciado del problema del bibliotecario no asuma que los expedientes se encontraran casi ordenados, por lo que habra que considerar el caso promedio.
2
Dijimos que el algoritmo de ordenacin por insercin es en este caso del orden de n .
Por consiguiente obtenemos la misma respuesta que para el algoritmo de ordenacin
por seleccin: ordenar 2000 expedientes es 4 veces ms trabajo que ordenar 1000 expedientes.
A pesar de ello, en virtud del mejor caso que presenta la ordenacin por insercin,
podramos armar que es preferible frente a la ordenacin por seleccin. Por ejemplo,
si un programa mantiene una base de datos a la que cada tanto se le agregan registros y
cada tanto se la ordena, puede ser muy conveniente utilizar para su ordenacin el algoritmo de ordenacin por insercin, ya que al ser ordenada la base con cierta frecuencia,
su comportamiento se asemejar a su mejor caso. No sera del todo extrao que ste
sea el caso del bibliotecario, ya que las bibliotecas acostumbran mantener sus objetos
(bastante) ordenados.
Anlisis de Algoritmos
El ejemplo ilustra varios aspectos del anlisis del comportamiento de un algoritmo:
la duracin de la operacin elemental depende del hardware, mejor hardware modicara esas constantes.
uno puede querer comparar 2 algoritmos cuyos anlisis fueron hechos eligiendo
operaciones elementales distintas en uno y en otro, sin saber a priori si
dichas operaciones elementales tienen igual o diferente duracin.
Pero hay que tener presente que se est haciendo una aproximacin. Las constantes y trminos que acabamos de llamar despreciables pueden ser signicativos para valores sucientemente bajos de n, o en casos en que se pretende
realizar un anlisis ms no, ms detallado, ms preciso.
14
La Notacin O
Sean
cs (n)
n
por insercin puede ser signicativamente menor en ciertos casos (puede ser del orden
de
n,
por ejemplo).
cuando el orden es exacto y cuando el orden es una cota. Por ejemplo, escribiremos
cs (n) (n2 ) para expresar que el orden de cs (n) es exactamente cuadrtico. Signica
2
que para n sucientemente grande cs (n) es proporcional a n . En cambio, escribiremos
2
ci (n) O(n ) para expresar que el orden de ci (n) es a lo sumo cuadrtico, o sea,
2
que para n sucientemente grande ci (n) es a lo sumo proporcional a n . Tambin
escribiremos
lineal, es
a
n.
A continuacin deniremos formalmente O ,
(n2 ), ci (n)
En particular nos interesa trabajar con funciones que sean no negativas en casi todo
0
su dominio: denotamos por f : N R
que n N.f (n) R .
0
Denicin: Dada f : N R , se dene el conjunto
f (n),
sucien-
ci (n) 6 O(n)
y por ello
ci (n) 6 (n).
O.
En efecto,
proposicin.
como
O.
15
Proposicin:
n N.
d1 t(n)
para todo
d > 0.
Transitividad: Sean
c2 h(n).
tales que
tomando
c=1
Los naturales que satisfacen ambas desigualdades siguen siendo todos salvo a
n N, f (n) c1 g(n) (c1 c2 )h(n).
g(n) O(h(n))
sii
O(g(n)) O(h(n)).
sii
Observar que esto no signica que la relacin es a lo sumo del orden de sea antisimtrica, de hecho no lo es.
f = g,
el corolario
Demostracin: Sean , c > 0 tales que n N.f (n) > y n N.g(n) cf (n).
d
d
0
0
Sea c = c + d/. Ahora n N.g(n) + d cf (n) + cf (n) + f (n) c f (n).
Esto demuestra que en este contexto la base del logaritmo es irrelevante y puede
ignorarse.
f, g N R0 ,
enunciados se cumplen:
(1)
(2)
f (n)
R+ O(f (n)) = O(g(n)).
n g(n)
f (n)
lim
= 0 O(f (n)) O(g(n)).
n g(n)
lim
si
limn
f (n)
existe los siguientes
g(n)
16
f (n)
= + O(g(n)) O(f (n)).
n g(n)
lim
dado que si
limn
f (n)
g(n)
=l
(3)
O(g(n)). Veamos que esta inclusin es estricta vericando que g(n) 6 O(f (n)).
+
Entonces n N.
1c , con ello limn g(n) no podra ser menor que 1c .
g(n)
Luego O(f (n)) O(g(n)).
f (n)
Como limn
= + implica que limn fg(n)
= 0, vale por (2).
g(n)
(n)
Corolario:
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
Demostracin:
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
1
limn nny = limn nyx
= 0.
x
Como limn loga n = + = limn n , por la Regla de L'Hpital tenemos
1
1
n ln a
limn lognax n = limn xn
x1 = limn xnx ln a = 0.
n
limn log
= limn log1 n = limn log n = +. Luego, O(n0 ) O(log n).
n0
x
0
Para todo x R , O(n ) O(n ) O(log n).
nk
Demostraremos por induccin que para todo k N, limn n = 0. Para k = 0
c
nk
la prueba es trivial. Asumimos como hiptesis inductiva que limn n = 0.
c
k
(k+1)nk
nk+1
k+1
Por la Regla de L'Hpital, limn
= limn cn log c = log c limn ncn = 0.
cn
e
e
k
n
Por lo tanto, para todo k N, O(n ) O(c ). Sea x R. Sea k N tal que
x
k
n
x k . Se obtiene O(n ) O(n ) O(c ).
nk
Similar al anterior, demostrando que para todo k N, limn n = +.
c
cn
c n
c
Sigue de limn n = limn ( ) = +, que vale pues
>
1
.
d
d
d
n
2
Sea c > d. Para todo n 2c se puede ver que n! n(n 1) . . . (n b c)
2
n
n
d
e
d n2 ed 2 e c2 2 cn . Por lo tanto, cn O(n!) que implica O(cn ) O(n!). Por
n
el inciso anterior, tenemos O(d ) O(n!).
n!
2...n
Para todo n 2,
= n1 n...n
n1 n...n
= n1 . Luego, limn nn!n
nn
n...n
limn n1 = 0 y O(n!) O(nn ).
17
Los incisos 3 y 5 demostrados son de poco inters ya que no es esperable tener programas cuyo nmero de comparaciones decrece a medida que
O.
g(n) O(f (n)) O(f (n) + g(n)) = O(f (n)).
+
g(n)
0
= 0, entonces f (n) g(n) : N
Proposicin: Sean f, g : N R . Si limn
f (n)
R0 y O(f (n) g(n)) = O(f (n)).
g(n)
Demostracin: n N.
< 12 . As, g(n) < 21 f (n) y 12 f (n) f (n) g(n) f (n).
f (n)
k
k
Corolario: O(ak n + . . . + a1 n + a0 ) = O(n ), si ak 6= 0.
por la notacin
Proposicin:
Proposicin: Si n N.f (n) > 0, entonces O(g(n)) O(h(n)) sii O(f (n)g(n))
O(f (n)h(n)).
1/2
O(n) y log(n) tiende a innito, log n = log1/2 n O(log n).
Ejemplo: Como n
Denicin: Si O(t(n)) = O(1), t se dice constante. Si O(t(n)) = O(log n), t se
2
dice logartmico. Si O(t(n)) = O(n), t se dice lineal. Si O(t(n)) = O(n ), t se
3
k
dice cuadrtica. Si O(t(n)) = O(n ), t se dice cbica. Si O(n ) O(t(n))
k+1
O(n ) para algn k N, t se dice polinomial. Si O(t(n)) = O(cn ), para algn
c > 1, t se dice exponencial. Si t(n) expresa la eciencia de un algoritmo, ste se
Proposicin:
f (n) (g(n)) sii g(n) O(f (n)) sii O(g(n)) O(f (n)) sii (f (n)) (g(n))
f (n) (g(n)) sii g(n) (f (n)) sii O(f (n)) = O(g(n)) sii (f (n)) = (g(n))
0}
do i
end fun
a[i]
6=
i:= i+1
od
x=a[i]}
18
Sean
t1 (n), t2 (n)
t3 (n)
t1 (n) = 1 (1).
t2 (n) = n (n).
insercin.
Ejercicio: Cmo puede modicarse la bsqueda lineal si se asume que el arreglo est
ordenado? De qu orden seran
t1 (n), t2 (n)
t3 (n)
en tal caso?
El segundo ejemplo es el de bsqueda binaria, que requiere que el arreglo est ordenado de menor a mayor. Es similar a cuando uno busca una palabra en un diccionario:
uno lo abre al medio y (a menos que tenga la suerte de encontrarla justo donde abri el
diccionario, en cuyo caso la bsqueda termina) si la palabra que uno busca es anterior a
las que se ven donde se abri el diccionario uno limita la bsqueda a la parte izquierda
(abriendo nuevamente al medio, etc), si en cambio la palabra que uno busca es posterior
a las que se ven, uno limita la bsqueda a la parte derecha, etc.
{Pre: n
a ordenado}
do izq
der
x
x
<
=
>
(i
6=
x = a[i])}
i = 0
med:= (izq+der)
if x
a[med]
a[med]
a[med]
der:= med-1
i:= med
izq:= med+1
od
end fun
{Post: (x est en a sii i
6=
0)
(i
6=
x = a[i]))}
La complejidad del algoritmo est dada por la cantidad de veces que se ejecuta el ciclo,
dado que cada ejecucin del ciclo insume tiempo constante (a lo sumo 2 comparaciones).
En cada ejecucin del ciclo, o bien se encuentra x, o bien el espacio de bsqueda se
19
reduce a la mitad. En efecto, si izqj y derj denotan los valores de izq y der despus de
la j -sima ejecucin del ciclo, sabemos que dj = derj izqj + 1 son la cantidad de celdas
que nos quedan por explorar para encontrar x. Se puede ver que si
dj > 1,
entonces
dj+1 dj /2.
Puede notarse que este algoritmo se comporta mucho mejor que el anterior para
grandes valores de
1.000.000.
dado que
n.
t2 (n) O(n),
Recurrencias
El algoritmo de bsqueda binaria que acabamos de dar, puede tambin denirse por
recursin:
{Pre: 0
izq
der
n}
fun binary_search_rec (a: array[1..n] of T, x:T, izq, der : nat) ret i:nat
var med: int
if izq > der i = 0
izq
der
med:= (izq+der)
if x
x
x
<
=
>
a[med]
6=
0)
a[med]
a[med]
i:= med
i:= binary_search_rec(a, x, med+1,der)
end fun
{Post: (x est en a[izq,der] sii i
(i
6=
x = a[i])}
0 }
end fun
{Post: (x est en a sii i
6=
0)
(i
6=
x = a[i]))}
El algoritmo es esencialmente el mismo que en la versin iterativa, veremos a continuacin cmo calcular el orden de algoritmos recursivos como ste. Sea
t(n) el nmero de
es igual a der - izq + 1. Por simplicidad, consideraremos que evaluar las condiciones
del
20
Entonces, a menos que izq sea mayor que der en cuyo caso
ciones entre elementos de a (t(0)
= 0),
la ejecucin de binary_search_rec(a,x,izq,der)
si n = 0
0
1
si n 6= 0 x = a[med]
t(n) =
1 + t(n 2)
caso contrario
o bien x
o bien x
realiza
En el peor caso,
Para
Pregunta 4: Un bibliotecario demora 1 da en ordenar alfabticamente una biblioteca con 1000 expedientes. Cunto demorar en ordenar una con 2000 expedientes?
Le proponemos al bibliotecario que ordene la primera mitad de la biblioteca (tarea A
trabajo). Esto le llevar mucho menos que los 4 das que le llevara hacer todo con el
algoritmo de ordenacin por seleccin.
Esto a su vez puede mejorarse: la tarea A puede desdoblarse en ordenar 500 expedientes (tarea AA
AB
250.000 comparaciones), e intercalar (tarea AC 1.000 comparaciones). Similar 1.004.000 comparaciones. Poco
[ T]
merge_sort [] = []
merge_sort [t] = [t]
merge_sort ts = merge sts1 sts2
21
where
sts1 = merge_sort ts1
sts2 = merge_sort ts2
(ts1,ts2) = split ts
split :: [T]
([T],[T])
where n = length ts
merge :: [T]
[ T]
[T]
if t1 t2
then t1:merge sts1 (t2:sts2)
else t2:merge (t1:sts1) sts2
(en realidad slo uno es necesario, incluimos el otro para simplicar el algoritmo de
intercalacin) se lo puede escribir de la siguiente manera:
{Pre: n
der
izq
>
a = A}
end proc
22
{Post: a permutacin de A
En realidad, los arreglos b y c no necesitan ser tan largos, pero por simplicidad se
declaran de n celdas, igual que el arreglo a.
La ejecucin de la ordenacin por intercalacin comienza llamando al procedimiento
con izq igual a 1 y der igual a n:
{Pre: n
a = A}
end proc
{Post: a est ordenado y es permutacin de A}
El procedimiento merge_sort_rec utiliza el procedimiento intercalar que puede programarse de la siguiente manera.
{Pre: n
der
>
med
izq
>
a = A
B[1,med-izq+1] = A[izq,med]
b = B
c = C
C[1,der-med] = A[med+1,der]
b[i]
c[j])
od
end proc {a permutacin de A
Esta tcnica para resolver un problema, consistente en dividir el problema en problemas menores (de idntica naturaleza pero de menor tamao), asumir los problemas
menores resueltos y utilizar dichos resultados para resolver el problema original, se
conoce por divide and conquer, es decir, divide y vencers, o divide y reinars.
Justamente el hecho de que los problemas menores sean de igual naturaleza que el
original, es lo que permite que el mismo algoritmo pueda aplicarse recursivamente para
resolver los problemas menores.
arreglo de longitud menor o igual que 1, para el problema de ordenacin) deben resolverse aparte. Estos casos frecuentemente son triviales. Usualmente el trmino divide
y vencers se aplica a aquellos casos en que el problema se subdivide en problemas
menores fraccionando el tamao de la entrada.
Nmero de Comparaciones. Sea t(n) el nmero de comparaciones entre elementos de T que realiza el procedimiento merge_sort_rec con una entrada de tamao n
t(1) = 0.
23
t(dn/2e),
t(n)?
t en trminos de s misma.
t(n) est acotada:
Cmo
Usaremos que
las operaciones
t(2m1 )
t(2m )
+ 1.
2m
2m1
Iterando este proceso, se llega a
t(2m1 )
t(2m2 )
t(2mk )
t(20 )
t(1)
t(2m )
+1
+2
+k
+m =
+m = 0+m = m.
m
m1
m2
mk
0
2
2
2
2
2
1
t(2m ) 2m m. Como n = 2m , sabemos que m = log2 n. Reemplazando
queda t(n) n log2 n, para todo n que sea potencia de 2. Podemos concluir entonces
que t(n) O(n log n|n potencia de 2).
1
Anlogamente, usando la segunda desigualdad obtenemos t(n)
n log n y con2
cluimos que t(n) (n log n|n potencia de 2) y nalmente de ambas conclusiones,
obtenemos que t(n) (n log n|n potencia de 2).
Resta ver cul es la cantidad de comparaciones para el resto de los valores de n.
Primero se puede ver que t(n) es creciente. Por induccin en n, t(n + 1) > t(n). El
caso base es sencillo, t(2) = t(1)+t(1)+21 = 1 > 0 = t(1). Supongamos que para todo
1 k < n, t(k+1) > t(k). Entonces, t(n+1) = t(d(n+1)/2e)+t(b(n+1)/2c)+n+11 >
t(dn/2e) + t(bn/2c) + n 1 = t(n).
k
k+1
Para n sucientemente grande, sea k 1 tal que 2 n < 2
. Esto implica
k+1
que k log2 n < k + 1. Como t es creciente, tenemos t(n) < t(2
) 2k+1 (k + 1) =
2(2k k + 2k ) 2(2k k + 2k k) = 4 2k k 4 2log2 n log2 n = 4n log2 n. Por lo tanto t(n)
O(n log n). Tambin por t creciente, se tiene que t(n) t(2k ) 21 2k k 18 2k+1 (k + 1) >
1 log2 n
2
log2 n = 18 n log2 n. Entonces t(n) (n log n), o sea, t(n) (n log n).
8
Despejando,
24
f : N R0 ,
+
(f (n)|P (n)) = {t : N R0 |c
T R . n N.P (n) t(n) cf (n)}
(f (n)|P (n)) = O(f (n)|P (n)) (f (n)|P (n))
El razonamiento hecho para el anlisis del procedimiento merge_sort_rec puede generalizarse:
realidad, tambin podemos concluir que es 2-suave por ser producto de dos funciones
que son 2-suave). Del resultado que sigue se desprende que
Lema:
es
i-suave
si y slo si
n log2 n
es tambin suave.
es suave.
dlogi je
f (jn) f (i
n)
dlogi je
d
f (n)
por lo tanto,
es
j -suave
f
f
j -suave.
Sea
es i-suave
sucientemente grande,
es eventualmente no decreciente y
es
jn idlogi je n
i-suave
(con constante
ddlogi je ).
nk , log n, nk log n,
mientras que
R0
tualmente no decreciente, si
Anlogamente para
Demostracin:
que
bm n < bm+1 .
En-
tonces
t(n) t(bm+1 )
t es eventualmente no decreciente
cf (bm+1 )
t(n) O(f (n)|n potencia de b)
m
cdf (b )
f es b-suave
cdf (n)
f es eventualmente no decreciente
por lo tanto t(n) O(f (n)).
Ejemplo: sea t(n) el nmero de comparaciones que realiza el procedimiento merge_sort_rec:
t(n) es eventualmente no decreciente, t(n) (n log n|n potencia de 2) y n log n es
suave. Luego t(n) (n log n). Volviendo al problema del bibliotecario, ordenar 1000
expedientes requiere 1000*10 = 10000 comparaciones.
cambio, requiere 2000*11 = 22000 comparaciones.
25
Recurrencias/relaciones divide y vencers (divide and conquer). (Introduction to Algorithms: A Creative Approach, pginas 50 y 51)
(1) comprobar que
t(n)
es eventualmente no decreciente.
b:
(nlogb a )
(nk log n)
t(n)
(nk )
si
si
si
a > bk
a = bk
a < bk
t(n)
at(n/b)+g(n) (resp. t(n) at(n/b)+g(n)) para n potencia de b, g(n) O(nk )
k
(resp. g(n) (n )) se obtiene el mismo resultado en los 3 casos, slo que escribiendo O (resp. ) en vez de .
(4) Si la ecuacin inicial no contiene una igualdad sino slo una cota:
Ejemplos de recurrencias divide y vencers son el nmero de comparaciones que realiza el procedimiento merge_sort, o el nmero de comparaciones que realiza la bsqueda
binaria en el peor caso. El primer ejemplo fue desarrollado en detalle recientemente.
Para la bsqueda binaria hicimos las cuentas detalladamente, pero ahora podemos
volver a hacerla utilizando recurrencias. Si pensamos en el nmero de comparaciones
que hacen falta para buscar en un arreglo de longitud n, observamos que es 1 ms el
nmero de comparaciones que hacen falta para buscar en un arreglo de longitud
t(n) = t(bn/2c) + 1.
bn/2c.
26
ak tn + . . . + a0 tnk = 0,
(2) considerar el
m1 , . . . , m j
r1 , . . . , rj
ak x k + . . . + a0 ,
mi 1
m1 + . . . + mj = k ),
.
.
.
.
.
.
+ cm1 +...+mj1 +1 rjn + cm1 +...+mj1 +2 nrjn + . . . + cm1 +...+mj nmj 1 rjn
m1 + . . . + mj = k , tenemos k incgnitas: c1 , . . . , ck ,
k condiciones iniciales tn0 , . . . , tn0 +k1 (n0 es usualmente 0 1) plantear
sistema de k ecuaciones con k incgnitas:
como
t(n0 ) = tn0
t(n0 + 1) = tn0 +1
.
.
.
.
.
.
.
.
.
t(n)
reemplazando
ci
ri
c1 , . . . , c k ,
tn = t0 (n),
donde
t0 (n)
se obtiene a partir
t(n0 + k) = tn0 +k ,
donde
tn0 +k
puede obtenerse
tn ,
n:
{pre : n 0}
=
>
1
1
A
p(n-1)
p(n-2)
end proc
Al calcular
tn ,
obtenemos
0
1
tn =
t
n1 + tn2
n=0
n=1
tn tn1 tn2 = 0, k = 2.
2
caracterstico asociado x x 1.
27
1+ 5
1 5
de multiplicidad m1 = 1 y r2 =
de multiplicidad
2
2
1+ 5 n
1 5 n
(4) forma general t(n) = c1 (
) + c2 ( 2 ) .
2
(5) condiciones iniciales t0 = 0 y t1 = 1. Sistema de ecuaciones:
(3) races
r1 =
m2 = 1.
c1 +c2 = 0
(t(0) = t0 )
1 5
1+ 5
(t(1) = t1 )
c1 ( 2 ) + c2 ( 2 ) = 1
1
1
(6) despejando, c1 = y c2 = .
5
5
1 1+ 5 n
1 ( 1 5 )n .
)
t2
tn =
n
5tn1 8tn2 + 4tn3
tn
donde
n = 0, 1, 2
c1 + c2 = 0
c1 + 2c2 + 2c3 = 1
c1 + 4c2 + 8c3 = 2
(t(0) = t0 )
(t(1) = t1 )
(t(2) = t2 )
c1 = 2, c2 = 2 y c3 = 1/2.
1
n
n
n+1
solucin nal tn = 2 + 2 2 n2 , simplicando tn = 2
n2n1 2.
2
efectivamente, con la solucin nal t3 = 2 coincidiendo con el resultado obtenido
para calcular t3 usando la recurrencia original.
(6) despejando,
(7)
(8)
0
8
tn =
7t
n1 18tn2 + 20tn3 8tn4
n = 0, 1, 2
n=3
28
r1 , . . . , rj
m1 , . . . , m j
d.
mi 1
m1 + . . . + mj = k + d + 1),
.
.
.
.
.
.
+ cm1 +...+mj1 +1 rjn + cm1 +...+mj1 +2 nrjn + . . . + cm1 +...+mj nmj 1 rjn
m1 + . . . + mj = k + d + 1, tenemos k + d + 1 incgnitas: c1 , . . . , ck+d+1 ,
a partir de las k condiciones iniciales tn0 , . . . , tn0 +k1 (n0 es usualmente 0 1),
obtener usando la ecuacin caracterstica, los valores de tn0 +k , . . . , tn0 +k+d ,
con los k+d+1 valores tn0 , . . . , tn0 +k+d plantear un sistema de k+d+1 ecuaciones
con k + d + 1 incgnitas:
como
(5)
(6)
t(n0 ) = tn0
t(n0 + 1) = tn0 +1
.
.
.
.
.
.
.
.
.
t(n)
reemplazando
ci
ri
c1 , . . . , ck+d+1 ,
tn = t0 (n), donde t0 (n)
se obtiene a partir
donde
tn0 +k+d+1
Ejemplo: calcular explcitamente
tn
donde
tn =
0
2tn1 + n
n=0
tn 2tn1 = n, k = 1, b = 1, p(n) = n, d = 1.
2
polinomio caracterstico asociado (x 2)(x 1) .
races r1 = 2 de multiplicidad m1 = 1 y r2 = 1 de multiplicidad m2 = 2.
n
n
n
n
forma general t(n) = c1 2 + c2 1 + c3 n1 , simplicando t(n) = c1 2 + c2 + c3 n.
condiciones iniciales t0 = 0. Tambin podemos obtener usando la recurrencia
t1 = 2t0 + 1 = 1 y t2 = 2t1 + 2 = 4.
(7)
(8)
(9)
c1 + c2 = 0
(t(0) = t0 )
2c1 + c2 + c3 = 1
(t(1) = t1 )
4c1 + c2 + 2c3 = 4
(t(2) = t2 )
despejando, c1 = 2, c2 = 2 y c3 = 1.
n
n+1
solucin nal tn = 2 2 2 n, simplicando tn = 2
n 2.
efectivamente, con la solucin nal t3 = 11 coincidiendo con el resultado obtenido
para calcular t3 usando la recurrencia original.
29
(n log n) comparaciones.
pivote, es
decir, un elemento que se utilizar para separar ambos fragmentos. Aquellos elementos
que sean menores o iguales al pivote pertenecern al primer fragmento, aqullos que
no, al segundo. Llamaremos pivot al procedimiento que realiza dicha fragmentacin.
El procedimiento quick_sort invocar al procedimiento pivot.
{Pre: 1
izq
<
der
a = A}
proc pivot (in/out a: array[1..n] of elem, in izq, der: nat, out piv: nat)
var i,j: nat
piv:= izq
i:= izq+1
j:= der
do i
j
{piv
<i
j+1
if a[i]
a[j]
a[i]
>
que a[piv]}
>
que a[piv]}
que a[piv]}
swap(a,i,j)
i:= i+1
j:= j-1
od
{i
swap(a,piv,j)
piv:= j
que a[piv]}
end proc
Este procedimiento elige un pivote (se elige arbitrariamente a[izq]) y lo utiliza para
clasicar los elementos que se encuentran entre las posiciones izq y der: por un lado
los que son menores o iguales al pivote, y por el otro los que son mayores a l.
El
procedimiento modica el arreglo y la variable piv de forma que los primeros queden
entre izq y piv y los segundos entre piv+1 y der. El ndice i se utiliza para recorrer desde
la posicin izq+1 hacia la derecha, avanzando mientras encuentre elementos menores
o iguales al pivote. El ndice j se utiliza para recorrer desde la posicin der hacia la
izquierda, retrocediendo mientras encuentre elementos mayores al pivote.
no puede avanzar ni j retroceder es porque a[i]>a[piv] y a[j]a[piv].
Cuando i
En ese caso, se
intercambian los contenidos de a[i] y a[j], tras lo cual i puede avanzar y j retroceder.
30
Cuando termina el ciclo, i es j+1, todos los anteriores a i son menores o iguales que el
pivote y todos los posteriores a j son mayores que el pivote. Para nalizar, se ubica el
pivote al nal del primer fragmento y se asigna a piv esa nueva posicin.
Para ordenar el fragmento del arreglo a que va de las posiciones izq a der, el algoritmo
de ordenacin primero realiza la clasicacin que se acaba de explicar, y luego ordena
recursivamente los dos segmentos: el anterior al pivote y el posterior al pivote.
{Pre: 0
der
izq
n+1
izq-1
der
a = A}
piv der}
que a[piv]}
> que a[piv]}
quick_sort_rec(a,izq,piv-1)
quick_sort_rec(a,piv+1,der)
end proc
{Post: a[1,izq) = A[1,izq)
a(der,n] = A(der,n]
a = A}
end proc
{Post: a est ordenado y es permutacin de A}
El algoritmo de ordenacin rpida es muy utilizado en la prctica ya que su comportamiento en el caso medio es eciente. En efecto, asumiendo que piv siempre quede en
el medio entre izq y der, su nmero de comparaciones estara dado por la recurrencia
(n log n).
Por ello
t(n)
mismo resultado. Esto se debe a que piv tiene una alta probabilidad de quedar cerca
del medio entre izq y der. Por ello, si tomamos t(n) como el nmero de comparaciones
que realiza la ordenacin rpida en el caso medio obtenemos tambin
estar casi ordenado. En ese caso utilizar esta versin de ordenacin rpida puede ser
muy ineciente.
En efecto, pensemos qu pasara si aplicamos quick_sort a un arreglo perfectamente
ordenado.
31
un nuevo piv, esta vez en la posicin 2, luego de haber realizado n-2 comparaciones.
Iterando esto hasta terminar, vemos que el algoritmo realiza (n-1) + (n-2) + . . . + 1
comparaciones, es decir, es cuadrtico. Qu ocurri? Ocurri que piv qued siempre
en un extremo del segmento de arreglo a ordenar.
Podemos verlo tambin utilizando recurrencias. En el caso de aplicar quick_sort a
un arreglo ordenado obtenemos
t(0) = 0
t(n) = c1 1n + c2 n1n + c3 n2 1n = c1 + c2 n + c3 n2
para
c1 , c2
c3
y por lo tanto,
c3 6= 0
t(n) (n2 ).
pero es cuadrtico en el peor caso. Para conar en el caso medio, es necesario comprobar
que los datos a ordenar son aleatorios.
Sin embargo, existen modicaciones muy sencillas a la versin presentada ac que
permiten conar en el caso medio an si se aplica a un arreglo ordenado. La ms sencilla
consiste en elegir (pseudo)aleatoriamente el pivote, en vez de tomar siempre el primero
del segmento.