Algoritmos y Estructuras de Datos I
Primer cuatrimestre de 2014Departamento de Computaci´on - FCEyN - UBA
Programaci´on imperativa - clase 8
Algoritmos de ordenamiento II
1
Ordenamiento de un arreglo
I Tenemos un arreglo de un tipo T con una relaci´on de orden (≤) y queremos modificar el arreglo para que sus elementos quedenen orden creciente.
problema sort<T> (a : [T]) { requiere 1 ≤ |a|;
modifica a;
asegura mismos(a, pre(a)) ∧ ordenado(a); }
I Adoptamos como estrategia que modificamos el arreglo
permutando elementos, para garantizar que nuestro algoritmo no pierde ning´un elemento del arreglo.
I Conocemos el algoritmo upsort, con complejidad O(n2).
2
Algoritmo downsort
// invariante I : 0 ≤ actual ≤ |a| − 1 ∧ mismos(a, pre(a)) ∧ ordenado(a(actual ..|a|))
∧ actual < |a| − 1 → (∀x ← a[0..actual ]) x ≤ aactual +1
I Podemos considerar un algoritmo similar, que en cada paso busque el m´ınimo elemento y lo ubique al principio de la parte no ordenada del arreglo.
I Este algoritmo se llama downsortoselection sort.
// invariante I : 0 ≤ actual ≤ |a| − 1 ∧ mismos(a, pre(a)) ∧ ordenado(a[0..actual ))
∧ actual > 0 → (∀x ← a[actual ..|a|)) x ≥ aactual −1
Upsort vs downsort
I Los invariantes de ambos algoritmos provienen de reemplazar distintas constantes en la postcondici´on del problema:
asegura mismos(a, pre(a)) ∧ ordenado(a[0..|a|));
I Si reemplazamos el lado izquierdo en a[0..|a|), tenemos el invariante de upsort (m´as una condici´on adicional):
I : 0 ≤ actual ≤ |a| − 1 ∧ mismos(a, pre(a)) ∧ ordenado(a(actual ..|a|)) ∧ ...
I Si reemplazamos el lado derecho en a[0..|a|), tenemos el invariante de downsort:
I : 0 ≤ actual ≤ |a| − 1 ∧ mismos(a, pre(a)) ∧ ordenado(a[0..actual )) ∧ ...
I Consideremos qu´e sucede ahora si eliminamos la ´ultima condici´on del invariante de downsort:
I : 0 ≤ i ≤ |a| ∧ mismos(a, pre(a)) ∧ ordenado(a[0..i ))
I La funci´on variante esv = n − i.
I ¿Qu´e dice este invariante? ¿Se puede implementar un algoritmo de ordenamiento sobre esta base?
5
i = 0;
while( i < a.length ) {
I : 0 ≤ i < |a| ∧ mismos(a, pre(a)) ∧ ordenado(a[0..i ))
j = i;
while( j>0 && a[j] < a[j-1] ) {
swap(a, j, j-1); --j;
}
E : 0 ≤ i ≤ |a| ∧ mismos(a, pre(a)) ∧ ordenado(a[0..i + 1))
++i; }
6
Problema de la bandera holandesa
I Supongamos que tenemos la siguientehip´otesis adicional:
problema dfp(a : [Z ]) { requiere |a| ≥ 1;
requiere (∀ i ∈ [0..|a|)) 0 ≤ ai ≤ 2; modifica a;
asegura mismos(a, pre(a)) ∧ ordenado(a); }
I Reglas: Solamente podemos modificar el arreglo haciendo swaps, y el algoritmo debe tener complejidad O(n).
Problema de la bandera holandesa
I Observar que la postcondici´on se puede escribir de la siguiente forma equivalente (abusando levemente de la notaci´on):
asegura mismos(a, pre(a)) ∧ (∃ i , j ∈ [0..|a|), i ≤ j ) a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[j ..|a|) = 2
I En esta expresi´on, definimos
a[d , h) = x ⇔ (∀i ∈ [d , h)) ai = x I En forma gr´afica:
0 1 2
Problema de la bandera holandesa
I El invariante proviene derelajar la variable j:
I: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j ≤ k ≤ |a| ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
I En forma gr´afica:
0 1 ? 2
i j k
I El invariante es equivalente a la postcondici´on sij=k, y entonces tenemos la guarda del ciclo.
I Para inicializartrivialmenteel invariante, hacemos: i = 0;
j = 0;
k = a.length;
9
Problema de la bandera holandesa
i = 0;j = 0;
k = a.length; while( j != k ) {
I ∧ B : ... ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
if( a[j] == 0 ) ...
I ∧ j 6= k ∧ aj = 0 else if( a[j] == 1 )
...
I ∧ j 6= k ∧ aj = 1 else if( a[j] == 2 )
... I ∧ j 6= k ∧ aj = 2 } 10
Caso 1: a
j= 1
0 1 1 2 i j kE1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 1 ∧
a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
++j;
E2: a = a@E1 ∧ j = j@E1+ 1 ∧ i = i @E1 ∧ k = k@E1
implica mismos(a, pre(a)) implica 0 ≤ i ≤j ≤ k ≤ |a| implica a[i ..j − 1) = 1 ∧ aj −1= 1 implica a[i ..j − 1] = 1
implica I
I: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j ≤ k ≤ |a| ∧
Caso 2: a
j= 2
0 1 2 2
i j k
E1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 2 ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
swap(a,j,k-1);
E2: aj = ak−1@E1 ∧ ak−1= aj@E1
∧ (∀t ∈ [0..|a|), t 6= j, k − 1)at = at@E1
∧ i = i @E1 ∧ j = j@E1 ∧ k = k@E1
implica ak−1= 2 ∧ mismos(a, pre(a))
0 1 2 2
i j k
E2: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ a[0..i ) = 0 ∧ a[i ..j) = 1 ∧ a[k − 1..|a|) = 2
--k;
E3: a = a@E2 ∧ i = i @E2 ∧ j = j@E2 ∧ k = k@E2− 1 implica mismos(a, pre(a))
implica 0 ≤ i ≤j ≤ k ≤ |a|
implica a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2 implica I
13
0 1 0 2
i j k
E1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 0 ∧
a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
swap(a,i,j);
E2: aj = ai@E1 ∧ ai = aj@E1
∧ (∀t ∈ [0..|a|), t 6= i , j)at = at@E1
∧ i = i @E1 ∧ j = j@E1 ∧ k = k@E1 implica mismos(a, pre(a))
implica ai = 0 ∧ (i < j → aj = 1)
implica a[0..i ] = 0 ∧ a(i ..j ) = 1 ∧ a[k..|a|) = 2
14
Caso 3: a
j= 0
0 0 1 1 2
i j k
E2: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ a[0..i ] = 0 ∧ a(i ..j ) = 1 ∧ a[k..|a|) = 2 ∧ (i < j → aj = 1)
++i;
E3: a = a@E2 ∧ i = i @E2+ 1 ∧ j = j @E2 ∧ k = k@E2
implica mismos(a, pre(a)) implica 0 ≤i − 1 ≤ j < k ≤ |a|
implica a[0..i )= 0 ∧a[i ..j )= 1 ∧ a[k..|a|) = 2
Caso 3: a
j= 0
0 1 1 2
i j k
E3: mismos(a, pre(a)) ∧ 0 ≤i − 1 ≤ j < k ≤ |a| ∧
a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2 ∧ (i < j → aj = 1)
++j;
E3: a = a@E2 ∧ i = i @E2 ∧ j = j@E2+ 1 ∧ k = k@E2
implica mismos(a, pre(a)) implica 0 ≤i ≤ j ≤ k ≤ |a|
implica a[0..i ) = 0 ∧a[i ..j )= 1 ∧ a[k..|a|) = 2 implica I
Problema de la bandera holandesa
I Tenemos el cuerpo del ciclo, que (1)preserva el invariante.
i = 0; j = 0;
k = a.length; while( j != k ) {
I ∧ B : ... ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
if( a[j] == 0 ) {
swap(a,i,j); ++i; ++j; }
else if( a[j] == 1 ) ++j;
else if( a[j] == 2 ) { swap(a,j,k-1); --k; }
}
17
Problema de la bandera holandesa
I Por construcci´on, (2)la inicializaci´on establece el invariante:
Pc: i = 0 ∧ j = 0 ∧ k = |a| ∧ a = pre(a) ⇒
I : mismos(a, pre(a)) ∧ 0 ≤ i ≤ j ≤ k ≤ |a| ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
I Adem´as, (3)al salir del ciclo vale la postcondici´on:
I ∧ ¬B: mismos(a, pre(a)) ∧ 0 ≤ i ≤j = k ≤ |a| ∧ a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[k..|a|) = 2
⇒
mismos(a, pre(a)) ∧ (∃ i , j ∈ [0..|a|), i ≤ j ) a[0..i ) = 0 ∧ a[i ..j ) = 1 ∧ a[j ..|a|) = 2
18
Problema de la bandera holandesa
I En cada una de las tres ramas, (4)el variante decrece: I E1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 1 ∧ ...
++j;
E2: k = k@E1 ∧ j = j@E1+ 1
implica k − j < (k − j )@E1
I E1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 2 ∧ ...
swap(a,j,k-1); --k;
E2: k = k@E1− 1 ∧ j = j@E1 implica k − j < (k − j )@E1
Problema de la bandera holandesa
I E1: mismos(a, pre(a)) ∧ 0 ≤ i ≤ j < k ≤ |a| ∧ aj = 0 ∧ ... swap(a,i,j);
++i; ++j;
E2: k = k@E1 ∧ j = j@E1+ 1
implica k − j < (k − j )@E1
I Finalmente, (5) cuando el variante pasa la cota, el ciclo termina, puesto quek − j ≤ 0 ⇔ ¬B.
I Por lo tanto, el algoritmo es correcto respecto de su especificaci´on.
I ¿Cu´al es lacomplejidad del algoritmo?
1. Antes de comenzar las iteraciones, v = |a|.
2. En cada iteraci´on, el variante decrece estrictamente.
3. Cuando v ≤ 0, el ciclo termina.
I Entonces, el algoritmo realiza a lo sumo |a| iteraciones (m´as a´un, realiza exactamente |a| iteraciones, dado que en cada paso el variante decrece en exactamente una unidad). I El algoritmo tiene una complejidad deO(n).