Documentos de Académico
Documentos de Profesional
Documentos de Cultura
31 de Agosto 2011
El comando diff
diff es un comando de Linux que muestra las diferencias en el
contenido de dos textos (equivalente a fc.exe de Windows/DOS)
¿Cúando se usa?
Se usa, entre otras cosas, para comparar ediciones (versiones) de
un mismo archivo
¿Cómo compara?
El criterio por defecto es buscar lı́neas comunes a ambos archivos.
Nueva versión
/** Versión “original”
* main class of diff program */
*/ class Diff {
class Diff { int commonLines(File a, File b) {
/* calculate common lines */ String contentA = read(b);
int commonLines(File a, File b) { String contentB = read(b);
String contentA = read(a); //fixed String[] pA = split(contentA);
String contentB = read(b); String[] pB = split(contentB);
ParsedText pA = parse(contentA); int result = LCS.calculate(pA, pB);
ParsedText pB = parse(contentB); return result;
int result = LCS.calculate(pA, pB); }
return result; }
}
}
Referencia de colores
lı́neas sin cambios lı́neas nuevas lı́neas eliminadas
Operaciones de moficiación
Sin operación : las lı́neas comunes
Inserción : las lı́neas no comunes del nuevo
Borrado : las lı́neas no comunes del “original”
¿Cuál es el problema?
Para hacer una buena comparación de archivos, hay que tener un
algoritmo que encuentre la mayor cantidad de lı́neas comunes entre
ambos. Además, las lı́neas comunes tienen que aparecer con el
mı́smo orden en ambos archivos.
Ejemplos
LCS([X AABBDZ ], [AX AC BDY ]) = 4
LCS([], [XXYY ]) = 0
LCS([123456], [123456]) = 6
LCS([♠♠♠♥], [♥♠♠♠]) = 3
Problemas, algoritmos y programación Programación dinámica
Problema: diff - ¿Cómo se puede resolver?
Más problemas
Se calculan las LCS de todos los prefijos de A contra todos los
prefijos de B
Entre estos resultados está la LCS de A y B
Si n y m son el tamaño de A y B, la cantidad de LCS que se
van a calcular son (n + 1)(m + 1)
Cada prefijo de A se puede identificar con su longitud, que es
un número del 0 al n. De la misma forma, cada prefijo de B
se identifica con los números del 0 al m.
LCS2 : (int, int, T [], T []) → int
Prefijo 8
/** Prefijo 5
* main class of diff program */
class Diff {
*/
int commonLines(File a, File b) {
class Diff {
String contentA = read(b);
/* calculate common lines */
String contentB = read(b);
int commonLines(File a, File b) {
String[] pA = split(contentA);
String contentA = read(a); //fixed
String contentB = read(b);
Prefijo 6
/** Prefijo 2
* main class of diff program */
*/ class Diff {
class Diff { int commonLines(File a, File b) {
/* calculate common lines */
int commonLines(File a, File b) {
Prefijo 6
/** Prefijo 3
* main class of diff program */
class Diff {
*/
int commonLines(File a, File b) {
class Diff {
String contentA = read(b);
/* calculate common lines */
int commonLines(File a, File b) {
Primeras consideraciones
Si se dispone de la función LCS2 , la función LCS se programa
muy simplemente
La función LCS2 se puede programar recursivamente
Los argumentos A y B de LCS2 son siempre los mismos
Entonces LCS2 se puede programar como una función
recursiva de dos variables (los prefijos i y j). Los arreglos A y
B se pueden considerar como variables globales
El caso base de la recursión es cuando i = 0 o j = 0
Observación
Este programa calcula todos los valores de la función LCS2 en la
matriz mem
Problemas, algoritmos y programación Programación dinámica
Problema: diff - ¿Cómo se puede programar?
1, 0 1, 1 1, 2 1, 3 1, 4 1, 5 1, 6 ··· 1, m
2, 0 2, 1 2, 2 2, 3 2, 4 2, 5 2, 6 ··· 2, m
. . . . . . . . .
. . . . . . . .. .
. . . . . . . .
n, 0 n, 1 n, 2 n, 3 n, 4 n, 5 n, 6 ··· n, m
i, j − 1 i, j n, 0 n, m
Observación
Se puede eliminar la recursión calculando los valores de mem
directamente. El orden en que se vayan calculando los valores debe
satisfacer todas las “dependencias”.
Introducción
“Programación Dinámica” es una técnica para resolver cierto tipo
de problemas (como LCS)
Ejemplo: LCS
Entrada: Dos arreglos
Resultado: La longitud de la sub-secuencia común mas larga
Ejemplo: Ordenamiento
Entrada: Un arreglo
Resultado: Un arreglo con los elementos ordenados
Ejemplo: SAT
Entrada: Un sistema de ecuaciones booleanas
Resultado: La satisfacibilidad del sistema
Problemas, algoritmos y programación Programación dinámica
Programación dinámica: Definiciones
Instancia de un problema
Es un problema con parámetros de entrada concretos (no
genéricos). Toda instancia tiene un resultado definido.
Ejemplo: LCS
LCS de “XAABBDZ” y “AXACBDY” → 4
LCS de “123456” y “123456” → 6
Ejemplo: Ordenamiento
Ordernar: [“perro”,“casa”,“gato”] → [“casa”,“gato”,“perro”]
Ordernar: [0, 3, 0, 3, 4, 5, 6] → [0, 0, 3, 3, 4, 5, 6]
Ejemplo: SAT
a ∨ ¬b
¿El sistema es satisfacible? → Sı́
b ∧ (¬a ⇒ b)
Problemas, algoritmos y programación Programación dinámica
Programación dinámica: Definiciones
Problema recursivo
En un problema recurisvo el resultado de una instancia se puede
obtener en base a otros resultados de instancias “mas chicas“ del
mismo problema (sub-problemas / sub-instancias)
Ejemplo: LCS
El resultado se puede obtener combinando resultados de los
prefijos.
Ejemplo: Ordenamiento
El resultado se puede obtener encontrando el menor,
intercambiarlo con el primero y ordenando el resto (Selection
sort)
El resultado se puede obtener ordenando dos mitades del
arreglo y combinando ambos resultados (Merge sort)
Sub-problemas superpuestos
La programación dinámica es útil si distintos sub-problemas se
pueden resolver en base a sub-sub-problemas comunes
Ejemplo: LCS (ası́ se ve en el “arbol de llamadas”)
Mal ejemplo: Merge sort
Principio de optimalidad
La solución de un problema esta formada por soluciones de
sub-problemas (sub-soluciones)
Problemas, algoritmos y programación Programación dinámica
Programación dinámica: Aplicaciones
sub-problemas sub-soluciones
Problemas
Con sub-problemas
Que cumplan el principio de optimalidad
Superpuestos
Algoritmos
Enfoque recursivo
Inducción / Inducción estructural
Divide & Conquer
Programación
Memoization (función recursiva memorizada)
Llenado de tabla
Problema
Encontrar la menor cantidad de ediciones para llegar de una
palabra a otra.
Operaciones de edición
Eliminación de una letra
Inserción de una letra
Substitución de una letra
Usos comunes
Corregir errores de tipeo
Detección de fraude
Encontrar variaciones en ADN
Edit distance
Entrada: dos cadenas A y B
Salida: la distancia de Levenshtein entre A y a B
LEV : (String , String ) → int
Observaciones
El orden de llenado satisface las “dependencias”
Complejidad temporal: O(n.m), espacial: O(n.m)
Se puede lograr complejidad espacial O(n + m)
Problemas, algoritmos y programación Programación dinámica
Otros problemas de cadenas
Transformar en palı́ndromo
Problema: Dada una cadena, encontrar la mı́nima cantidad de
ediciones para transformarla en palı́ndromo (capicúa)
Sub-problemas a considerar: todas las sub-cadenas (recortes)
de la original. Complejidad temporal: O(n2 )
Ejemplos
LIS([0, 8, 4, 12, 2, 10, 6, 14, 6, 9, 5]) → 4
LIS([2, 3, 7, 10, 15]) → 5
LIS([]) → 0
LIS([15, 10, 7, 3, 2]) → 1
Observaciones
Complejidad temporal: O(n.log (n))
Complejidad espacial: O(n)
Se puede aplicar sobre cualquier conjunto con orden total
Problemas, algoritmos y programación Programación dinámica
Problema: Cálculo de probabilidades
Ejemplo
La probabiliad de tirar un dado 6 veces y sumar 20 es la suma de
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 19
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 18
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 17
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 16
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 15
+ 1/6× la probabilidad de tirar un dado 5 veces y sumar 14
Definición recursiva
P(0, 0, ) = 1 y P( , 0, ) = 0
P(s, n, d) = di=1 d1 P(s − i, n − 1, d)
P
Observaciones
Hay sub-problemas superpuestos
d es siempre el mismo
Complejidad temporal: O(s.n.d) y espacial: O(s.n)
Problemas, algoritmos y programación Programación dinámica
Programación dinámica y juegos
Juegos
Algunos juegos pueden ser analizados con programación dinámica.
En especial aquellos que sean:
por turnos
finitos y sin empate
Sub-problemas
El itinerario más barato para todos los sub-conjuntos de ciudades
(sub-grafo inducido) y todas sus terminaciones.
La cantidad de sub-grafos es 2n
La cantidad de sub-problemas es O(2n .n)
C2 (int, int[][], SubConjunto, int) → int
C (n, c) = min0≤i<n {C2 (n, c, S, i)} siendo S el sub-conjunto
de todas las ciudades (impropio)
Problemas, algoritmos y programación Programación dinámica
Programación dinámica sobre subconjuntos
Definición recursiva de C2
C2 ( , , {t}, t) = 0
C2 (n, c, S, t) = mini∈S−{t} {c[i][t] + C2 (n, c, S − {t}, i)}
Dependencias de C2
El valor de C2 (n, c, m(S), t) depende de los valores de C2 para
todos los sub-conjuntos de S
Los sub-conjuntos de S se representan con un número menor
a m(S)
C2 se puede calcular en orden creciente de m(S)
Observaciones
Complejidad temporal: O(2n .n2 ), espacial: O(2n .n)
Es mejor que la fuerza bruta O(n!), para n = 25 es ∼ 1015
veces más rápido y necesita ∼ 1G de memoria.
Problemas, algoritmos y programación Programación dinámica