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 = i@e + 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