Algoritmos sobre varias secuencias

Anuncio
Problemas con dos secuencias de entrada
Algoritmos y Estructuras de Datos I
Presentaremos tres problemas que toman dos secuencias de entrada:
1. Problema producto cartesiano: dadas dos secuencias producir otra
cuyos elementos son los pares formados por un elemento de la
primera y un elemento de la segunda.
Segundo cuatrimestre de 2014
Departamento de Computación - FCEyN - UBA
2. Problema distancia de Hamming: medir la diferencia entre dos
secuencias.
Algoritmos - clase 13
3. Problema merge: Dadas dos secuencias ordenadas, obtener una
tercera secuencia intercalando ordenadamente sus elementos.
Algoritmos con dos secuencias
1
Problema: producto cartesiano
2
Algoritmo producto cartesiano
problema cartesiano(a, b : [Int], c : [(Int, Int)], n, m : Int){
modifica c;
requiere |a| == n;
requiere |b| == m;
requiere |c| == n ∗ m;
asegura c == [(a[i], b[j]) | i ← [0..|a|), j ← [0..|b|)]
}
Recorrer a linealmente. Para cada elemento de a recorrer todos los
elementos de b. Coleccionar en c los pares visitados.
Este algoritmo no tiene precondiciones. Realiza una cantidad de
operaciones del orden |a| ∗ |b|.
3
4
Subproblema auxiliar
Especificación del ciclo de producto cartesiano
Usaremos el algoritmo fila que resuelve este suproblema:
problema fila(a, b : [Int], c : [(Int, Int)], i, m : Int){
modifica c;
requiere 0 ≤ i < |a|;
requiere m == |b|;
requiere |c| == |a| ∗ |b|;
asegura c[0..i ∗ |b|) == pre(c[0..i ∗ |b|));
asegura c[i ∗ |b|..(i + 1) ∗ |b|) == [(a[i], b[j]) | j ← [0..|b|)];
asegura c[(i + 1) ∗ |b|..|a| ∗ |b|) == pre(c[(i + 1) ∗ |b|..|a| ∗ |b|));
}
Pc: i == 0
invariante:
0 ≤ i ≤ |a| ∧ c[0..i ∗ |b|) == [(a[k], b[`])|k ← [0..i), ` ← [0..|b|)]
variante: |a| − i
Qc: i == |a| ∧ c == [(a[i], b[j]) | i ← [0..|a|), j ← [0..|b|)]
5
Programa producto cartesiano
6
Problema: Distancia Hamming
void cartesiano(int a[], int b[], Par c[], int n, int m) {
int i = 0;
// Pc: i == 0
while (i < n) {
// invariante: 0 ≤ i ≤ n∧
//
c[0..i ∗ |b|) == [(a[k], b[`])|k ← [0..i), ` ← [0..|b|)]
// variante v= n − i
fila(a,b,c,i,m);
// estado e
// vale Qfila(a,b,c,i,m)
// implica c[0..i ∗ |b|) == [(a[k], b[`])|k ← [0..i), ` ← [0..|b|)]∧
//
c[i ∗ |b|..(i + 1) ∗ |b|) == [(a[i], b[j]) | j ← [0..|b|)]
// implica c[0..(i + 1) ∗ |b|) == [(a[k], b[`])|k ← [0..i + 1), ` ← [0..|b|)]
i++;
// vale i = [email protected] + 1
// implica c[0..i ∗ |b|) == [(a[k], b[`])|k ← [0..i), ` ← [0..|b|)]
}
// Qc: i == |a| ∧ c == [(a[i], b[j]) | i ← [0..|a|), j ← [0..|b|)]
I
Definición. (Richard Hamming, 1950) La distancia de Hamming
entre dos palabras es el número de caracteres que tienen que
cambiarse para transformar una en la otra.
I
Es una métrica de la diferencia entre las dos palabras, que sólo tiene
en cuenta la cantidad de diferencias entre ellas.
I
Ejemplos:
1.
2.
3.
4.
5.
hamming (1011101, 1001001) = 2.
hamming (1011101, 1011101) = 0.
hamming (123, 321) = 2.
hamming (123, 133) = 1.
hamming (123, 12345) = 2.
}
7
8
Problema: distancia Hamming
Algoritmo de distancia Hamming
1. Inicializar contador en 0.
2. Utilizar un único indice i para recorrer a y b linealmente hasta
alcanzar la última posición de la más corta.
problema hamming (a, b : [char], n, m : Int) = res : Int{
requiere |a| == n;
requiere |b| == m;
P
asegura res == [β(a[i]! = b[i]) | i ← [0.. mı́n(n, m))]+
abs(n − m)
}
I
I
I
Comparar a[i] y b[i].
Si difieren, incrementar el contador en 1.
Incrementar el ı́ndice en 1.
3. Sumar al contador la diferencia entre las longitudes de a y b.
4. Retornar el valor del contador.
I
Este algoritmo no tiene precondiciones, y realiza una cantidad lineal
de operaciones.
10
9
Especificación del ciclo de distancia Hamming
Programa hamming
int hamming(char a[], char b[], int n, int m) {
int i = 0;
int c = 0;
// Pc: i == 0 ∧ c == 0
while (i < n && i < m) {
// invariante: 0 ≤ i ≤
Pmı́n(n, m) ∧
// invariante: c == [β(a[j]! = b[j])| j ← [0..i)]
// variante: mı́n(n, m) − i
if ( a[i]!=b[i] ) c++;
i++;
}
P
Qc: i == mı́n(m, n) ∧ c == [β(a[j]! = b[j])|j ← [0.. mı́n(n, m))]
return c P
+ abs(n-m);
// res == [β(a[i]! = b[i]) | i ← [0.. mı́n(n, m))] + abs(n − m)
}
Pc: i == 0 ∧ c == 0
invariante: 0≤i ≤mı́n(n, m) ∧ c ==
P
[β(a[j]! = b[j])|j ← [0..i)]
variante: mı́n(n, m) − i;
Qc: i == mı́n(n, m) ∧ c ==
P
[β(a[j]! = b[j])| j ← [0.. mı́n(n, m))]
11
12
Problema merge: intercalar dos secuencias ordenadas
Algoritmo de merge
Problema merge. Dados dos arreglos ordenados a y b, dar un tercer
arreglo de salida c que contenga a los elementos de ambos arreglos de
entrada, y esté ordenado.
1. Mantener un ı́ndice para recorrer a y otro para b.
2. Recorrer linealmente los arreglos a y b, asignando de a un elemento
por vez en el arreglo de salida c. El elemento a asignar es el menor
entre el elemento actual de a y el actual de b.
problema merge(a : [Int], b : [Int], c : [Int], n : Int, m : Int){
requiere n == |a| ∧ m == |b| ∧ n + m == |c|;
requiere ordenado(a) ∧ ordenado(b);
modifica c;
asegura ordenado(c) ∧ mismos(c, a + +b);
aux ordenado(a : [T]) : Bool = todos([ai ≤ ai+1 |i ← [0..|a| − 1)])
aux mismos(a, b : [T]) : Bool = |a| == |b| ∧
(∀x ← a) cuenta(x, a) == cuenta(x, b);
}
3. Incrementar en 1 el ı́ndice del arreglo del que provino el elemento.
4. Cuando uno de los arreglos de entrada ya esté completamente
recorrido, asignar un elemento del otro arreglo.
13
Especificación del ciclo de merge
14
Programa merge
void merge(int a[], int b[], int c[], int n, int m) {
int i,j = 0;
// Pc: i == 0 ∧ j == 0
while (i+j < n+m) {
// invariante: 0 ≤ i ≤ n ∧ 0 ≤ j ≤ m ∧ ordenado(c[0..i + j)) ∧
// invariante: mismos(c[0..i + j), a[0..i) + +b[0..j)) ∧ ...;
// variante: n + m − (i + j);
if (i<n && j<m)
if (a[i] ≤ b[j]) { c[i+j]= a[i]; i++; }
else
{ c[i+j]= b[j]; j++; }
else
if (i==n) { c[i+j]= b[j]; j++; }
else
{ c[i+j]= a[i]; i++; }
}
// Qc: i == n ∧ j == m ∧ ordenado(c[0..n + m))∧
//
mismos(c[0..n + m), a[0..n) + +b[0..m))
}
Pc: i == 0 ∧ j == 0
invariante: 0 ≤ i ≤ n ∧ 0 ≤ j ≤ m ∧
ordenado(c[0..i + j))∧
mismos(c[0..i + j), a[0..i) + +b[0..j))∧
(j < m ⇒ todos([x ≤ bj |x ← a[0..i)]))∧
(i < n ⇒ todos([x ≤ ai |x ← b[0..j)]));
variante: n + m − (i + j);
guarda: i + j < n + m;
Qc: i == n ∧ j == m ∧ ordenado(c[0..n + m)) ∧
mismos(c[0..n + m), a[0..n) + +b[0..m))
15
16
Descargar