UNIDAD 2:
TEMA:
ESTRATEGIAS DE DISEÑO DE ALGORITMOS
(APUNTES DE APOYO A CLASES TEÓRICAS)
(TIEMPO DE EXPOSICIÓN: 2 HS)
.
1
Bibliografía:
1. Pareja Flores, Ojeda Aciego, Algoritmos y programación en Pascal. Versión
pdf. ó ISBN 8478972900, 9788478972906 Editorial Ra-ma
2
2. es.wikipedia.org/
3. Brassard, Gilles; Bratley, Paul (1997). «Exploración de grafos».
Fundamentos de Algoritmia. Madrid: PRENTICE HALL. ISBN: 84-89660-00-X.
4. http://www.lcc.uma.es/~av/Libro/
Técnicas de diseño de algoritmos
Rosa Guerequeta y Antonio Vallecillo
Servicio de Publicaciones de la Universidad de Málaga. 1998
ISBN: 84-7496-666-3
Cap 3-4-5-6
Objetivos:
1. Mostrar las sig. estrategias de diseño de algoritmos, con el objeto de mejorar
nuestros propios algoritmos.
3
Ellas son:
1) Gready :Voraz ó golosa ó algoritmos ávidos.
2) Divide y vencerás
3) Programación Dinámica
4) Vuelta atrás ó con retroceso ó Backtracking
5) otras
2. Mostrar el esquema general y ejemplos de aplicación de cada estrategia de
diseño de algoritmos.
Estrategia: Gready (Voráz)
Idea:
La estrategia de estos algoritmos es básicamente iterativa
Consiste en una serie de etapas.
En cada etapa se consume una parte de los datos y se construye una parte de la solución,
parando cuando se hayan consumido todos los datos.
Cada parte consumida se evalúa una única vez, siendo descartada o seleccionada,
de tal forma que si es seleccionada forma parte de la solución,
y si es descartada, no forma parte de la solución ni volverá a ser considerada para la misma
El nombre (Voraz-ávido-goloso) de esta estrategia es muy descriptivo:
• En cada etapa se consume (bocado) una parte de los datos.
• Se intentará que la parte consumida sea lo mayor posible, bajo ciertas condiciones.
4
Estrategia: Gready (Voráz)(Ejemplo I) Descomposición en factores primos
La descomposición de un número natural n en factores primos : 600 = 23 . 31 . 52
El proceso consiste en eliminar en cada etapa un divisor de n cuantas veces sea posible:
• en la primera etapa se elimina el 2, y se considera el correspondiente cociente, • en la segunda etapa se elimina el 3, y • así siguiendo hasta que el cociente sea 1, como lo hicimos en la clase teórica de Teorema
Fundamental de la Aritmética (TFA)
5
ordPar( p, a) = Sea (c, r) = cr10(a, p) Si r 0 luego (a, 0) Sino sea (m, q) = ordPar(p, c)
(m, q+1)
factorizar(a) = factorizar1 (a, 2, ) donde
factorizar1(a, p, L) = Si (a = 1) luego L Sino Sea (m, q) = ordPar(p, a)
factorizar1(m, proximoPrimo(p), si (q = 0) L sino L U {(p, q)} )
Estrategia: Gready (Voráz) Esquema general de un algoritmo voráz
(versión iterativa)
6
Resolver P (D: datos; var S: solucion);
Inicio
Generar la parte inicial de la solución S (y las condiciones iniciales)
Mientras (D ) {todavía quedan datos por procesar }
Extraer de D el máximo trozo posible T { D:= D – T }
Procesar T
Incorporar el procesado de T a la solucion S: { S := S T }
Fin_mientras
Fin
(versión recursiva)
7
Resolver P (D: datos; var S: solucion); ResolverP1 (Parte inicial de la solución S y las condiciones iniciales) Donde ResolverP1 (D:datos; var S: solución); Inicio Si (D = ) luego S sino Extraer de D el máximo trozo posible T Procesar T ResolverP1 (D-T, S T) Fin_si Fin
Estrategia: Gready (Voráz) Esquema general de un algoritmo voráz
Estrategia: Gready (Voráz) (Ejemplo II) Cambio con el menor nro. de billetes
8
"Se pide crear un algoritmo que permita a una máquina expendedora devolver el
cambio mediante el menor número de billetes posible, considerando que el
número de billetes es limitado, es decir, se tiene un número concreto de billetes de
cada tipo".
La estrategia a seguir consiste en seleccionar los billetes de mayor valor que no
superen la cantidad de cambio a devolver, en cada etapa.
Supongamos que se tiene la siguiente disponibilidad de billetes y monedas:
Hay que devolver la cantidad 110 pesos:
1) Se tomaría 1 billete de 50, quedando una cantidad de cambio a devolver de 60
pesos.
2) Como 50 (el billete) es aún menor que 60 pesos, se tomaría otro billete de 50,
quedando ahora una cantidad de cambio a devolver de 10 pesos.
3) Como 50 y 20 (la denominación de los billetes) son mayores que 10 se
desechan, tomando 1 billete de 5 pesos.
4) La cantidad de cambio a devolver ahora es 5 pesos.
5) Se tomaría otro billete de 5, pero ya no nos queda ninguno, entonces deberán
devolverse 5 monedas de 1 peso, terminando así el problema de forma correcta.
Luego 110$ = 2 billetes de 50$ + 1 billete de 5$ + 5 monedas de 1$
Estrategia: Gready (Voráz) (Ejemplo II) Cambio con el menor nro. de billetes
(Trabajo Práctico: codificación del programa que resuelve el problema)
10
El algoritmo incrementa continuamente el tamaño de un “árbol”,
comenzando por un nodo inicial (cualquiera) , al que se le van
agregando sucesivos nodos.
En cada iteración las aristas a considerar son aquellas que inciden en los nodos
que ya pertenecen al árbol, pero:
Vamos guardando en un conjunto C (Visitados) los nodos conexos con un nodo
inicial, y en un conjunto B las aristas que permiten mantener la conexión de todos
los nodos en C.
Finaliza cuando C = N (se haya visitado todos los nodos)
aristas ‘a’ / c(a) es mín donde a = {p, q} con p C y q C.
1 1 5 1 1 5
4
1
1
1 5
3 4
1
1 1 2
1 5
3 4 2
1
1 1
Estrategia: Gready (Voráz)(Ejemplo III) Arbol cubridor mínimo: algoritmo de Prim
11
Algoritmo:
arbolCubMinimo(G) = cub1({p}, { }) //p es cualquier nodo //
donde
cub1(C, B) = Si (C = N) luego B
Sino cub1 (C U {q}, B U {a})
Donde ‘a’ es una arista de G /
P(a) = {p,q} con p C y q C
y c(a) lo más pequeño posible
Estrategia: Gready (Voráz) (Ejemplo III) Arbol cubridor mínimo: algoritmo de Prim
Estrategia: Divide y Vencerás 12
Idea:
Básicamente esta estrategia consiste en:
Dado un problema P, con datos D:
• Si los datos permiten una solución directa para P, se ofrece esta solución;
• Sino se siguen las siguientes fases:
(a) Se dividen los datos D en varios conjuntos de datos más pequeños Di
.
(b) Se resuelven los problemas P(Di) parciales, sobre los conjuntos de
datos Di , recursivamente
(c) Se combinan las soluciones parciales, resultando así la solución final.
Estrategia: Divide y Vencerás Ejemplo I: Quicksort
13
Quicksort (Hoare en 1960 Complejidad O(n) = n log2(n))
Dado un vector D de elementos desordenados: - elegimos un elemento llamado pivote (puede ser el primero de D) - organizamos el resto de los elementos de manera tal que a la:
• izquierda del pivote: se encuentren todos los elementos que son menores que él (sub-vector1 ó D1)
• derecha del pivote: se encuentren todos los que sean mayores o iguales a él, (sub-vector2 ó D2)
- Aplicamos este proceso a los dos sub-vectores , componiendo la ordenación de cada sub-vector con el pivote: Quicksort(D1) ° Pivote ° Quicksort(D2)
Ejemplo en el pizarrón D =
20 5 22 13 33 10 7
14
Si longitud(D) = 0 luego ( vacío) si no Pivote := Primero(D) dividir D en dos partes: D1 y D2
donde todos los elementos de D1 son menores que Primero(D) y todos los elementos de D2 son mayores o iguales que Primero(D)
S1 := Ordenar D1 usando QuickSort S2 := Ordenar D2 usando QuickSort Devolver: S1 ° Pivote ° S2
En Haskell: ordenar D = quicksort (D) quicksort (D) = if vacia(D) then [] else quicksort(D1) ++ [primero(V)] ++ quicksort (D2) where D1 = [x | x <- resto(D) , x < primero(D)] D2= [x | x <- resto(D) , x >= primero(D)]
QuickSort: Algoritmo
Estrategia: Divide y Vencerás Ejemplo I: Quicksort
(Trabajo Práctico: codificación del programa que resuelve el problema)
Estrategia: Divide y Vencerás Esquema general:
15
Resolver P (D: datos; var S: solucion);
Inicio
Si los datos D admiten un tratamiento directo luego S
sino
Dividir D en varios subconjuntos de datos: D1, …, Dk
Resolver P(D1,S1);
…
Resolver P(Dk, Sk);
S:= Combinar(S1, …,Sk.)
Fin_si
Fin
Estrategia: Divide y Vencerás Ejemplo II: Búsqueda binaria
16
Búsqueda Binaria. Complejidad (O(n) = log2(n))
Comparar x con el elemento central:
Si x = elemento central ya hemos terminado,
Sino buscamos en la mitad del array que nos interese:
Si x < elemento central, buscamos en la primera mitad del array
Sino buscamos en la segunda mitad del array.
Repetimos este proceso comparando x con el elemento central del subarray
seleccionado, y así sucesivamente hasta que:
ó bien encontremos el valor x
ó bien podamos concluir que x no está
(porque el subarray de búsqueda está vacío).
17
Buscar (x:Tipo_elemento; L:Lista) : Posicion; {Devuelve la posición donde se encuentra x en L, caso contrario devuelve 0 } Buscar := buscar2 (x, L, 1, L.ult); Donde: buscar2 (x:Tipo_Elemento; L: Lista; i, j:Posición ): Posicion; Var K:Posicion; Inicio Si (i ≤ j) luego k := cociente (i + j, 2); Si (L.Elementos[k] = x) luego buscar2:=k sino Si (x < L.Elementos[k]) luego buscar2:=buscar2 (x, L, i, k – 1) Sino buscar2 := buscar2 (x, L, k + 1, j) sino 0 Fin
Estrategia: Divide y Vencerás Ejemplo II: Búsqueda binaria
Estrategia: Programación dinámica Esquema general:
18
La programación dinámica es un método para reducir el tiempo de ejecución de
un algoritmo mediante la utilización de subproblemas superpuestos y
subestructuras óptimas.
Una subestructura óptima significa que se pueden usar soluciones óptimas de
subproblemas para encontrar la solución óptima del problema en su conjunto.
En general, se pueden resolver problemas con esta técnica siguiendo estos tres
pasos:
1. Dividir el problema en subproblemas más pequeños.
2. Resolver estos problemas de manera óptima usando este mismo proceso de
tres pasos recursivamente.
3. Usar estas soluciones óptimas para construir una solución óptima al problema
original.
Un problema tiene subproblemas superpuestos cuando se utiliza a un mismo
subproblema varias veces para resolver diferentes problemas mayores.
Estrategia: Programación dinámica Ejemplo I: Sucesión de Fibonacci
19
Fn =
0 si n = 0
1 si n = 1
Fn-1 + Fn -2 si n > 1
Para n=3, F3 = F2 + F1
Para n=4, F4 = F3 + F2
Para n=5, F5 = F4 + F3
Supone calcular F2 más de dos veces
Para n=6, F6 = F5 + F4
20
Al calcular F6, se calculó F2 más de dos veces:
“Esto sucede siempre que haya subproblemas superpuestos:
desperdiciando tiempo recalculando las soluciones óptimas a problemas
que ya han sido resueltos anteriormente.”
Fibonacci (n: NATURAL): NATURAL; INICIO Si n = 0 luego 0 Sino Si n = 1 luego 1 Sino Fibonacci := Fibonacci(n-1)+ Fibonacci (n-2); Fin_Si FIN
Complejidad exponencial C(n) = n donde = 1+ 5
2
Estrategia: Programación dinámica Ejemplo I: Sucesión de Fibonacci
Estrategia: Programación dinámica Ejemplo I: Sucesión de Fibonacci
21
Esto se puede evitar: memorizando las soluciones que ya hemos calculado.
Entonces, si necesitamos resolver el mismo problema más tarde, podemos
obtener la solución de la lista de soluciones calculadas y reutilizarla.
Fibonacci (n: NATURAL): NATURAL; VAR Fib: ARRAY [0..n] of NATURAL; i: NATURAL; INICIO SI n = 0 luego 0 SINO SI n = 1 luego 1 SINO Fib[0] := 0; Fib[1] := 1; PARA i = 2 HASTA n HACER Fib[i] := Fib[i-1] + Fib[i-2] FINPARA Fibonacci:= Fib[n] FINSI FIN
Complejidad lineal C(n) = n
Estrategia: Programación dinámica Ejemplo I: Sucesión de Fibonacci
22
Un mejoramiento del algoritmo anterior consiste en quedarnos solo con los dos
últimos valores, (no con un vector de valores) calculados en cada iteración:
FUNCTION Fibonacci (n: NATURAL): NATURAL; INICIO SI n = 0 luego 0 SINO SI n = 1 luego 1 SINO Fib1 (0, 1 ,n-2) FIN Donde: Fib1 (x, y ,n) = Si n = 0 luego (x + y) SINO Fib1( y, x + y, n-1) FIN Complejidad lineal C(n) = n
Trabajo Práctico: Programar todas las versiones del problema
Estrategia: Programación dinámica Ejemplo II: El problema de la mochila
23
Resolver este problema seleccionando
los elementos más caros en primer
lugar, o los más livianos, no
necesariamente garantiza llegar a una
solución ideal.
Es posible que otras combinaciones de
elementos produzcan mejores
resultados!
Imagen extraída de wikipedia
Estrategia: Programación dinámica Ejemplo II: El problema de la mochila
24
Sea w = capacidad de la mochila
Sea wi = la capacidad de cada caja
Sea vi = el valor de la caja i
Sea Z = función a maximizar
Z(w) = máximo valor obtenible con una capacidad de w.
Si la solución óptima de Z(w) incluye el objeto i, entonces
la eliminación del objeto i nos proporcionará la solución óptima de Z(w-wi).
Z(w) simplemente es Z(w-wi)+vi para algún i.
Como no sabemos para qué i, tenemos que probar todas las posibilidades, es decir:
𝑍 𝑤 = max { 𝑍 𝑤 − 𝑤𝑖 + 𝑣𝑖 } i: wi w
Trabajo Práctico: Programar para obtener la solución al problema
Estrategia:
Vuelta atrás ó retroceso ó backtracking 25
Algunas veces debemos encontrar una solución óptima a un subproblema, pero ocurre que no es aplicable ninguna teoría que hasta ahora hemos visto. Entonces debemos recurrir a una búsqueda exhaustiva sistemática.
Consideremos el problema de completar un rompecabezas: En un momento dado, se han colocado unas cuantas piezas, y se tantea la colocación de una nueva pieza. Por lo general, sería posible continuar de diversos modos, y cada uno de ellos podría ofrecer a su vez diversas posibilidades, multiplicándose así las posibilidades de tanteo. Por otra parte, el tanteo de soluciones supone muchas veces abandonar una vía muerta cuando se descubre que no conduce a la solución, deshaciendo algunos movimientos y regresando por otras ramas del árbol de búsqueda a una posición anterior. De ahí viene la denominación de esta clase de algoritmos: vuelta atrás o búsqueda con retroceso
26
Estrategia: Vuelta atrás ó retroceso ó backtracking
Ejemplo I: El problema de las 8 reinas
El problema de las ocho reinas es un juego en el que se colocan ocho reinas en
un tablero de ajedrez sin que se amenacen entre ellas:
En el ajedrez la reina amenaza a aquellas piezas que se encuentren en su misma
fila, columna o diagonal.
Una solución correcta entre las 92 posibles sería la siguiente:
27
Estrategia: Vuelta atrás ó retroceso ó backtracking
Ejemplo I: El problema de las 8 reinas
Como cada reina puede amenazar a todas las reinas que estén en la misma fila,
cada una de ellas debe situarse en una fila diferente.
Podemos representar las 8 reinas mediante un vector V de 8 elementos,
teniendo en cuenta que cada índice i del vector representa una fila y el valor V[i]
una columna.
Así cada reina estaría en la posición (i, v[i]) para i = 1..8.
Por ejemplo, el Vector V = [3,1,6,2,8,6,4,7]
nos indica que:
• la reina 1 esta en la columna 3, fila1;
• la reina 2 en la columna 1, fila 2;
• la reina 3 en la columna 6, fila 3;
• la reina 4 en la columna 2, fila 4;
• etc...
Como se puede ver esta solución es incorrecta
ya que estarían la reina 3 y la 6 en la misma columna:
V[3] = V[6] = 6.
28
Estrategia: Vuelta atrás ó backtracking
Ejemplo I: El problema de las 8 reinas
Para decidir en cada etapa cuáles son los valores que puede tomar cada uno de los elementos de V debemos tener en cuenta “restricciones “ a fin de que el número de opciones a considerar en cada etapa sea el menor posible. Podemos diferenciar dos tipos de restricciones: • Restricciones explícitas: Formadas por reglas que restringen los valores que pueden tomar los elementos de V a un conjunto determinado. En nuestro problema este conjunto es S = {1,2,3,4,5,6,7,8}. (valores de las columnas del tablero de ajedrez) • Restricciones implícitas. Indican la relación existente entre los posibles valores V para que éstos puedan formar parte de una 8-tupla solución. Deberán formar parte de la lógica de nuestro algoritmo. • Restricción 1: “dos reinas no pueden situarse en la misma columna” y por tanto no puede haber dos
valores de V iguales, entonces V[i] ≠ V[j] • (la propia definición de la tupla impide situar a dos reinas en la misma fila, con lo cual tenemos
cubiertos los dos casos, el de las filas y el de las columnas).
• Restricción 2: “dos reinas no pueden estar en la misma diagonal”. Sean x y x’= valores de fila, y, y’ = valores de columnas. Sean Ri = (x,y) y Rj = (x’, y’) las coordenadas de las reinas i y j en el tablero de ajedrez. Para que no se amenacen en la diagonal debe ocurrir que |x – x’| ≠ |y – y’|
29
Estrategia: Vuelta atrás ó backtracking
Esquema general:
La estrategia de diseño de Vuelta Atrás es un método exhaustivo de tanteo (prueba y error) que se caracteriza por un avance progresivo en la búsqueda de una solución mediante una serie de etapas. En dichas etapas se presentan unas opciones cuya validez ha de examinarse con objeto de seleccionar una de ellas para proseguir con el siguiente paso. Este comportamiento supone la generación de un árbol y su examen y eventual poda hasta llegar a una solución o a determinar su imposibilidad.
Este avance se puede detener cuando se alcanza una solución, o bien si se llega a una situación en que ninguna de las soluciones es válida; en este caso se vuelve al paso anterior, lo que supone que deben recordarse las elecciones hechas en cada paso para poder probar otra opción aún no examinada. Este retroceso (vuelta atrás) puede continuar si no quedan opciones que examinar hasta llegar a la primera etapa (punto de partida).
El agotamiento de todas las opciones de la primera etapa supondrá que no hay solución posible pues se habrán examinado todas las posibilidades.
Arbol para 4 reinas 30
31
Estrategia: Vuelta atrás ó backtracking
Esquema general:
VueltaAtras(etapa);
Inicio
InicializarOpciones;
Repetir
SeleccionarNuevaOpcion;
Si Aceptable (se cumplen las restricciones) luego AnotarOpcion;
Si SolucionIncompleta luego VueltaAtras(etapa_siguiente);
Si NO(éxito) luego CancelarAnotacion;
Sino (* solucion completa *)
exito:=TRUE
Fin_si
Fin_si
Hasta (exito) ó (UltimaOpcion)
Fin
Trabajo Práctico: Programar para obtener la solución al problema
Hasta aquí vimos ….
Estrategias
de diseño
de
Algoritmos
Idea
Ejemplos: Factorización, cambio, arbol cub. Voráz
(Algoritmos ávidos)
32
Esquema General
Idea
Ejemplos: QuickSort, búsqueda binaria Divide y Vencerás
Esquema General
Idea
Ejemplos: Fibonacci, problema de la mochila Programación
Dinámica Esquema General
Idea
Ejemplos: el problema de las 8 reinas Vuelta atrás
(Backtracking) Esquema General
Top Related