1 - WordPress.com

Anuncio
.
1
UNIDAD 2:
TEMA:
ESTRATEGIAS DE DISEÑO DE ALGORITMOS
(APUNTES DE APOYO A CLASES TEÓRICAS)
(TIEMPO DE EXPOSICIÓN: 2 HS)
Bibliografía:
2
1. Pareja Flores, Ojeda Aciego, Algoritmos y programación en Pascal. Versión
pdf. ó ISBN 8478972900, 9788478972906 Editorial Ra-ma
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/CAP6.pdf: Técnicas de diseño de algoritmos
Objetivos:
3
1. Mostrar algunas estrategias de diseño de algoritmos, con el objeto de
mejorar nuestros propios algoritmos.
Ellas son:
1) Gready :Voraz ó golosa ó algoritmos ávidos.
2) Divide y vencerás
3) De programación Dinámica
4) De vuelta atrás ó con retroceso ó backtracking
5) otras
2. Mostrar el esquema general y ejemplos de aplicación de cada estrategias de
diseño de algoritmos.
Estrategia: Gready (Voráz)
4
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.
Estrategia: Gready (Voráz)(Ejemplo I)
Descomposición en factores primos
5
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)
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
6
(versión iterativa)
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
Estrategia: Gready (Voráz)
Esquema general de un algoritmo voráz
7
(versión recursiva)
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) (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".
Supongamos que se tiene la siguiente disponibilidad de billetes y monedas:
La estrategia a seguir consiste en seleccionar los billetes de mayor valor que no
superen la cantidad de cambio a devolver, en cada etapa.
Estrategia: Gready (Voráz) (Ejemplo II)
Cambio con el menor nro. de billetes
(Trabajo Práctico: codificación del programa que resuelve el problema)
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 III)
Arbol cubridor mínimo: algoritmo de Prim
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.
 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.
 En cada iteración las aristas a considerar son aquellas que inciden en los nodos
que ya pertenecen al árbol, pero:
aristas ‘a’ / c(a) es mín donde a = {p, q} con p  C y q C.
 Finaliza cuando C = N (se haya visitado todos los nodos)
1
1
1
5
1
1
5
1
4
1
1
3
1
5
1
4
1
2
1
2
3
1
5
1
4
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: 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 D, 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
Estrategia: Divide y Vencerás
Ejemplo I: Quicksort
(Trabajo Práctico: codificación del programa que resuelve el problema)
14
QuickSort: Algoritmo
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)]
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).
Estrategia: Divide y Vencerás
Ejemplo II: Búsqueda binaria
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: 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.
Un problema tiene subproblemas superpuestos cuando se utiliza a un mismo
subproblema varias veces para resolver diferentes problemas mayores.
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.
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,
Para n=4,
Para n=5,
Para n=6,
F 3 = F2 + F1
F 4 = F3 + F 2
F5 = F4 + F3
F 6 = F 5 + F4
Supone calcular F2 más de dos veces
Estrategia: Programación dinámica
Ejemplo I: Sucesión de Fibonacci
20
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
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.”
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!
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
Estrategia: Vuelta atrás ó retroceso ó backtracking
Ejemplo I: El problema de las 8 reinas
26
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:
Estrategia: Vuelta atrás ó retroceso ó backtracking
Ejemplo I: El problema de las 8 reinas
27
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.
Estrategia: Vuelta atrás ó backtracking
Ejemplo I: El problema de las 8 reinas
28
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’|
Estrategia: Vuelta atrás ó backtracking
Esquema general:
29
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.
Estrategia: Vuelta atrás ó backtracking
Esquema general:
30
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 ….
31
Voráz
(Algoritmos ávidos)
Estrategias
de diseño
de
Algoritmos
Divide y Vencerás
Idea
Ejemplos: Factorización, cambio, arbol cub.
Esquema General
Idea
Ejemplos: QuickSort, búsqueda binaria
Esquema General
Programación
Dinámica
Vuelta atrás
(Backtracking)
Idea
Ejemplos: Fibonacci, problema de la mochila
Esquema General
Idea
Ejemplos: el problema de las 8 reinas
Esquema General
Descargar