Se desea calcular el tiempo de ejecucin de la funcin f, cuyo cdigo

Anuncio
PROBLEMA 7 (PROGRAMACIÓN DINÁMICA)
Para el alineamiento global de secuencias de ADN y de proteínas (emparejamiento de
símbolos de las secuencias) se usan algoritmos, basados en Programación Dinámica, que
producen el alineamiento óptimo de dos secuencias. Supóngase que en la búsqueda de dicho
alineamiento se utiliza una función que pondera positivamente la coincidencia de dos
elementos semejantes en las secuencias y negativamente la aparición de huecos y la
discordancia de símbolos en el emparejamiento. Por ejemplo, el siguiente alineamiento para
las secuencias “ASILO” y “SELLOS” puntuaría positivamente los emparejamientos que
aparecen encuadrados y negativamente los demás:
A
S
-
I
L
O
-
-
S
E
L
L
O
S
Para dos secuencias A = a1a2…an, B = b1b2…bm, la ecuación general de la puntuación Sij
correspondiente al alineamiento óptimo de las subsecuencias a1a2…ai y b1b2…bj será:
⎧ Si −1, j −1 + C (ai , b j )
⎪
Sij = max ⎨
Si −1, j − 1
⎪
Si , j −1 − 1
⎩
⎧+1 si x = y
con C ( x, y ) = ⎨
⎩−1 si x ≠ y
Nótese que:
- la coincidencia de dos elementos se pondera con +1 y la discordancia como –1
- Un salto en la secuencia se pondera con –1 (dos últimos casos de Sij)
Se pide:
a) Completar la ecuación en recurrencias para todos los casos, explicando su significado
(la expresión anterior sólo es válida en el caso general, y no para los casos base).
b) Obtener un algoritmo para el cálculo de Sij que permita obtener la puntuación
correspondiente al alineamiento óptimo de dos secuencias dadas, A y B, de longitudes
N y M respectivamente.
c) Razonar cómo se obtendría uno de los posibles alineamientos óptimos entre las dos
secuencias A y B, y utilizando en el apartado b) las estructuras de datos que serían
necesarias para ello.
Ejemplo: Alineamiento óptimo de las secuencias “ASILO” y “SELLOS”
O
S
S
E
L
L
0
-1
-2
-3
-4
-5
-6
A
-1
-1
-2
-3
-4
-5
-6
S
-2
0
-1
-2
-3
-4
-4
I
-3
-1
-1
-2
-3
-4
-5
L
-4
-2
-2
+0
-1
-2
-3
O
-5
-3
-3
-1
-1
0
-1
A
S
-
I
L
O
-
-
S
E
L
L
O
S
Solución
a) Ecuación en recurrencias
Sea S[i,j] la puntuación del alineamiento óptimo de las secuencias A[1..i] y B[1..j]
(0<=i<=N, 0<=j<=M):
⎧max( S [i − 1, j − 1] + C ( A[i ], B[ j ]), S [i − 1, j ] − 1, S [i, j − 1] − 1) , si i > 0 ∧ j > 0
S [i, j ] = ⎨
, si i = 0 ∨ j = 0
⎩− max(i, j )
b) Obtención de matrices:
tipo Direccion = Entero // 1=diagonal, 2=arriba, 3=izquierda
func max3(x, y, z: Entero) dev (indice,
alg
si x >= y:
si x >= z:
< indice, valor > := < 1, x
|otras:
< indice, valor > := < 3, z
fsi
|otras:
si y >= z:
< indice, valor > := < 2, y
|otras:
< indice, valor > := < 3, z
fsi
fsi
fin
valor: Entero)
>
>
>
>
func matricesAlineamiento(A: array[1..N] de Simbolo, B: array[1..M] de Simbolo)
dev (S: array[0..N,0..M] de Entero, D: array[0..N,0..M] de Direccion)
var
i, j: Entero
alg
desde j := 0 hasta M
< S[0,j], D[0,j] > := < j, 3 > // izquierda
fdesde
desde i := 1 hasta N
< S[i,0], D[i,0] > := < i, 2 > // arriba
desde j := 1 hasta M
si A[i] = B[j]:
< S[i,j], D[i,j] > := max3(S[i-1,j-1]+1,S[i-1,j]-1,S[i,j-1]-1)
|otras:
< S[i,j], D[i,j] > := max3(S[i-1,j-1]-1,S[i-1,j]-1,S[i,j-1]-1)
fsi
fdesde
fdesde
fin
func puntAlineamientoOpt(A: array[1..N] de Simbolo, B: array[1..M] de Simbolo)
dev (punt: Entero)
var
S: array[0..N, 0..M] de Entero, D: array[0..N, 0..M] de Direccion
alg
< S, D > := matricesAlineamiento(A, B)
punt := S[N, M]
fin
c) Obtención del alineamiento óptimo de las secuencias A y B:
Una vez obtenidas las matrices S y D:
• S[i,j] es la puntuación del alineamiento óptimo de las subsecuencias A[1..i] y B[1..j]
• D[i,j] contiene el código de la decisión óptima utilizada para S[i,j]:
o 1=diagonal: la posición i de A se alinea con la posición j de B
o 2=arriba: la posición i de A se alinea con un hueco en B, es decir, se produce un
desplazamiento en B
o 3=izquierda: la posición j de B se alinea con un hueco en A, es decir, se produce
un desplazamiento en A
El proceso para obtener el alineamiento óptimo comienza por la parte final de las secuencias, lo
que corresponde con la posición (N, M) de las matrices, hasta llegar a la posición (0, 0),
correspondiente a las subsecuencias vacías. En cada paso, según el valor de D[i, j] se actualizan
los valores de i y j:
• Si D[i, j] = 1 (diagonal): tanto i como j decrecen una unidad (no se genera hueco en
ninguna de las secuencias)
• Si D[i, j] = 2 (arriba): i decrece una unidad y j se mantiene igual, lo que equivale a que se
genere un hueco para la secuencia B
• Si D[i, j] = 3 (izquierda): j decrece una unidad e i se mantiene igual, lo que equivale a que
se genere un hueco para la secuencia A
func alineamientoOpt(A: array[1..N] de Simbolo, B: array[1..M] de Simbolo)
dev (AA, BB: Lista de Simbolo)
var
S: array[0..N, 0..M] de Entero
D: array[0..N, 0..M] de Direccion
i, j: Entero
alg
< S, D > := matricesAlineamiento(A, B)
< i, j > := < N, M >
< AA, BB > := < listaVacia, listaVacia >
mientras i > 0 Y j > 0
si D[i, j] = diagonal:
AA := A[i] + AA
BB := B[j] + BB
< i, j > := < i - 1, j - 1 >
| D[i, j] = arriba:
AA := A[i] + AA
BB := ‘-‘ + BB
// hueco en B
i := i - 1
| otras:
AA := ‘-‘ + AA
// hueco en A
BB := B[j] + BB
j := j - 1
fsi
fmientras
mientras i > 0
AA := A[i] + AA
BB := ‘-‘ + BB
// hueco en B
i := i - 1
fmientras
mientras j > 0
AA := ‘-‘ + AA
BB := B[j] + BB
// hueco en B
j := j - 1
fmientras
fin
PROBLEMA 8 (PROGRAMACIÓN DINÁMICA)
La circulación viaria de una ciudad viene representada por un grafo dirigido en el que los
vértices se corresponden con intersecciones de calles y las aristas con las propias vías de
tráfico, de manera que se dispone de la anchura, en número de carriles, de cada vía. Así,
A[i, j] representa el número de carrilles de la vía que une la intersección i con la intersección
j, en el sentido “desde i a j” (0 si no hay una vía que una directamente a las dos intersecciones
en el sentido indicado). Se define la anchura de un trayecto entre dos intersecciones a la
correspondiente al tramo de menor anchura. Aplicar la técnica de Programación Dinámica
para:
(a) implementar un algoritmo que obtenga la anchura correspondiente al trayecto de
mayor anchura entre cada par de intersecciones.
(b) implementar un algoritmo que obtenga el trayecto de mayor anchura desde una
intersección origen hasta otra destino.
Solución
(a) Sea Amax[i, j, k] la anchura del trayecto de anchura máxima que va desde i hasta
j pudiendo pasar por los nodos intermedios 1..k:
Amax[i, j, 0] = A[i, j]
Amax[i, j, k] = max(Amax[i, j, k – 1], min(Amax[i, k, k - 1], Amax[k, j, k - 1]), i ≠ k, j ≠ k
Amax[k, j, k] = A[k, j, k – 1]
Amax[i, k, k] = A[i, k, k – 1]
La estructura es la misma que la del algoritmo de Floyd. Podemos pues utilizar una
matriz Amax[1..N, 1..N] para almacenar los resultados para las etapas sucesivas
k = 0, 1, …, N, ya que la fila k y la columna k no cambian en la etapa k.
Sea P[i, j] un nodo intermedio del trayecto de anchura máxima que va desde i hasta j
(cero si no existen nodos intermedios).
func carriles(A:array [1..N, 1..N] de Entero) dev (Amax, P: array[1..N, 1..N] de Entero)
var
i, j, k: Entero
alg
< Amax, P > := < A, array de ceros >
desde k := 1 hasta N
desde i := 1 hasta N
desde j := 1 hasta N
si i ≠ k Y j ≠ k:
si min(Amax[i, k], Amax[k, j]) > Amax[i, j]:
Amax[i, j] := min(Amax[i, k], Amax[k, j])
P[i, j] := k
fsi
fsi
fdesde
fdesde
fdesde
fin
(b)
proc imprime-camino(i,j: Entero; Amax, P: array[1..N, 1..N] de Entero)
alg
si Amax[i, j] > 0:
imprimir(“Camino de “+ i + “ a “ + j + “: “)
imprimir(i)
imprime-camino-aux(i, j, P)
imprimir(j)
| otras:
imprimir(“No hay camino desde “ + i + “ a “ + j)
fsi
fin
proc imprime-camino-aux(i, j:Entero; P: array[1..N, 1..N] de Entero)
var k: Entero
alg
k := P[i, j]
si k > 0:
imprime-camino-aux(i, k, P)
imprimir(k)
imprime-camino-aux(k, j, P)
fsi
fin
PROBLEMA 9 (BACTRACKING)
Dada una fórmula lógica, formada por una serie de variables (lógicas) y los operadores de
conjunción (∧), disyunción (∨) y negación (¬), se desea saber si se puede satisfacer, es decir,
se puede establecer una asignación a cada variable de manera que la fórmula se evalúe a
cierto. Se dispone de los tipos:
Variable: Entero
// sirve para identificar o representar a una variable lógica
Variables: array [] de Variable
// un conjunto de variables lógicas
Formula: (descripción oculta)
// representa una fórmula lógica
Asimismo, se dipone de las funciones:
func esCierto(f: Formula) dev (b: Logico)
func esFalso(f: Formula) dev (b: Logico)
// indica si f es la fórmula lógica cierto
// indica si f es la fórmula lógica falso
func varsFormula(f: Formula) dev (vs: Variables)
// devuelve las variables
// que aparecen en f
func asigna(f: Formula; v: Variable; b: Logico) dev (f1: Formula)
// devuelve la Formula equivalente al asignar el valor b (cierto o falso)
// a la variable v en la fórmula f; no necesariamente todas las variables
// de f aparecen en f1
Ejemplos:
asigna(((¬x1∨x2∨x3)∧(x1∨x4)∧(x1∨¬x4)), x1, cierto) Æ (x2∨x3)
asigna(((¬x1∨x2∨x3)∧(x1∨x4)∧(x1∨¬x4)), x1, falso) Æ (x4∧¬x4)
Mediante la técnica de Backtracking, implementar la función SAT(f: Formula) dev (b:
Logico) que devuelva cierto si f se puede satisfacer y falso en caso contrario.
Solución
Clase EsquemaBtUna
func btUna(x: Etapa) dev (exito: Logico)
var
xsig: Etapa
cand: Candidatos
alg
si esSolucion(x):
exito := cierto
| otras:
exito := falso
cand := calculaCandidatos(x)
mientras NO exito Y quedanCandidatos(cand)
xsig = seleccionaCandidato(cand, x)
si esPrometedor(cand, x, xsig):
anotaSolucion(cand, x, xsig)
exito := btUna(xsig)
si NO exito:
cancelaAnotacion(cand, x, xsig)
fsi
fsi
fmientras
fsi
fin
…
fClase
Clase SATBT extiende a EsquemaBtUna
// No hace falta la clase Solucion
Clase Etapa
f: Formula // la fórmula a considerar
fClase
Clase Candidatos
vLog: array [1..2] de Logico := {cierto, falso}
i: Entero // para recorrer el array
fClase
func SAT(f: Formula) dev (b: Logico)
var
x: Etapa
alg
x.f := f
b := btUna(x)
fin
func esSolucion(x: Etapa) dev (b: Logico)
alg
b := esCierto(x.f)
fin
func calculaCandidatos(x: Etapa) dev (cand: Candidatos)
alg
cand.i := 0
fin
func quedanCandidatos(cand: Candidatos) dev (b: Logico)
alg
b := (cand.i < 2)
fin
func seleccionaCandidato(cand: Candidatos, x: Etapa) dev (xsig : Etapa)
var
varsNoInstanciadas: Variables
alg
cand.i := cand.i + 1
varsNoInstanciadas := varsFormula(x.f) // seguro que el array no está vacío
xsig.f := asigna(x.f, varsNoInstanciadas[1], Candidatos.vLog[cand.i])
fin
func esPrometedor(cand: Candidatos, x, xsig: Etapa) dev (b: Logico)
alg
b := (esFalso(xsig.f) ≠ falso)
fin
proc anotaSolucion(cand: Candidatos, x, xsig: Etapa)
alg
fin
proc cancelaAnotacion(cand: Candidatos, xsig: Etapa)
alg
fin
PROBLEMA 10 (BACTRACKING)
Se tiene una cantidad de dinero C distribuida en diferentes billetes y monedas, con la que se
necesita realizar una serie de pagos. Para realizar cada pago se usarán los billetes y monedas
disponibles, teniendo en cuenta que quien recibe el pago verá satisfecha al menos la cantidad
que se le debía, sin estar obligado a devolver cambio.
Nota: Se deben usar las siguientes variables
cantidadDisponible: Entero
// cantidad en céntimos de euro
monedasDisponibles: array[1..15] de Entero
// número de monedas o
// billetes de cada tipo
valoresMonedas: array[1..15] := {50000, 20000, 10000, 5000, 2000,
1000, 500, 200, 100, 50, 20, 10, 5, 2, 1}
facturas: array[1..N] de Entero
// las N cantidades a pagar
pagos: array[1..N, 1..15] de Entero
// pagos[i, j] es el número
// de monedas o billetes usados del tipo j para pagar la factura i
Diseñar un algoritmo usando Backtracking que indique para cada pago la forma de realizarlo,
de manera que se minimice la cantidad total que se paga de más.
Solución
Clase EsquemaBtOptimo
proc btOptimo(x: Etapa)
var
xsig: Etapa
cand: Candidatos
alg
si esSolucion(x):
si esMejor():
actualizaSolucion()
fsi
fsi
cand := calculaCandidatos(x)
mientras quedanCandidatos(cand)
xsig = seleccionaCandidato(cand, x)
si esPrometedor(cand, x, xsig):
anotaSolucion(cand, x, xsig)
btOptimo(xsig)
cancelaAnotacion(cand, x, xsig)
fsi
fmientras
fin
…
fClase
Clase PagosBT hereda de EsquemaBTOptimo
cantidadDisponible: Entero
// en céntimos de euro
monedasDisponibles: array [1..15] de Entero
valoresMonedas: array [1..15] de Entero := {50000, 20000, 10000,
…, 5, 2, 1}
N: Entero
// número de facturas a pagar
facturas: array [1..N] de Entero
// cantidades a pagar
sol, solOpt: Solucion
xini: Etapa
Clase Solucion
pagos: array [1..N, 1..15] de Entero
// pagos[i, j]: número de monedas
// billetes del tipo j en el pago de la factura i
cantDisp: Entero
// cantidad disponible aún
monDisp: array [1..15] de Entero
// número de monedas disponibles aún
fClase
Clase Etapa
i: Entero
// próxima factura a considerar
j: Entero
// próximo tipo de moneda o billete a considerar
pagadoFactura: Entero
// cantidad ya pagada en la factura i
fClase
Clase Candidatos
nMonedas: Entero
// número de monedas a usar (desde 0 hasta nMaxMonedas)
nMaxMonedas: Entero // número máximo de monedas a usar
fClase
proc resuelvePagos()
alg
< xini.i, xini.j, xini.pagadoFactura > := < 1, 1, 0 >
sol.pagos := < array de ceros >
sol.cantDisp := cantidadDisponible
sol.monDisp := monedasDisponibles
solOpt.cantDisp := -1
// los otros atributos son irrelevantes al inicio
btOptimo(xini)
si solOpt.cantDisp >= 0:
// hay solución
< imprimir solOpt >
| otras:
< imprimir “No hay solución” >
fsi
fin
func esSolucion(x: Etapa) dev (b: Logico)
alg
b := (x.i > N)
fin
func esMejor() dev (b: Logico)
alg
b:= (sol.cantDisp > solOpt.cantDisp)
fin
proc actualizaSolucion()
alg
solOpt := < copia sol >
fin
func calculaCandidatos(x: Etapa) dev (cand: Candidatos)
alg
si x.j = 15: // el número de monedas necesarias para pagar la factura i
cand.nMaxMonedas := ⎡(facturas[x.i] - x.pagadoFactura) /
valoresMonedas[x.j])⎤
si cand.nMaxMonedas > sol.monDisp[x.j]:
// no se puede pagar
cand.nMonedas := cand.nMaxMonedas
// no hay candidatos
| otras:
cand.nMonedas := cand.nMaxMonedas – 1
// se termina de pagar
// con nMaxMonedas monedas
fsi
| otras:
cand.nMonedas := -1;
cand.nMaxMonedas := min(sol.monDisp[x.j],
⎡(facturas[x.i] – x.pagadoFactura) / valoresMonedas[x.j])⎤)
fsi
fin
func quedanCandidatos(cand: Candidatos) dev (b: Logico)
alg
b := (cand.nMonedas < cand.nMaxMonedas)
fin
func seleccionaCandidato(cand: Candidatos; x: Etapa) dev (xsig: Etapa)
alg
cand.nMonedas := cand.nMonedas + 1
si x.pagadoFactura + cand.nMonedas * valoresMonedas[x.j] >= facturas[x.i]:
< xsig.i, xsig.j, xsig.pagadoFactura > := < x.i + 1, 1, 0 >
| otras:
< xsig.i, xsig.j > := < x.i, x.j + 1 >
xsig.pagadoFactura := x.pagadoFactura + cand.nMonedas
* valoresMonedas[x.j]
fsi
fin
func esPrometedor(cand: Candidatos; x, xsig: Etapa) dev (b: Logico)
alg
b := cierto
// en calculaCandidatos se determina si el pago no se puede hacer
fin
proc anotaSolucion(cand: Candidatos; x, xsig: Etapa)
alg
sol.pagos[x.i, x.j] := cand.nMonedas
sol.cantDisp := sol.cantDisp – cand.nMonedas * valoresMonedas[x.j]
sol.monDisp[x.j] := sol.monDisp[x.j] – cand.nMonedas
fin
proc cancelaAnotacion(cand: Candidatos, xsig: Etapa)
alg
sol.pagos[x.i, x.j] := 0
sol.cantDisp := sol.cantDisp + cand.nMonedas * valoresMonedas[x.j]
sol.monDisp[x.j] := sol.monDisp[x.j] + cand.nMonedas
fin
fClase
PROBLEMA 11 (RAMIFICACIÓN Y ACOTACIÓN)
Se necesita realizar N tareas independientes en una máquina multiprocesador, con M
procesadores pudiendo trabajar en paralelo (supóngase N > M). Siendo ti el tiempo de
ejecución de la i-ésima tarea en cualquier procesador, el problema consiste en determinar en
qué procesador hay que ejecutar cada uno de los trabajos, de forma que el tiempo final de la
ejecución de todos los trabajos (tiempo de ejecución del procesador más cargado) sea mínimo.
Supóngase que no hay restricciones acerca de cuándo puede comenzar la ejecución de cada
trabajo.
Implementar un algoritmo de ramificación y acotación que resuelva el problema teniendo en
cuenta:
(a) Para la solución inicial suponer que se usa el algoritmo voraz correspondiente al ejercicio
4 del boletín de algoritmos voraces (no resolver).
(b) En la expansión de nodos deben evitarse soluciones equivalentes, como las que surgirían
al considerarse varios procesadores con el mismo tiempo final de ejecución de las tareas
anteriores. Así pues, en el caso señalado sólo se considerará uno de los procesadores.
(c) Considerar como función de cota el tiempo acumulado del procesador más cargado.
(d) Explicar las modificaciones que habría que realizar si se usa como función de cota una
que considere, aparte del tiempo total acumulado, que el tiempo total de ejecución de las
tareas que quedan por asignar se distribuye uniformemente entre todos los procesadores,
rellenando los huecos existentes (ver figura). De esta manera, cualquier solución que se
obtenga a partir del nodo en cuestión no podría nunca mejorar la que idealmente se está
obteniendo mediante esta estimación.
cota inferior
cota inferior
Las barras blancas se corresponden con
tareas ya asignadas, y la zona sombreada
se corresponde con el tiempo total de las
tareas que faltan por asignar
Solución
Clase EsquemaRyA
fClase
proc RyA()
var
ed: EstructuraDatos
n: Nodo
hijos: array[] de Nodo
numHijos, i: Entero
alg
ed := crearEstructuraDatos()
calcularSolucionInicial()
n := calcularNodoInicial()
mientras NO fin(n, ed)
hijos := expande(n)
numHijos := tamaño(hijos)
desde i := 1 hasta numHijos
si esMejorCota(hijos[i]):
si esSolucion(hijos[i])
actualizaSolucion(hijos[i])
eliminaNodos(ed)
|otras
ed.añadir(hijos[i])
fsi
fsi
fdesde
si NO ed.esVacia():
n := ed.obtener()
|otras
n := NULO
fsi
fmientras
fin
...
Clase TareasRyA hereda de EsquemaRyA
t: array [1..N] de Numero
// duración de cada tarea
solOpt: Solucion
Clase Solucion
asigTarea: array [1..N] de Entero
// procesador asignado a cada tarea
tFinal: Numero
// tiempo en que han acabado todas las tareas
fClase
Clase Nodo
sol: Solucion
// si es parcial, se refiere a las tareas asignadas
nivel: Entero
// número de tareas asignadas
cotaInf: Numero
// estimación de la mejor solución a partir del nodo
fFinProc: array [1..M] de Numero
// tiempo acumulado en cada procesador
totalHuecos: Numero
// se usa para el apartado (d)
fClase
proc asignacion(tiemposTareas: array [1..N] de Numero)
alg
t := < copiar tiemposTareas >
RyA()
< imprimir solOpt >
fin
func crearEstructuraDatos() dev (ed: EstructuraDatos)
alg
ed := < crear Monticulo >
// de mínimos, según cotaInf
fin
proc calcularSolucionInicial()
var
aux: Entero
alg
solOpt = < llamada al algoritmo voraz >
fin
func calcularNodoInicial() dev (n: Nodo)
alg
n := < crear Nodo >
n.sol.asigTarea := < crear array de ceros >
n.sol.tFinal := 0
n.nivel := 0
n.finProc := < array de ceros >
n.totalHuecos := 0
n.cotaInf := calcularCota(n)
fin
func fin(n: Nodo, ed: EstructuraDatos) dev (b: Logico)
alg
b := ((n = NULO) O (n.cotaInf >= solOpt.tFinal))
fin
func expande(n: Nodo) dev (hijos: array[] de Nodo)
var
i, j: Entero
procesadoresAAsignar: array [1..M] de Logico
// los que tienen un tiempo
// acumulado diferente a los anteriores
alg
< hijos, procesadoresAAsignar > := numeroProcContFinProcDiferentes(n)
j := 0
desde i := 1 hasta M
si procesadoresAAsignar[i]:
j := j + 1
hijos[j] := < crear Nodo >
hijos[j].nivel := n.nivel + 1
hijos[j].tFinProc := < copiar n.tFinProc >
hijos[j].tFinProc[i] := hijos[j].tFinProc[i] + t[hijos[j].nivel]
hijos[j].sol := < copiar n.sol >
hijos[j].sol.asigTarea[hijos[j].nivel] := i
si hijos[j].tFinProc[i] <= n.sol.tFinal:
hijos[j].sol.tFinal := n.sol.tFinal
// esto es redundante
hijos[j].totalHuecos := n.totalHuecos - t[hijos[j].nivel]
| otras:
hijos[j].sol.tFinal := hijos[j].tFinProc[i]
hijos[j].totalHuecos := n.totalHuecos - t[hijos[j].nivel] +
M*(hijos[j].sol.tFinal - n.sol.tFinal)
fsi
hijos[j].cotaInf := calcularCota(n)
fsi
fdesde
fin
func numeroProcContFinProcDiferentes(n: Nodo) dev (hijos: array [] de Nodo,
procesadoresAAsignar: array [1..M] de Logico)
var
i, j, numProc: Entero
enc: Logico
alg
numProc := 0
desde i := 1 hasta M
< j, b > := < 1, falso >
mientras j < i Y NO enc
si n.tFinProc[j] = n.tFinProc[i]:
enc := cierto
fsi
j := j + 1
fmientras
si enc:
procesadoresAAsignar[i] := falso
| otras:
procesadoresAAsignar[i] := cierto
numProc := numProc + 1
fsi
fdesde
hijos := < crear array [1..numProc] de Nodo >
fin
func calcularCota(n: Nodo) dev (cota: Numero)
var
tRestantes: Numero
alg
tRestantes := 0
desde i := n.nivel + 1 hasta N
tRestantes := tRestantes + t[i]
fdesde
si tRestantes < n.totalHuecos:
cota := n.sol.tFinal
| otras:
cota := n.sol.tFinal + (tRestantes – n.totalHuecos) / M
fsi
fin
func esMejorCota(n: Nodo) dev (b: Logico)
alg
b := (n.cotaInf < solOpt.tFinal)
fin
func esSolucion(n: Nodo) dev (b: Logico)
alg
b := (n.nivel = N)
fin
proc actualizaSolucion(n: Nodo)
alg
solOpt := < copiar n.sol >
fin
proc eliminaNodos(ed: EstructuraDatos)
alg
fin
fClase
PROBLEMA 12 (RAMIFICACIÓN Y ACOTACIÓN)
Se tiene un laberinto bidimensional, representado por una matriz lab[1..M, 1..N] de enteros,
de forma que en cada casilla puede haber un obstáculo (valor –1), un objeto de valor v > 0, o
no haber nada (valor 0). La entrada al laberinto se produce por la casilla (1, 1) (esquina
superior izquierda), y la salida por la casilla (M, N).
Para atravesar el laberinto, los únicos movimientos
0 -1 -1 0
0
0
0
1
posibles son realizar un paso hacia la derecha o
0
1
0
5
5 -1 0
0
hacia abajo en la matriz, sin pasar dos veces por la
misma casilla y sin pasar por los obstáculos.
0
3 -1 0
0
0
0
0
Codificar un algoritmo basado en Ramificación y
acotación, para obtener una de las posibles
soluciones óptimas (que maximicen el valor total
obtenido), teniendo en cuenta que el valor máximo
de un objeto es VMAX.
-1
0
0
0
-1
0
-1
-1
0
2
-1
1
2
1
0
-1
4
0
5
-1
-1
0
0
0
Para la solución inicial suponer que se usa algoritmo voraz correspondiente al ejercicio 4 del
boletín de algoritmos voraces (no resolver), de manera que devuelve la secuencia de pasos y
el valor obtenido (-1 si no encuentra solución)
Solución
Clase EsquemaRyA
fClas
proc RyA()
var
ed: EstructuraDatos
n: Nodo
hijos: array[] de Nodo
numHijos, i: Entero
alg
ed := crearEstructuraDatos()
calcularSolucionInicial()
n := calcularNodoInicial()
mientras NO fin(n, ed)
hijos := expande(n)
numHijos := tamaño(hijos)
desde i := 1 hasta numHijos
si esMejorCota(hijos[i]):
si esSolucion(hijos[i])
actualizaSolucion(hijos[i])
eliminaNodos(ed)
|otras
ed.añadir(hijos[i])
fsi
fsi
fdesde
si NO ed.esVacia():
n := ed.obtener()
|otras
n := NULO
fsi
fmientras
fin
...
Clase LaberintoRyA hereda de EsquemaRyA
lab: array [1..M, 1..N] de Entero
solOpt: Solucion
Clase Solucion
movs: array [1..M+N-2] de (derecha, abajo)
valor: Numero
// valor obtenido
fClase
// sucesión de movimientos
Clase Nodo
x, y: Entero
// posición actual (fila, columna)
nivel: Entero
// número de pasos realizados
sol: Solucion
// si es parcial, se refiere a los pasos realizados
cotaSup: Numero
// estimación de la mejor solución a partir del nodo
fClase
proc recorreLaberinto(laberinto: array [1..M, 1..N] de Entero)
alg
lab := < copiar laberinto >
RyA()
si solOpt.valor = -1:
< no hay solución >
| otras:
< imprimir solOpt >
fsi
fin
func crearEstructuraDatos() dev (ed: EstructuraDatos)
alg
ed := < crear Monticulo >
// de máximos, según cotaSup
fin
proc calcularSolucionInicial()
var
aux: Entero
alg
solOpt = < llamada al algoritmo voraz (ej. 11, boletín voraces) >
// si el algoritmo voraz no ha encontrado solución, solOpt.valor = -1
fin
func calcularNodoInicial() dev (n: Nodo)
alg
n := < crear Nodo >
< n.x, n.y > := < 0, 0 >
n.nivel := 0
n.sol.movs := < crear array de (derecha, abajo) >
n.sol.valor := 0
n.cotaSup := calcularCota(n)
fin
// da igual el valor
func fin(n: Nodo, ed: EstructuraDatos) dev (b: Logico)
alg
b := ((n = NULO) O (n.cotaSup <= solOpt.valor))
fin
func expande(n: Nodo) dev (hijos: array[] de Nodo)
var
numHijos, i, j: Entero
movsPosibles: array [1..2] de Logico
// 1: abajo, 2: derecha
alg
numHijos := 0
si n.i < M Y lab[n.x + 1, n.y] ≠ -1
// posible mov. abajo
< numHijos, movsPosibles[1] > := < numHijos + 1, cierto >
| otras:
movsPosibles[1] := falso
fsi
si n.j < N Y lab[n.x, n.y + 1] ≠ -1
// posible mov. derecha
< numHijos, movsPosibles[2] > := < numHijos + 1, cierto >
| otras:
movsPosibles[2] := falso
fsi
hijos := < crear array [1..numHijos] de Nodo >
// puede ser vacío
si movsPosibles[1]:
j := 1
// para recorrer movsPosibles
| otras:
j := 2
fsi
desde i := 1 hasta numHijos
// recorre hijos
hijos[i] := < crear Nodo >
hijos[i].nivel := n.nivel + 1
hijos[i].sol := < copiar n.sol >
si j = 1:
// abajo
< hijos[i].x, hijos[i].y > := < n.x + 1, n.y >
hijos.sol.movs[hijos[i].nivel] := abajo
| otras:
// derecha
< hijos[i].x, hijos[i].y > := < n.x, n.y + 1 >
hijos.sol.movs[hijos[i].nivel] := derecha
fsi
hijos.sol.valor := n.sol.valor + lab[hijos[i].x, hijos[i].y]]
hijos[i].cotaSup := calcularCota(n)
j := j + 1
fmientras
fin
func calcularCota(n: Nodo) dev (cota: Numero)
alg
cota := n.sol.valor + VMAX * (M – n.x + N – n.y)
// lo mejor que podemos
// obtener, si en las restantes casillas encontramos VMAX
fin
func esMejorCota(n: Nodo) dev (b: Logico)
alg
b := (n.cotaSup > solOpt.valor)
fin
func esSolucion(n: Nodo) dev (b: Logico)
alg
b := (n.x = M Y n.y = N)
fin
proc actualizaSolucion(n: Nodo)
alg
solOpt := < copiar n.sol >
fin
proc eliminaNodos(ed: EstructuraDatos)
alg
fin
fClase
Descargar