, Algoritmos y Programación 6 de Abril de 2010 Alejandro Deymonnaz
Definiciones Divide and Conquer Deiniciones previas Problema recursivo Un problema recursivo es un problema que se puede resolver en base a instancias más chicas del mismo problema. Problema Un problema define un valor buscado (valor de verdad, valor óptimo, etc) en base a ciertos parámetros genéricos (variables de entrada del problema). Instancia de un problema Una instancia de un problema es un problema para el cual se determinaron las variables de entrada. Nota: una instancia de un problema tiene un resultado determinado.
Caso base Técnicas Definiciones Divide and Conquer Pregunta: Si cada instancia se resuelve en base a una o más instancias más chicas cuando termina? El problema se termina de calcular cuando se llega al caso base, que es una instancia trivial que se responde inmediatamente sin resolver otros subproblemas. (Nota: puede haber varios casos base)
Definiciones Divide and Conquer Ejemplos Calcular el n-ésimo término de la sucesión de Fibonacci. Calcular el n-ésimo término de cualquier sucección recursiva lineal. Ordenar un arreglo de n números. Calcular el camino mínimo entre dos nodos de un grafo. Calcular la distancia de edición entre dos palabras Determinar cuántas letras se deben agregar como mínimo a una palabra para que sea un paĺındromo. Nota: Un problema recursivo no necesariamente se implementa con una función recursiva.
Divide and Conquer Técnicas Definiciones Divide and Conquer Divide and Conquer es una técnica de resolución de problemas. En D&C la resolción de un problema se separa en dos partes: Divide: Partir el problema en uno o más sub-problemas más pequeños. Conquer o combine: Resolver los sub-problemas y volver a juntarlos para obtener la solución del problema original.
Definiciones Divide and Conquer Ejemplo - Ordenar un arreglo Dos algoritmos distintos de Divide and Conquer para ordenar: Quicksort Mergesort La estrategia es: 1 partir el arreglo en dos partes 2 ordenar cada parte usando el propio algoritmo 3 combimar las partes ordenadas.
Definiciones Divide and Conquer Ejemplo - Quicksort Algorithm 1.1: quicksort(array v) if v.size() <= 1 then return pivot v 0 vlow {v i v i < pivot} veq {v i v i = pivot} vhigh {v i v i < pivot} return (quicksort(vlow) + veq + quicksort(vhigh)) Divide: Divide el arreglo en los menores que el pivote y los mayores que el pivote. Combine: Ordena los menores por un lado, los mayores por otro lado y combina trivialmente.
Definiciones Divide and Conquer Ejemplo - Mergesort Algorithm 1.2: mergesort(array v) if v.size() <= 1 then return mid v.size()/2 vfirst {v i i [0..mid)} vsecnd {v i i [mid..v.size())} return (merge(mergesort(vfirst), mergesort(vsecnd))) Divide: Parte el arreglo a la mitad. Combine: Ordena cada midad y hace el merge ordenado.
Definiciones Divide and Conquer es una técnica de resolución de problemas. Se utiliza para resolver problemas recursivos. IMPORTANTE Programación dinámica es sumamente útil cuando los sub-problemas comparten sub-sub-problemas entre sí.
Ejemplo - Fibonacci Técnicas Definiciones Divide and Conquer Calcular los términos de la sucesión de Fibonacci puede verse como un problema de programación dinámica. F (n) = F (n 1) + F (n 2) Si lo planteamos como un problema de Divide and Conquer: para resolver F (6), hay sub-problemas involucrados en resolver F (5) que también servirán para resolver F (4). Nota: Los sub-problemas comparten sub-sub-problemas entre sí.
Ejemplo - Fibonacci Técnicas Definiciones Divide and Conquer F(5) = F(4) + F(3) F(4) = F(3) + F(2) F(3) = F(2) + F(1) F(2) = F(1) + F(0) F(2) = F(1) + F(0) F(3) = F(2) + F(1) F(2) = F(1) + F(0) Para calcular F (n), se llama a la función F (0) una cantidad igual a F (n) veces.
Definiciones Divide and Conquer Principio de optimalidad Principio de optimalidad El principio de optimalidad establece que si S es solución de un problema P entonces una parte de S (subsolución) es solución del subproblema correspondiente de P. Ejemplos: Si un camino más corto S entre u y v en un grafo pasa por w, entonces la porción de S entre u y w es un camino más corto entre u y w. El contrarecíproco del principio de optimalidad garantiza que un problema que lo satisface puede ser escrito como problema recursivo.
Dinámica de la clase Vamos a ver varios problemas. Vamos a pensar algoritmos de programación dinámica que los resuelvan. Vamos a analizar algunas soluciones ya programadas. Algoritmos programación.
Ejemplo: de Juegos Los problemas de juegos son un caso particular donde varios jugadores juegan con una estrategia definida y el objetivo del problema es alguno de estos: Saber quién gana el juego (o equivalentemente saber si el primer jugador gana o pierde), Saber en cuántas jugadas como mínimo gana o pierde un determinado jugador, Saber si el juego puede no terminar, etc. Nos concentraremos en los juegos: Simétricos: Cada jugador tiene las mismas reglas de juego. Cada jugador juega de la mejor manera. Terminan.
de juegos como problemas recursivos En estos juegos, decimos que cada posición o estado del juego puede ser: una posición ganadora: si jugando de manera óptima se asegura la victoria una posición perdedora: si aún jugando de manera óptima no se puede asegurar la victoria En términos del problema recursivo una posición p es ganadora sii: ( j jugadas(p))/jugar(p, j) es perdedora
Ejemplo: Cálculo de probabilidades En muchos problemas es necesario calcular la probabilidad de un evento o la esperanza de una variable, sabiendo algunas probabilidades particulares. Ejemplo: Calcular la probabilidad de sumar M tirando N dados equilibrados de K caras. Si cada instancia se puede partir utilizando la probabilidad de un evento conocido (ej: la probabilidad de que el primer dado sea un 1 ) se puede plantear como problema recursivo sumando probabilidades condicionales.
Solución Subproblema: P(m, n) Probabilidad de sumar m tirando n dados de K caras. (Nota: K es un dato global. Las caras son de 1 a K.) 0 m < 0 1 m = 0 n = 0 P(m, n) = 0 m > 0 n = 0 K 1 i=1 P(m i, n 1) K m 0 n > 0 Solución: P(M, N).
Ejemplo: Paĺındromos Dado un string S, determinar la cantidad de caracteres mínima que hay que agregarle (antes, después y/o entre medio) para obtener un paĺındromo. Un paĺındromo es un string que es igual a su reverso, como sometemos, seres o neuquen. Ejemplo: nequ neuquen nequ nequqen ababbba abbbabbba ababbba ababbbaba
Implementación Se debe implementar una función que dada una cadena de caracteres de longitud n, devuelva un entero con la cantidad mínima de caracteres que se deben insertar en ella (antes, después y/o entre medio) para que el resultado sea un paĺındromo. 1 int palindromo(const string& s) { 2 int n = s.size(); 3... 4 return...; 5 }
Subproblemas Pensemos en los subproblemas: Sea S = s 1 s 2 s 3 s 4 s n 1 s n Si s 1 = s n, S es paĺındromo s 2 s n 1 es paĺındromo. axyza XYZ Si s 1 s 2 s n 1 es paĺındromo s n s 1 s 2 s n = s n S es paĺındromo. S = axyzb: axyz baxyzb Si s 2 s 3 s n es paĺındromo s 1 s 2 s n s 1 = Ss 1 es paĺındromo. S = axyzb: XYZb axyzba
Algoritmo Planteamos 3 casos recursivos y 1 caso base. palindromo(s) = res: Si n 1, S es paĺındromo, entonces res = 0. Si no (caso recursivo): Si s 1 = s n, considerar res = palindromo(s 2 s n 1 ) Considerar res = palindromo(s 1 s n 1 ) + 1 Considerar res = palindromo(s 2 s n ) + 1 Devolver el mínimo de los res considerados.
Importante Para usar estas técnicas Hay que definir el problema como un problema recursivo. Hay que separar qué parámetros son globales a todos los sub-problemas. No necesariamente el resultado es una función recursiva.
S es global. i, j, los índices del intervalo son locales. Algorithm 2.1: Palindromo(int i, int j) if j i 1 then return (0) res if s i = s j 1 then res min(res, Palindromo(i + 1, j 1)) res min(res, Palindromo(i, j 1) + 1) res min(res, Palindromo(i + 1, j) + 1) return (res) Solución: Palindromo(0, length(s))
Algorithm 2.2: Palindromo(int i, int j) if memoria [i] [j] NO CALCULADO then return (memoria [i] [j]) if j i 1 then return (0) res if s i = s j 1 then res min(res, Palindromo(i + 1, j 1)) res min(res, Palindromo(i, j 1) + 1) res min(res, Palindromo(i + 1, j) + 1) memoria [i] [j] res return (res)
Pasemos a problemas de la vida real.
El fede...
... se compró un corsa...
... y quiere ver a la aplanadora...
... del rock and roll...
... en Tilcara.
Son 1700 Km. Entonces a él se le ocurrió ponerle GNC...
... porque el GNC es rock and roll.
Pero el GNC tiene menos autonomía y hay que parar a cargar más.
Además no hay GNC en todos los pueblos.
Problema 1: Dada una ruta y la autonomía de una carga de gas, decidir en dónde, parar minimizando la cantidad de paradas.
Solución Definimos el subproblema: P 1 (u) Conjunto de lugares donde cargar gas para ir desde Buenos Aires hasta la ciudad u. P 2 (u) Conjunto de lugares donde cargar gas para ir desde la ciudad u hasta Tilcara. { P 1 (u) = min {x} P 1 (x) si puedo ir(x, u) P 1 (Buenos Aires) = (asumimos que sólo miramos los x que están más cerca de Buenos Aires (caso base)) Se puede definir análogamente para P 2 usando puedo venir en vez de puedo ir.
Variantes Problema 2: Mismo problema, pero ahora la autonomía de una carga de gas depende de la estación donde se carga (presión de carga, calidad del gas, condiciones atmosféricas). Cambia la definición de puedo ir y puedo venir.
Hoy vimos 1 Técnicas Definiciones Divide and Conquer 2
de hoy ACM-ICPC UVA - 4212 - Candy - http://acmicpc-live-archive.uva.es/nuevoportal/ data/problem.php?p=4212