Problemas, Modelos, Grafos Y Algoritmos: Miguel Toro Bonilla
Problemas, Modelos, Grafos Y Algoritmos: Miguel Toro Bonilla
Problemas, Modelos, Grafos Y Algoritmos: Miguel Toro Bonilla
GRAFOS Y ALGORITMOS
Miguel Toro Bonilla
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Director de la Colección
Miguel Toro Bonilla. Universidad de Sevilla
Consejo de Redacción
Miguel Toro Bonilla. Universidad de Sevilla
Mariano González Romano. Universidad de Sevilla
Andrés Jiménez Ramírez. Universidad de Sevilla
Comité Científico
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Sevilla 2023
Colección: M
anuales de Informática del
Instituto de Ingeniería Informática
Núm.: 4
Comité editorial:
Araceli López Serena
(Directora de la Editorial Universidad de Sevilla)
Elena Leal Abad
(Subdirectora)
Concepción Barrero Rodríguez
Rafael Fernández Chacón
María Gracia García Martín
María del Pópulo Pablo-Romero Gil-Delgado
Manuel Padilla Cruz
Marta Palenque
María Eugenia Petit-Breuilh Sepúlveda
Marina Ramos Serrano
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
OBJETIVO Y AGRADECIMIENTOS......................................................................................... 11
ÍNDICE ................................................................................................................. 7
OBJETIVO Y YAGRADECIMIENTOS
PROBLEMAS ........................................................................ 13
MODELOS......................................................................................................... 11
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
77
Índice
Problema de las estaciones de bomberos......................................................... 72
Problema
Problemadedelalos mochila ................................................................................... 73
anuncios........................................................................................ 69
Problema de la asignación .............................................................................. 69
ALGORITMOS
Problema DEdeSIMULATED
las estaciones ANNEALING.
de bomberos ...................................................................
....................................................... 75 72
Problema de los anuncios ................................................................................ 73
PROBLEMAS Y GRAFOS............................................................................................................. 78
ALGORITMOS DE SIMULATED ANNEALING ......................................................... 79
Grafos virtuales............................................................................................................... 75
Introducción: espacio de estados y grafos virtuales..................................... 79
PROBLEMAS Y GRAFOS
De modelos a grafos ......................................................................................
extendidos............................................................................ 88 78
Problema
GRAFOS VIRTUALESde...................................................................................................
la mochila............................................................................................. 91 79
Caminos y grafos extendidos.................................................................................. 96
Introducción: espacio de estados y grafos virtuales ........................................ 79
Tareas y procesadores............................................................................................... 103
De modelos aygrafos
Heurísticas Funciones extendidos .....................................................................107
de cota............................................................................ 88
Problema
Pesos de de la mochila
caminos, ...................................................................................
soluciones voraces y heurísticas.................................... 110 91
Caminos
Acciones,y grafos
simetrías extendidos
y equivalencia...........................................................................
de vértices............................................... 112 96
Heurísticas.
Tareas .....................................................................................................................
y procesadores ................................................................................... 114 103
Diseño de un grafo virtual. . ......................................................................................
Heurísticas y Funciones de cota..................................................................... 107 114
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Problemas e Hypergrafos............................................................................................
Pesos de caminos, soluciones voraces y heurísticas ...................................... 116 110
Problema De Floyd...................................................................................................... 118
Acciones, simetrías y equivalencia de vértices .............................................. 112
Heurísticas
ALGORITMOS .....................................................................................................
VORACES. 114
........................................................................................................... 123
Diseño de un grafo virtual ............................................................................. 123
Introducción....................................................................................................................... 114
PROBLEMAS E HYPERGRAFOS .................................................................................... 116
Orden de las aristas en un grafo virtual y solución voraz.................... 131
Problema De Floyd ........................................................................................ 118
ALGORITMO A*............................................................................................................................. 133
ALGORITMOS VORACES ................................................................................... 123
Introducción e implementación............................................................................... 133
INTRODUCCIÓNdel
Corrección ......................................................................................................
algoritmo y filtrado de vértices......................................... 141 123
ORDEN
Uso deDE LASalgoritmos
los GRAFO VIRTUAL Y SOLUCIÓN VORAZ ................................... 144
ARISTAS EN UN A*.............................................................................................. 131
Implementación
ALGORITMO directa del algoritmo A*......................................................... 147
A* ............................................................................................... 133
INTRODUCCIÓN
ESQUEMAS E IMPLEMENTACIÓN
RECURSIVOS: ........................................................................... 151
BACKTRACKING..................................................................... 133
CORRECCIÓN DEL ALGORITMO
Introducción Y FILTRADO DE VÉRTICES .................................................. 151
e implementación............................................................................... 141
USO DE
Uso delLOS ALGORITMOSde
algoritmo A*backtracking.
.................................................................................... 144
...................................................................... 156
IMPLEMENTACIÓN DIRECTA
Implementación DEL ALGORITMO
directa del algoritmo A* ...........................................................
de backtracking......................... 158 147
Backtracking
ESQUEMAS aleatorio.
RECURSIVOS: ...............................................................................................
BACKTRACKING ....................................................... 161 151
INTRODUCCIÓN EDINÁMICA..................................................................................................
PROGRAMACIÓN IMPLEMENTACIÓN ........................................................................... 167 151
USO DEL ALGORITMO
Programación dinámica de reducción.
DE BACKTRACKING ..................................................................... 156
.................................................................. 167
IMPLEMENTACIÓN DIRECTA DEL ALGORITMO DE BACKTRACKING ....................................... 168
Implementación........................................................................................................... 158
Uso de la ALEATORIO
BACKTRACKING Programación Dinámica de Reducción........................................... 175
....................................................................................... 161
Implementación directa de la Programación Dinámica de Reducción. 183
PROGRAMACIÓN
ProgramaciónDINÁMICA ........................................................................... 188
dinámica................................................................................................. 167
Implementación de la Programación Dinámica............................................. 188
88
Índice
99
Índice
Ruta de tren......................................................................................................................... 281
ACADEMIA ............................................................................................................
Elementos y contenedores......................................................................................... 283 267
BUFETE .................................................................................................................
Productos y componentes............................................................................................ 286 270
PRODUCTOS Y de
Algoritmo PRECIOS
Floyd. ............................................................................................ 272
......................................................................................................... 289
PARTICIÓN DE CONJUNTOS
Multiplicación de matrices.......................................................................................
encadenadas........................................................... 291 275
CAMINO CERRADO .................................................................................................. 278
NOTACIÓN
RUTA DE YTREN
CATÁLOGO DE RESTRICCIONES DE USO GENERAL.......................... 295
........................................................................................................ 281
Tipos de datos. . ....................................................................................................................
ELEMENTOS Y CONTENEDORES .................................................................................. 283 295
Operadores. ..........................................................................................................................
PRODUCTOS Y COMPONENTES ................................................................................... 298 286
Restricciones......................................................................................................................
ALGORITMO DE FLOYD ............................................................................................. 301 289
Distancias
MULTIPLICACIÓN a restricciones.
DE MATRICES ENCADENADAS ..........................................................................................
............................................................. 303 291
10
10
Objetivo y agradecimientos
E
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
11
Objetivo y agradecimientos
Miguel Toro
Sevilla, septiembre de 2021
12
Problemas y modelos
E
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
13
Problemas y modelos
14
Problemas y modelos
𝑛𝑛𝑛𝑛−1
15
Problemas y modelos
𝑛𝑛𝑛𝑛−1,𝑛𝑛𝑛𝑛−1
Las restricciones primera y segunda indican que cada agente solo puede
tener asignada una tarea y cada tarea un solo agente.
Este es un modelo lineal.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
Lo que indicamos con 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 es que los elementos de la lista 𝑥𝑥𝑥𝑥 =
[𝑥𝑥𝑥𝑥0 , 𝑥𝑥𝑥𝑥1 , . . . , 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1 ] deben ser todos diferentes. En la notación que estamos
usando una secuencia de variables 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛) la consideraremos,
alternativamente, como la lista 𝑥𝑥𝑥𝑥 cuando sea conveniente.
Una tercera posibilidad es:
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )
𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑛𝑛𝑛𝑛−1
Lo que indicamos con 𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖) es que la lista 𝑥𝑥𝑥𝑥 = [𝑥𝑥𝑥𝑥0 , 𝑥𝑥𝑥𝑥1 , . . . , 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1 ] es
una permutación de [0,1, . . . , 𝑛𝑛𝑛𝑛 − 1].
16
Problemas y modelos
𝑛𝑛𝑛𝑛−1
Aquí han aparecido dos nuevos tipos de restricciones 𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖) y
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 que no son lineales.
Un catálago más completo de restricciones puede encontrarse en la
sección Notación y catálogo de restricciones de uso general.
17
Problemas y modelos
Un tipo de modelos que puede ser adecuado en éste y otros muchos casos
es representar el problema mediante un grafo implícito o virtual. Es éste
un tipo de grafo donde los vértices y aristas se definen mediante conjuntos
definidos por comprensión, es decir mediante un tipo y un predicado que
define los valores válidos. Un grafo virtual g de tipo Graph<V,E> lo
podemos definir mediante dos conjuntos: el conjunto de sus vértices 𝑠𝑠𝑠𝑠𝑣𝑣𝑣𝑣 y
el conjunto 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 de sus aristas. Así 𝑔𝑔𝑔𝑔 = (𝑠𝑠𝑠𝑠𝑣𝑣𝑣𝑣, 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠). Cada uno de estos conjuntos
tendrán asociado un tipo de datos y un predicado:
𝑠𝑠𝑠𝑠𝑣𝑣𝑣𝑣 = {𝑣𝑣𝑣𝑣: 𝑉𝑉𝑉𝑉 | 𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 (𝑣𝑣𝑣𝑣)}
𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 = {𝑣𝑣𝑣𝑣1: 𝑠𝑠𝑠𝑠𝑣𝑣𝑣𝑣, 𝑣𝑣𝑣𝑣2: 𝑠𝑠𝑠𝑠𝑣𝑣𝑣𝑣 | 𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖(𝑣𝑣𝑣𝑣1, 𝑣𝑣𝑣𝑣2)}
18
Problemas y modelos
Veamos el tipo de los vértices para un grafo virtual que modele el poblema
del puzle.
Hay cuatro posibles acciones o movimientos: Arriba (Up), Izquierda (Left),
Abajo (Down), Derecha (Right). Estas acciones serán posibles en algunas
configuraciones y en otras no. Abajo se incluyen algunas configuraciones
tras el movimiento correspondiente.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
19
Problemas y modelos
Y las propiedades de Vp
• Integer n: Número de filas
• 𝐼𝐼𝐼𝐼𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 𝑏𝑏𝑏𝑏𝑉𝑉𝑉𝑉(): Una tupla (f,c) con la posición de la casilla negra
• 𝐼𝐼𝐼𝐼𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 𝑔𝑔𝑔𝑔𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉), 𝑉𝑉𝑉𝑉 ∈ 𝑃𝑃𝑃𝑃: Valor entero contenido en la casilla 𝑉𝑉𝑉𝑉
definida por la fila 𝑓𝑓𝑓𝑓 y la columna 𝑐𝑐𝑐𝑐.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Como en Java, this representa el objeto actual que estamos diseñando. Por
otra parte la distancia de Manhattan se puede definir como :
𝑚𝑚𝑚𝑚ℎ((𝑓𝑓𝑓𝑓1, 𝑐𝑐𝑐𝑐1), (𝑓𝑓𝑓𝑓2, 𝑐𝑐𝑐𝑐2)) = |𝑓𝑓𝑓𝑓1 − 𝑓𝑓𝑓𝑓2| + |𝑐𝑐𝑐𝑐1 − 𝑐𝑐𝑐𝑐2|
20
Problemas y modelos
Transformación de modelos
Junto con las restricciones anteriores es conveniente recordar algunas
propiedades que nos permitirán transformar unos modelos en otros.
Estas transformaciones serán importantes para las técnicas que veremos
más adelante.
Transformación de problemas de maximización a problemas de
minimización:
min 𝑓𝑓𝑓𝑓(𝑥𝑥𝑥𝑥 ) ≡ max −𝑓𝑓𝑓𝑓 (𝑥𝑥𝑥𝑥 )
𝑥𝑥𝑥𝑥∈Ω 𝑥𝑥𝑥𝑥∈Ω
21
Problemas y modelos
𝑛𝑛𝑛𝑛−1
max 𝑓𝑓𝑓𝑓0 (𝑥𝑥𝑥𝑥 ), … , max 𝑓𝑓𝑓𝑓𝑛𝑛𝑛𝑛−1 (𝑥𝑥𝑥𝑥 ) ≡ max � 𝜔𝜔𝜔𝜔𝑖𝑖𝑖𝑖 𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖 (𝑥𝑥𝑥𝑥 )
𝑥𝑥𝑥𝑥∈Ω 𝑥𝑥𝑥𝑥∈Ω 𝑥𝑥𝑥𝑥∈Ω
𝑖𝑖𝑖𝑖=0
22
Programación Lineal Entera
L
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
23
Programación Lineal Entera
Para resolver estos problemas se usa una mezcla de relajación lineal más
algoritmos de vuelta atrás que veremos más adelante. El problema
relajado linealmente puede resolverse muy eficientemente mediante el
algoritmo del Simplex, pero con él sólo obtendremos una aproximación a
la solución del problema de Programación Lineal Entera. Para resolver
este primero se encuentra, mediante el algoritmo del Simplex, la solución
de la relajación lineal que es aproximada y posteriormente, mediante
vuelta atrás, el valor óptimo entero o binario que respete las restricciones
adicionales. Los algoritmos que resuelven los problemas de Programación
Lineal Entera tienen complejidades superiores a las polinomiales en el
caso peor.
En esta sección estamos interesados en modelos lineales de problemas y
asumiendo que disponemos de los algoritmos necesarios para encontrar
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
24
Programación Lineal Entera
25
Programación Lineal Entera
𝑛𝑛𝑛𝑛−1,𝑛𝑛𝑛𝑛−1
Ahora las variables son binarias, toman valores cero y uno y tenemos un
Problema de Programación Lineal Entera. El primer conjunto de
restricciones indica que cada agente 𝑖𝑖𝑖𝑖 tiene que realizar una tarea y sólo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
una. El segundo conjunto de restricciones indica que cada la tarea 𝑗𝑗𝑗𝑗 tiene
que ser realizada por un agente y sólo uno. Hay un total de 2𝑛𝑛𝑛𝑛
restricciones.
El fichero asignación LSI concreta el modelo anterior en formato LSI para
ser resuelto:
head section
Integer getN()
Integer getM()
Double costes(Integer i, Integer j)
Integer n = getN()
Integer m = getM()
goal section
constraints section
sum(x[i,j], i in 0 .. n) = 1, j in 0 .. n
sum(x[i,j], j in 0 .. n) = 1, i in 0 .. n
bin
x[i,j], i in 0 .. n, j in 0 .. n
26
Programación Lineal Entera
Estas son las restricciones básicas. Además de estas hay disponibles otras
restricciones más complejas que comentaremos más adelante.
Debemos tener en cuenta que el lenguaje LSI, como el formato LP, no
distingue entre desigualdades estrictas y no estrictas, por lo que, por
ejemplo, < y <= son equivalentes y por lo tanto no se recomienda usar el
operador <.
Finalmente están las secciones que declaran cotas para las variables,
bounds section, la declaración de variables binarias, bin, enteras, int, libres
free y semicontinuas semi-continuous. Implícitamente todas las variables
del modelo son mayores o iguales a cero. Una variable libre, free, es aquella
que no tiene esta restrcción y por lo tanto puede tomar valores negativos.
Una variable semicontinua, semi-continuous, es aquella cuyos valores
deben estar en un intervalo o ser cero. Veremos más adelante otros
detalles sobre las variables libres y semicontinuas.
27
Programación Lineal Entera
� 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0,𝑖𝑖𝑖𝑖=0
sum(costes(i,j) x[i,j], i in 0 .. n, j in 0 .. m)
𝑛𝑛𝑛𝑛
� 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0,𝑖𝑖𝑖𝑖=0|𝑖𝑖𝑖𝑖>𝑖𝑖𝑖𝑖
AuxGrammar.generate(TareasPLI.class,"models/tareas.lsi",
"ficheros/tareas.lp");
Locale.setDefault(new Locale("en", "US"));
GurobiSolution solution =
GurobiLp.gurobi("ficheros/tareas.lp");
System.out.println(solution.toString((s, d) -> d > 0));
Podemos entonces filtrar las soluciones con valor mayor que cero o de
alguna otra forma. La variables x[1,2], etc. en el lenguaje LSI se traducen
28
Programación Lineal Entera
And
La restricción x1 = 1 and x2 = 1, es equivalente a:
x1 + x2 = 2
𝑥𝑥𝑥𝑥1 = 1 𝑎𝑎𝑎𝑎𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 𝑥𝑥𝑥𝑥2 = 1 ≡ �
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥1, 𝑥𝑥𝑥𝑥2
Or
La restricción x1 = 1 or x2 = 1, es equivalente a:
x1 + x2 ≥ 1
𝑥𝑥𝑥𝑥1 = 1 𝑜𝑜𝑜𝑜𝑟𝑟𝑟𝑟 𝑥𝑥𝑥𝑥2 = 1 ≡ �
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥1, 𝑥𝑥𝑥𝑥2
29
Programación Lineal Entera
Como máximo n
𝑟𝑟𝑟𝑟
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
� 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑛𝑛𝑛𝑛
𝑖𝑖𝑖𝑖=0
Doble implicación
La restricción 𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥, 𝑦𝑦𝑦𝑦, 𝑥𝑥𝑥𝑥 − 𝑦𝑦𝑦𝑦 = 0, o lo que es lo mismo 𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥, 𝑦𝑦𝑦𝑦, 𝑥𝑥𝑥𝑥 = 𝑦𝑦𝑦𝑦,
hace que si 𝑥𝑥𝑥𝑥 = 1 entonces 𝑦𝑦𝑦𝑦 = 1 o si 𝑥𝑥𝑥𝑥 = 0 entonces 𝑦𝑦𝑦𝑦 = 0. Es decir,
ambas son verdaderas o falsas a la vez y por lo tanto hay una relación de
doble implicación entre ambas. Podemos comprobar que los pares de
valores posibles son: (0,0), (1,1) pero no (1,0), (0,1).
30
Programación Lineal Entera
Resultante And
La restricción y = (x1 = 1 and x2 = 1) es equivalente a:
𝑦𝑦𝑦𝑦 ≤ 𝑥𝑥𝑥𝑥1
𝑦𝑦𝑦𝑦 ≤ 𝑥𝑥𝑥𝑥2
𝑦𝑦𝑦𝑦 = (𝑥𝑥𝑥𝑥1 = 1 𝑎𝑎𝑎𝑎𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥2 = 1) ≡ �
𝑦𝑦𝑦𝑦 ≥ 𝑥𝑥𝑥𝑥1 + 𝑥𝑥𝑥𝑥2 − 1
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦, 𝑥𝑥𝑥𝑥1, 𝑥𝑥𝑥𝑥2
En el lenguaje LSI se puede expresar como:
Resultante Or
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
31
Programación Lineal Entera
Resultante Max
La restricción 𝑦𝑦𝑦𝑦 = 𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 es equivalente a:
𝑖𝑖𝑖𝑖=0,𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦
⎧ 𝑦𝑦𝑦𝑦 ≥ 𝑥𝑥𝑥𝑥0
⎪
𝑦𝑦𝑦𝑦 = 𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≡ ...
𝑖𝑖𝑖𝑖=0,𝑛𝑛𝑛𝑛−1 ⎨ 𝑦𝑦𝑦𝑦 ≥ 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1
⎪
⎩ 𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦, 𝑥𝑥𝑥𝑥0 , . . . , 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1
Resultante Min:
La restricción 𝑦𝑦𝑦𝑦 = 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , es equivalente a:
𝑖𝑖𝑖𝑖=0,𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑦𝑦𝑦𝑦
⎧ 𝑦𝑦𝑦𝑦 ≤ 𝑥𝑥𝑥𝑥0
⎪
𝑦𝑦𝑦𝑦 = 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≡ ...
𝑖𝑖𝑖𝑖=0,𝑛𝑛𝑛𝑛−1 ⎨ 𝑦𝑦𝑦𝑦 ≤ 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1
⎪
⎩ 𝑖𝑖𝑖𝑖 𝑛𝑛𝑛𝑛 𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦, 𝑥𝑥𝑥𝑥0 , . . . , 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1
32
Programación Lineal Entera
Valor absoluto
r = ABS (a)
Como vimos la restricción MAX implica que una variable nueva debe
entrar en la función objetivo.
La herramienta Gurobi ya tiene implementada esta restricción
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑥𝑥𝑥𝑥 ∈ {𝑣𝑣𝑣𝑣0 , … , 𝑣𝑣𝑣𝑣𝑛𝑛𝑛𝑛−1 } ≡ 𝑥𝑥𝑥𝑥 = � 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , � 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 = 1, 𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖=0
x[] in 0, 2, 3
Si los valores están indicados por compresión de la forma 𝐿𝐿𝐿𝐿3−1 𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖, el
conjunto de valores es 0, 1, 2, y al restricción se escribe como:
x[] in v in 0 .. 3
33
Programación Lineal Entera
En las listas por comprensión en rango de los valores del índice es cerrado
por la izquierda y abierto por la derecha. Es uso de esta restricción supone
introducir las n variables adicionales 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 .
Permutaciones
La restricción toma dos listas. Las listas pueden estar expresadas por
comprensión o simplemente por enumeración. El tamaño de la lista de la
izquierda debe ser menor o igual la de la derecha. La denominamos de
𝑛𝑛𝑛𝑛−1,𝑚𝑚𝑚𝑚−1
forma compacta 𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0,𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖 ), 𝑔𝑔𝑔𝑔(𝑗𝑗𝑗𝑗)).
permutation(x[i], i in 0 .. 3 ; 0, 2, 3)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑚𝑚𝑚𝑚−1
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = � 𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑖𝑖𝑖𝑖=0
𝑚𝑚𝑚𝑚−1
𝑛𝑛𝑛𝑛−1,𝑚𝑚𝑚𝑚−1 � 𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 1, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0,𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 ) ≡ 𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
� 𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 1, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑖𝑖𝑖𝑖=0
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
34
Programación Lineal Entera
Variables libres
Por defecto en Programación lineal entera las variables son mayores o
iguales a cero. Una variable libre es la que no tiene esta restricción. Esto
se expresa indicando que la variable tiene cotas inferiores bajas y
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
superiores altas
Se declaran en el lenguaje LSI en la sección free.
Una variable libre se puede sustituir por la diferencia de dos variables con
cota inferior cero.
𝑓𝑓𝑓𝑓𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟 𝑥𝑥𝑥𝑥 ≡ 𝑦𝑦𝑦𝑦1 − 𝑦𝑦𝑦𝑦2
35
Programación Lineal Entera
Variables semicontinuas
Son variables cuyo valor puede ser cero o estar en un intervalo que no
contiene el cero. Es decir:
𝑟𝑟𝑟𝑟 = 1 → 𝑥𝑥𝑥𝑥 = 0
𝑥𝑥𝑥𝑥 = 0 ∨ 𝑎𝑎𝑎𝑎 ≤ 𝑥𝑥𝑥𝑥 ≤ 𝑏𝑏𝑏𝑏 ≡ �𝑟𝑟𝑟𝑟 = 0 → 𝑎𝑎𝑎𝑎 ≤ 𝑥𝑥𝑥𝑥, 𝑥𝑥𝑥𝑥 ≤ 𝑏𝑏𝑏𝑏
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑟𝑟𝑟𝑟
Desigualdad
Modelo de la restricción a != b. siendo ambas enteras
La restricción 𝑎𝑎𝑎𝑎 ≠ 𝑏𝑏𝑏𝑏 es equivalente a
𝑟𝑟𝑟𝑟 = |𝑎𝑎𝑎𝑎 − 𝑏𝑏𝑏𝑏|, 𝑟𝑟𝑟𝑟 ≥ 1, 𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑎𝑎𝑎𝑎, 𝑏𝑏𝑏𝑏, 𝑟𝑟𝑟𝑟
En el lenguaje LSI se expresa como
x1 != x2
36
Programación Lineal Entera
Otra alternativa es
𝑥𝑥𝑥𝑥1 ! = 𝑥𝑥𝑥𝑥2 ≡ |𝑥𝑥𝑥𝑥1 − 𝑥𝑥𝑥𝑥2| ≥ 1
AllDiferent
allDifferent(x0,x2,…)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
La restricción toma una lista de variables e impone que sus valores sean
diferentes.
Con las variables indicadoras anteriores podemos implementar la
restricción allDifferent en la forma:
𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 1 → 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≥ 1, 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 > 𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) ≡ �𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 0 → 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≥ 1, 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 > 𝑖𝑖𝑖𝑖
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 > 𝑖𝑖𝑖𝑖
Otra alternativa es
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) ≡ |𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 | ≥ 1, 𝑖𝑖𝑖𝑖 = 0. . 𝑛𝑛𝑛𝑛, 𝑗𝑗𝑗𝑗 = 𝑖𝑖𝑖𝑖 + 1. . . 𝑛𝑛𝑛𝑛
37
Programación Lineal Entera
or(>=,k,c1|c2|…)
c1 => c2
Esto es equivalente a:
𝑦𝑦𝑦𝑦1 = 1 → 𝑐𝑐𝑐𝑐1 (𝑥𝑥𝑥𝑥 )
𝑦𝑦𝑦𝑦2 = 1 → 𝑐𝑐𝑐𝑐2 (𝑥𝑥𝑥𝑥 )
𝑦𝑦𝑦𝑦1 − 𝑦𝑦𝑦𝑦2 < 0
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖: [1,3]
38
Programación Lineal Entera
Restricciones no lineales
Se pueden expresar en el lenguaje LSI como una función lineal a trozos
y[] = PWL (x[]) : (0,1) (2,5) (5,13)
39
Programación Lineal Entera
Algunos ejemplos
Veamos aquí un catálogo de problemas que pueden ser resueltos
mediante modelos de Programación Lineal Entera. Al final veremos otros
para podemos obtener un modelo, pero no lineal y que nos invita a
resolverlos con otras técnicas.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Inversión de capital
El problema se puede enunciar de la siguiente forma: Supongamos que
deseamos invertir una cantidad total 𝑇𝑇𝑇𝑇. Tenemos identificadas 𝑛𝑛𝑛𝑛
oportunidades de inversión. Cada oportunidad de inversión requiere de
una cantidad 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 y se espera un beneficio 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 con 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛). ¿Qué inversiones
debemos realizar con el fin de maximizar beneficio total?
El problema anterior, de una forma abstracta, es similar al Problema de la
Mochila visto anteriormente. Ahora vamos a considerar algunas
restricciones adicionales que podríamos querer añadir. Por ejemplo,
consideremos limitaciones del tipo siguiente:
1. Sólo podemos hacer 2 inversiones como máximo.
2. Si se hace la inversión 𝑟𝑟𝑟𝑟 la 𝑠𝑠𝑠𝑠 también se debe hacer.
3. Si se hace la inversión 𝑢𝑢𝑢𝑢 la 𝑣𝑣𝑣𝑣 no se puede hacer y viceversa.
Asumiendo que las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 toman valor 1 si se realiza la
inversión 𝑖𝑖𝑖𝑖 y cero en caso contrario podemos modelar el problema como:
40
Programación Lineal Entera
𝑛𝑛𝑛𝑛−1
� 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 2
𝑖𝑖𝑖𝑖=0
𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟 ≤ 𝑥𝑥𝑥𝑥𝑠𝑠𝑠𝑠
𝑥𝑥𝑥𝑥𝑢𝑢𝑢𝑢 + 𝑥𝑥𝑥𝑥𝑣𝑣𝑣𝑣 ≤ 1
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
La restricción 𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟 − 𝑥𝑥𝑥𝑥𝑠𝑠𝑠𝑠 ≤ 0, o 𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟 ≤ 𝑥𝑥𝑥𝑥𝑠𝑠𝑠𝑠 , asumiendo que las variables toman
valores binarios, tiene como soluciones posibles para (𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟 , 𝑥𝑥𝑥𝑥𝑠𝑠𝑠𝑠 ) los pares
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
41
Programación Lineal Entera
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
head section
Integer getN()
Boolean esVecino(Integer i, Integer j)
Integer n = getN()
goal section
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
min sum(x[i], i in 0 .. n)
constraints section
bin
x[i], i in 0 .. n
42
Programación Lineal Entera
𝑛𝑛𝑛𝑛−1
son 𝑦𝑦𝑦𝑦 = 𝑥𝑥𝑥𝑥 + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖, 𝑦𝑦𝑦𝑦 = −𝑥𝑥𝑥𝑥 + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖. Los valores mínimos y máximos de dp, ds son
por lo tanto: 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖1 = −𝑛𝑛𝑛𝑛 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 𝑛𝑛𝑛𝑛 − 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖1 = 0, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 2𝑛𝑛𝑛𝑛 − 2.
En este problema no se trata de encontrar un mínimo. Se trata de
encontrar una solución y puede haber muchas. En este caso se podría
obviar la función objetivo pero por el diseño del lenguaje LSI es necesario
poner un valor en la función objetivo que puede ser constante o variable.
El modelo LSI para este problema es:
head section
Integer n = 150
goal section
min x[0,0]
constraints section
sum(x[i,j], i in 0 .. n) = 1, j in 0 .. n
sum(x[i,j], j in 0 .. n) = 1, i in 0 .. n
sum(x[i,j], i in 0 .. n, j in 0 .. n | j-i = k) <= 1, k in -n+1
.. n
sum(x[i,j], i in 0 .. n, j in 0 .. n | j+i = k) <= 1, k in 0
.. 2*n-1
bin
x[i,j], i in 0 .. n, j in 0 .. n
43
Programación Lineal Entera
Las variables 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 representan la ordenada de la casilla donde hay una reina
con abscisa i. Es decir en la columna i hay una reina en la fila 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 . Las filas
ocupadas serán los valores de las 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , las diagonales principales ocupadas
serán los valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑖𝑖𝑖𝑖 y las diagonales secundarias ocupadas serán los
valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Integer n = 20
goal section
min sum(x[i], i in 0 .. n)
constraints section
allDifferent(x[i], i in 0 .. n)
allDifferent(x[i] + i, i in 0 .. n)
allDifferent(x[i] - i, i in 0 .. n)
bounds section
x[i] <= n - 1, i in 0 .. n
int
x[i], i in 0 .. n
44
Programación Lineal Entera
head section
Integer n = 150
goal section
min x[0]
constraints section
permutation(x[i], i, i in 0 .. n)
allDifferent(x[i] + i, i in 0 .. n; k, k in -n+1 .. n)
allDifferent(x[i] - i, i in 0 .. n; k, k in 0 .. 2*n-1)
int
x[i], i in 0 .. n
45
Programación Lineal Entera
Redes de flujo
Una red de flujo es un grafo dirigido donde cada arista tiene una capacidad
(que debe ser no negativa) y por ella pasa un flujo que debe ser menor o
igual a esa capacidad. Un flujo sobre una red de flujo debe satisfacer la
siguiente restricción: la cantidad de flujo que llega a un vértice debe ser
igual al que sale del mismo excepto cuando es un vértice fuente o un
vértice sumidero. Las restricciones del flujo en cada vértice son
denominadas restricciones de Kirchorff. Los vértices que producen flujo
se llaman fuentes y los que consumen sumideros.
además de las propiedades de una red de flujo, cada vértice y arista puede
tener asociado un límite inferior, uno superior y un coste unitario del flujo
que pasa por él. La red de flujo generalizada tiene, además, una función
objetivo que es la suma de los costes del flujo que circula por las aristas,
por los vértices y el consumido o producido en ellos. Esta función objetivo
podemos maximizarla o minimizarla. Podemos añadir la restricción
adicional de que el flujo que circula por vértices y aristas tenga valor
entero. Una red de flujo simple no tiene cotas asociadas a los vértices, cota
inferior cero en las aristas.
Llamaremos vértices fuente a aquellos en los que se crea flujo y no tienen
aristas entrantes y vértices sumidero a aquellos en los que se consume flujo
y no tienen aristas salientes.
A partir de un grafo de flujo se puede construir un Problema de
Programación Lineal (o Programación Lineal Entera).
Las ideas para hacer esa transformación son:
• Partir de una vista del grafo de partida cuyos vértices sean enteros.
• Sean las variables y[i,j] asociadas al flujo que pasa por la arista (i,j)
• Si fuera necesario sea x[i] el flujo que pasa por el vértice i. En los
vértices intermedios y en los vértices sumidero este flujo es igual al
flujo de las aristas entrantes, y al flujo de las aristas salientes en el caso
de los vértices fuentes.
46
Programación Lineal Entera
Integer getN()
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
goal section
constraints section
sum(y[j,i], j in 0 .. n | containsEdge(j,i))
- sum(y[i,j], j in 0 .. n | containsEdge(i,j)) = 0, i in
1 .. n-1
bounds section
47
Programación Lineal Entera
También podemos ver que usamos una vista de un grafo cuyos vértices
son enteros. Esta vista se implementa mediante la clase
IntegerVertexGraphView<V,E>. Esta clase implementa
Graph<Integer,SimpleEdge<Integer>> tomando un Graph<V,E> de punto
de partida y está disponible en el repositorio.
Las vistas sobre grafos se pueden ver con más detalle en el libro de esta
misma colección Análisis y Diseño de Algoritmos y Tipos de Datos.
48
Programación Lineal Entera
#VERTEX#
A,0
B,0
C,0,2.,14.,2.
D,0
S,1,0.,inf,1.
T,2,0.,inf,-1.
#EDGE#
A,C,0.,10.,1.
C,A,0.,4.,1.
A,B,1.,12.,1.
D,B,3.,7.,1.
B,C,2.,9.,1.
C,D,2.,14.,1.
S,A,0.,16.,1.
S,C,0.,13.,1.
B,T,0.,20.,1.
D,T,0.,4.,1.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
49
Programación Lineal Entera
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
50
Programación Lineal Entera
y las aristas que van de 𝑈𝑈𝑈𝑈 a 𝑉𝑉𝑉𝑉, que formar un corte, minimizan la suma de
sus capacidades máximas. Si designamos por 𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑢𝑢𝑢𝑢 la cota supeirior del flujo
en la arista 𝑗𝑗𝑗𝑗, el problema el corte mínimo puede ser formulado como uno
de Programación Lineal Entera en la forma:
51
Programación Lineal Entera
Elección de proyectos
En un problema de elección de proyectos, hay 𝑛𝑛𝑛𝑛 proyectos y 𝑚𝑚𝑚𝑚
herramientas necesarias. Cada proyecto 𝑖𝑖𝑖𝑖 produce ingresos 𝑏𝑏𝑏𝑏𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖)
y cada herramienta 𝑗𝑗𝑗𝑗 cuesta 𝑐𝑐𝑐𝑐𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑗𝑗𝑗𝑗) si se usa. Cada proyecto requiere
varias herramientas y cada herramienta puede ser compartida por varios
proyectos. El problema es determinar qué proyectos y máquinas deben
seleccionarse y comprarse respectivamente, de modo que se maximice el
beneficio. Sea 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) verdadero si el proyecto 𝑖𝑖𝑖𝑖 necesita la
herramienta 𝑗𝑗𝑗𝑗.
head section
…
goal section
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
constraints section
bin
x[i], i in 0 .. n
y[j], j in 0 .. m
52
Programación Lineal Entera
head section
Integer getN()
Boolean containsEdge(Integer i, Integer j)
Integer n = getN()
goal section
constraints section
sum(x[j,i], j in 0 .. n | containsEdge(j,i))
- sum(x[i,j], j in 0 .. n |
containsEdge(i,j)) = 0, i in 1 .. n-1
bin
x[i,j], i in 0 .. n, j in 0 .. n | containsEdge(i,j)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Conectividad de redes
El problema de la conectividad de redes trata de buscar el mínimo número
de aristas tales que eliminadas desconectan un vértice de un destino.
Equivale al problema de corte mínimo con pesos de aristas igual a uno.
53
Programación Lineal Entera
Problema de la asignación
El problema de la asignación trata de asignar n personas a m tareas y como
ya hemos comentado. Se supone que el coste de realizar la tarea j por la
persona i es c(i,j). Se trata de decidir qué persona hace cada tarea para
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
54
Programación Lineal Entera
55
Programación Lineal Entera
Al sumar, si el camino es cerrado, 𝑥𝑥𝑥𝑥0 = 𝑥𝑥𝑥𝑥𝑘𝑘𝑘𝑘 , 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 1 y cada 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 se anula con
la del paso anterior, por lo que obtenemos 𝑘𝑘𝑘𝑘 𝑛𝑛𝑛𝑛 ≤ 𝑘𝑘𝑘𝑘 (𝑛𝑛𝑛𝑛 − 1). Lo cual es una
contradicción si el camino es cerrado .
Por otra parte si 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 0, la arista no está en el camino. Por lo tanto, la
restricción se reduce a:
56
Programación Lineal Entera
Integer getN()
Double edgeCost(Integer i, Integer j)
Boolean containsEdge(Integer i, Integer j)
Integer n = getN()
Integer origin()
Integer target()
Integer v0 = origin()
Integer v1 = target()
goal section
constraints section
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
sum(y[v0,j], j in 0 .. n | containsEdge(v0,j)) = 1
sum(y[i,v1], i in 0 .. n | containsEdge(i,v1)) = 1
sum(y[j,i], j in 0 .. n | containsEdge(j,i))
- sum(y[i,j], j in 0 .. n | containsEdge(i,j)) = 0,
i in 1 .. n | i != origin() && i != v1
x[i] - x[j] + n y[i,j] <= n - 1, i in 0 .. n, j in 0 .. n |
containsEdge(i,j)
x[v0] = 0
bounds section
bin
y[i,j], i in 0 .. n, j in 0 .. n | containsEdge(i,j)
int
x[i], i in 0 .. n
57
Programación Lineal Entera
head section
Integer getN()
Double edgeCost(Integer i, Integer j)
Boolean containsEdge(Integer i, Integer j)
Integer n = getN()
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
goal section
constraints section
sum(y[i,j], i in 0 .. n | containsEdge(i,j)) = 1, j in 0 .. n
sum(y[i,j], j in 0 .. n | containsEdge(i,j)) = 1, i in 0 .. n
x[i] - x[j] + n y[i,j] <= n-1, i in 1 .. n, j in 1 .. n |
containsEdge(i,j)
x[0] = 0
bounds section
bin
y[i,j], i in 0 .. n, j in 0 .. n | containsEdge(i,j)
int
x[i], i in 0 .. n
58
Algoritmos genéticos
L
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎/𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑓𝑓𝑓𝑓(𝑥𝑥𝑥𝑥)
𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 (𝑥𝑥𝑥𝑥 ), 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
𝑥𝑥𝑥𝑥 ∈ Ω
59
Algoritmos genéticos
60
Algoritmos genéticos
61
Algoritmos genéticos
posible.
Para resolver un problema mediante algoritmos genéticos debe tener un
modelo en la forma:
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(𝑣𝑣𝑣𝑣)
𝑣𝑣𝑣𝑣 ∈ 𝑉𝑉𝑉𝑉
62
Algoritmos genéticos
predefinidos junto con sus operadores genéticos, que puedan ser usados
para resolver una amplia mayoría de problemas.
Un catálogo de cromosomas
Hay una amplia gama de problemas combinatorios que pueden ser
abordados partiendo de un catálogo de cromosomas cuyos operadores de
cruce y mutación son conocidos y pueden ser reutilizados. Veamos
algunos tipos de cromosomas adecuados para ser usados en los
Algoritmos Genéticos. Todos ellos se pueden implementar basándose en
dos cromosomas aportados por las librerías de Apache:
BinaryChrosomose y RandomKey<T>. Referencias a las librerías de Apache
se incluyen al final del libro.
Para el diseño e implementación de los cromosomas que usaremos
partimos del tipo genérico Chromosome<E>:
63
Algoritmos genéticos
ValuesInRangeChromosome<E,S>
Representa una lista de valores de tipo E que están en un rango.
Dispondremos de varios subtipos: BinaryChromosome,
RangeChromosome y DoubleChromosome. Los detalles específicos para
cada uno de estos cromosomas se indicarán en una implementación del
tipo ValuesInRangeProblemAG<E,S>. Dónde E es el tipo de los elementos
del cromosoma y S el tipo de la solución del problema.
64
Algoritmos genéticos
Restricciones:
• d = decode();
• n = decode.size();
• min(i)<= d[i] < max(i), i:0..n-1;
Binary
Este cromosoma es un caso particular del anterior donde los valores son
enteros de mínimo 0 y máximo 1. Es un cromosoma básico a partir del cual
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Range
Es un subtipo de ValuesInRangeChromosome<Integer,S> cuyos valores son
listas de enteros en los rangos especificados.
Usos:
Es un cromosoma adecuado para resolver problemas cuya solución es un
Multiset formado con elementos de un conjunto dado u otros problemas
en los que aparecen variables enteras en un rango.
Real
Es un subtipo de ValuesInRangeChromosome<Double,S>. Los valores de
estos cromosomas son listas de números reales en rangos especificados.
65
Algoritmos genéticos
Restricciones y notación:
• n = itemsNumber();
• s = normalSequence().size();
• d = decode();
• r = d.size();
• n<=r<=s;
• m(i) = maxMultiplicity (i);
Cada cromosoma de este tipo tiene asociada la secuencia normal. La
longitud de la secuencia normal es ∑𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖= 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 .
66
Algoritmos genéticos
Permutation
Es un subtipo de SeqChromosome cuyos valores son listas de enteros que
son permutaciones de la secuencia normal. Es un cromosoma adecuado
para resolver problemas cuya solución es una permutación de un
multiconjunto dado de objetos.
PermutationSubList
Es un subtipo de SeqChromosome cuyos valores son listas de enteros que
son permutaciones de subconjuntos de la secuencia normal. Es un
cromosoma adecuado para resolver problemas cuya solución es una
permutación de un subconjunto de un multiconjunto dado de objetos.
SubList
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
BlocksData<S>
Es cromosoma cuyos valores son listas de enteros divididas en bloques.
Dentro de cada bloque aparecerán permutaciones de los valores
especificados previamente para ese bloque. Cada bloque viene
especificado por los límites de ese bloque y los valores que pueden
aparecer en ese bloque.
67
Algoritmos genéticos
ExpressionData
Es un cromosoma cuyos valores son expresiones construidas a partir de
un conjunto dado de operadores. Para concretar los detalles del
cromosoma debemos establecer el número de variables y de constantes
máximo, el rango y tipo de las constantes y el operador de combinación de
las expresiones parciales.
Para flexibilizar el tipo del cromosoma podemos especificar la longitud de
la cabeza del cromosoma y el número de genes. Ejemplos de uso pueden
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
68
Algoritmos genéticos
Ejemplos
Problema de la mochila
Se parte de 𝐿𝐿𝐿𝐿, una lista de objetos de tamaño 𝑛𝑛𝑛𝑛, donde cada objeto 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 es
de la forma 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 = (𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 , 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 ) que representan, respectivamente, su peso
unitario, su valor unitario y el número máximo de unidades posibles. La
mochila tiene una capacidad 𝐶𝐶𝐶𝐶. El problema busca ubicar en la mochila el
máximo valor posible de los objetos colocados en la mochila siempre que
no superen la capacidad.
𝑛𝑛𝑛𝑛−1
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖 = � 𝑖𝑖𝑖𝑖 [𝑖𝑖𝑖𝑖 ]𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 − 𝐾𝐾𝐾𝐾 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(� 𝑖𝑖𝑖𝑖 [𝑖𝑖𝑖𝑖 ] 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 − 𝐶𝐶𝐶𝐶)
𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖=0
Problema de la asignación
En este problema tenemos una lista de agentes 𝐿𝐿𝐿𝐿 y una lista de tareas 𝑇𝑇𝑇𝑇
ambas del mismo tamaño 𝑛𝑛𝑛𝑛. El coste de que el agente 𝑖𝑖𝑖𝑖 realice la tarea 𝑗𝑗𝑗𝑗
sea 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 . Se pretende asignar a cada agente una tarea y sólo una de tal forma
que se ejecuten todas las tareas con el coste mínimo.
Una primera versión del problema es hacer una implementación de la
solución propuesta más arriba mediante la técnica de la Programación
Lineal Entera. En la solución asumimos las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 toman
69
Algoritmos genéticos
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )
𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
Lo que indicamos es que la lista 𝑥𝑥𝑥𝑥 = [𝑥𝑥𝑥𝑥0 , 𝑥𝑥𝑥𝑥1 , . . . , 𝑥𝑥𝑥𝑥𝑛𝑛𝑛𝑛−1 ] es una permutación
de [0,1, . . . , 𝑛𝑛𝑛𝑛 − 1].
70
Algoritmos genéticos
71
Algoritmos genéticos
𝑛𝑛𝑛𝑛−1
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
min � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
Este modelo nos llevaría a un cromosoma binario de tamaño 𝑛𝑛𝑛𝑛, con 𝑛𝑛𝑛𝑛
restricciones y función de fitness:
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
| ∪𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥𝑗𝑗𝑗𝑗 =1 𝑉𝑉𝑉𝑉(𝑗𝑗𝑗𝑗)| = 𝑛𝑛𝑛𝑛
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛)
Donde 𝑉𝑉𝑉𝑉(𝑗𝑗𝑗𝑗) es un conjunto que incluye los barrios vecinos a 𝑗𝑗𝑗𝑗 incluyendo
él mismo, Ahora la única restricción indica que la unión de todos los
conjuntos 𝑉𝑉𝑉𝑉(𝑗𝑗𝑗𝑗) es igual al total de los barrios. El modelo no es lineal, pero
tiene menos restricciones. La función de fitness queda en la forma:
72
Algoritmos genéticos
𝑛𝑛𝑛𝑛−1
� 𝑖𝑖𝑖𝑖(𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) ≤ 𝑇𝑇𝑇𝑇
𝑖𝑖𝑖𝑖=1
𝑛𝑛𝑛𝑛
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=1 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [1, 𝑛𝑛𝑛𝑛]
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑠𝑠𝑠𝑠
Haciendo que la variable 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 esté en la casilla 𝑖𝑖𝑖𝑖 [𝑖𝑖𝑖𝑖 − 1] podemos escoger un
cromosoma de permutación con la función de fitness se escribe como:
𝑠𝑠𝑠𝑠−1
𝑏𝑏𝑏𝑏 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖 [𝑖𝑖𝑖𝑖 ])
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = �( + 𝑐𝑐𝑐𝑐)
𝑖𝑖𝑖𝑖 + 1
𝑖𝑖𝑖𝑖=0
73
Algoritmos genéticos
74
Algoritmos de Simulated Annealing
D
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣)
𝑣𝑣𝑣𝑣 ∈ V
1, ∆≤ 0
𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(∆) = �
𝑠𝑠𝑠𝑠 ∗ 𝑠𝑠𝑠𝑠 −∆/𝑇𝑇𝑇𝑇 , ∆> 0
75
Algoritmos de Simulated Annealing
𝑖𝑖𝑖𝑖
incrementos positivos con probabilidad 𝑠𝑠𝑠𝑠 1−∆/𝑇𝑇𝑇𝑇0 𝛼𝛼𝛼𝛼 . Para ajustar el
algoritmo debemos escoger los parámetros anteriores. Buscamos el valor
de la ratio Δ�𝑇𝑇𝑇𝑇 y del parámetro 𝛼𝛼𝛼𝛼 en función de 𝑉𝑉𝑉𝑉0 , 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 , 𝑛𝑛𝑛𝑛. Para conseguir
0
que la función 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(∆, 𝑖𝑖𝑖𝑖 ) sea continua en ∆ = 0 hacemos Δ�𝑇𝑇𝑇𝑇 = 1. Por lo
0
tanto 𝑇𝑇𝑇𝑇0 = Δ�𝑇𝑇𝑇𝑇 . La probabilidad de aceptación al principio será muy alta
0
y al final 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 (en el paso 𝑛𝑛𝑛𝑛, 0.01 por ejemplo). A partir de lo anterior vemos
que debe cumplirse
−𝑛𝑛𝑛𝑛
𝑠𝑠𝑠𝑠 1−𝛼𝛼𝛼𝛼 = 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓
𝑛𝑛𝑛𝑛 1
𝛼𝛼𝛼𝛼 = �
1 − ln 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓
300
Si 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 = 0.01, 𝑛𝑛𝑛𝑛 ≈ 300 tenemos 𝛼𝛼𝛼𝛼 ≈ √0.178 ≈ 0.994.
O alternativamente
200
Si 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 = 0.01, 𝑛𝑛𝑛𝑛 ≈ 200 tenemos 𝛼𝛼𝛼𝛼 ≈ √0.178 ≈ 0.991.
En muchos casos es más interesante fijar 𝛼𝛼𝛼𝛼 y calcular 𝑛𝑛𝑛𝑛 para que se cumpla
ln 0.178
la condición anterior: 𝑛𝑛𝑛𝑛 = . Para 𝛼𝛼𝛼𝛼 = 0.95 tenemos 𝑛𝑛𝑛𝑛 = 34.
ln 𝛼𝛼𝛼𝛼
76
Algoritmos de Simulated Annealing
1 − 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 99
𝑠𝑠𝑠𝑠 −𝑙𝑙𝑙𝑙𝑛𝑛𝑛𝑛(1+𝛼𝛼𝛼𝛼𝑛𝑛𝑛𝑛) = 1/(1 + 𝛼𝛼𝛼𝛼𝑛𝑛𝑛𝑛) = 𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 , 𝑛𝑛𝑛𝑛 = ≈
𝛼𝛼𝛼𝛼𝑉𝑉𝑉𝑉𝑓𝑓𝑓𝑓 𝛼𝛼𝛼𝛼
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Operadores de mutación
Para implementar el algoritmo debemos disponer de un objeto que
implemente:
public interface StateSa {
Double fitness();
StateSa mutate();
StateSa random();
StateSa copy();
}
77
Problemas y grafos
E
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
78
Problemas y grafos
Grafos virtuales
79
Problemas y grafos
Los valores del tipo Vp serán todas las configuraciones posibles del puzle.
Podemos designar cada casilla por una tupla (𝑓𝑓𝑓𝑓, 𝑐𝑐𝑐𝑐) que indique su fila y
columna. Sean 𝑉𝑉𝑉𝑉 = (𝑓𝑓𝑓𝑓, 𝑐𝑐𝑐𝑐) los valores de esa tupla que representamos por
el tipo IntPair y 𝑃𝑃𝑃𝑃 = {(𝑓𝑓𝑓𝑓, 𝑐𝑐𝑐𝑐): 𝑓𝑓𝑓𝑓 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑐𝑐𝑐𝑐 ∈ [0, 𝑛𝑛𝑛𝑛)} el conjunto de todas las
casillas. Este tipo se puede implementar como un record de Java.
Para modelar los tipos seguiremos las ideas de la programación orientada
a objetos. Desde este punto de vista un tipo se define como un conjunto de
propiedades de tipos conocidos, algunas de las cuales se concretan en
métodos, y un conjunto de restricciones sobre los valores de esas
propiedades. Un tipo tendrá, además, métodos de factoría, parsing, etc.
para ver más detalles sobre el diseño de tipos veanse los libros de
Fundamentos de Programación en esta misma colección.
80
Problemas y grafos
En los grafos virtuales cada arista suele tiene asociada un acción 𝑎𝑎𝑎𝑎(𝑣𝑣𝑣𝑣1, 𝑣𝑣𝑣𝑣2)
y un peso 𝑤𝑤𝑤𝑤(𝑣𝑣𝑣𝑣1, 𝑣𝑣𝑣𝑣2). En el ejemplo del Puzle 𝑤𝑤𝑤𝑤 (𝑣𝑣𝑣𝑣1, 𝑣𝑣𝑣𝑣2) = 1 y las acciones
posibles son las del conjunto 𝐴𝐴𝐴𝐴𝑉𝑉𝑉𝑉 = {𝑈𝑈𝑈𝑈𝑉𝑉𝑉𝑉, 𝐿𝐿𝐿𝐿𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠, 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴, 𝑅𝑅𝑅𝑅𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖ℎ𝑖𝑖𝑖𝑖}. Solo un
subconjunto 𝐴𝐴𝐴𝐴𝑉𝑉𝑉𝑉(𝑣𝑣𝑣𝑣) de 𝐴𝐴𝐴𝐴𝑉𝑉𝑉𝑉 nos dará las acciones permitidas en un vértice
concreto v.
El método Vp neighbor(A a) nos indica el vecino que se alcanza si tomamos
la acción a. Para definirlo asociemos a cada acción una dirección de
movimiento. Sean estas direcciones los pares de enteros de la lista:
81
Problemas y grafos
Dos vértices v y v1 son vecinos si existe una acción válida en v que nos
lleve hasta v1. Así el predicado isNeighbor podemo definirlo como:
min 𝑟𝑟𝑟𝑟
𝑥𝑥𝑥𝑥0 = 𝑣𝑣𝑣𝑣0
𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟−1 = 𝑣𝑣𝑣𝑣1
𝑟𝑟𝑟𝑟−1
𝑂𝑂𝑂𝑂𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑟𝑟𝑟𝑟
𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑟𝑟𝑟𝑟 − 1]
El modelo define un grafo virtual (por comprensión) y especifica que
debemos buscar el camino (simple y abierto) con extremos 𝑣𝑣𝑣𝑣0 , 𝑣𝑣𝑣𝑣1 y el
mínimo número de vértices. La variables son 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 son del tipo Vp que hemos
definido previamente para representar las diferentes configuraciones del
puzle. El número de variables es r que a su vez es variable. El camino
vendrá dado por la lista 𝑥𝑥𝑥𝑥 = [𝑥𝑥𝑥𝑥0 , 𝑥𝑥𝑥𝑥1 , … , 𝑥𝑥𝑥𝑥𝑟𝑟𝑟𝑟−1 ] de vértices que cumplen la
𝑟𝑟𝑟𝑟−1
restricción 𝑂𝑂𝑂𝑂𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 .
82
Problemas y grafos
interface VirtualVertex<V,E,A>{
Public Boolean isValid();
Public List<A> actions();
Public V neighbor(A a);
public E edge(A a);
…
}
83
Problemas y grafos
interface SimpleEdge<V> {
V source();
V target();
Double weight();
}
implementa un grafo virtual que será del tipo Egraph<V,E> que extiende
el tipo Graph<V,E>.
El tipo EGraph<V, E> es un grafo extendido que extiende el tipo
Graph<V,E> con la información sobre el vértice inicial y los vértices finales,
etc.
public interface EGraph<V, E> extends Graph<V,E> {
V startVertex();
Predicate<V> goal();
…
}
84
Problemas y grafos
óptima.
Los caminos son secuencias de vértices y aristas que se representan por
el tipo GraphPath<V,E> que tiene las propiedades:
interface GraphPath<V,E>
List<E> getEdgeList()
V getEndVertex()
Graph<V,E> getGraph()
Int getLength()
V getStartVertex()
List<V> getVertexList()
double getWeight()
};
Veamos la concreción del problema del Puzzle anterior. Sea el tipo para
los vértices VertexPuzzle, el tipo para las aristas EdgePuzzle y el tipo para
las acciones ActionPuzzle.
85
Problemas y grafos
86
Problemas y grafos
}
@Override
public Boolean isValid() {
return validData(this.datos()) &&
getDato(blackPosition()) == 0;
}
87
Problemas y grafos
El primer paso es imaginar las propiedades del tipo V de los vértices del
grafo y el predicado que define los valores válidos de ese tipo. Esta tarea,
que llamaremos generalización, es similar a la que llevamos a cabo cuando
realizamos un diseño recursivo. En aquel caso buscábamos un conjunto
de problemas, ahora un conjunto de vértices. En ambos casos se trata de
imaginar propiedades de un tipo. En este caso los vértices van a ser
estados de un camino que progresa hasta el vértice final. El modelo nos va
a sugerir valores que deben cumplir ciertas restricciones y por lo tanto
debemos incluirlos como propiedades de los vértices. Estas propiedades
serán útiles para definir los vértices válidos.
El primer paso es establecer las propiedades de los vértices a partir de las
variables del modelo y sus restricciones. Los valores que toman las
variables del modelo dan una pista sobre el tipo y los valores de las
acciones. El peso de las aristas vendrá definido por la función objetivo. No
olvidemos que el peso de un camino, la suma del peso de sus aristas, debe
ser el definido por la función objetivo.
Junto a lo anterior tenemos los siguientes elementos:
Vértice Inicial (startVertex): El vértice del grafo desde donde partimos.
Está asociado al problema inicial.
Casos Base: Son vértices de los cuales ya sabemos la solución o sabemos
que no existe. Estos casos bases es conveniente caracterizarlos: saber si
88
Problemas y grafos
89
Problemas y grafos
pesos de las aristas. Esta definición del peso del camino puede deducirse
generalmente de la función objetivo del modelo del problema. Veremos
ejemplos más adelante. Pero hay muchos casos en los que el peso del
camino se debe definir de otra forma dependiendo del problema en
cuestión. Estos casos los veremos más adelante.
Camino Solución: Las soluciones que buscamos en este tipo de problemas
son caminos en el grafo diseñado que van desde el vértice inicial a uno
final que cumpla la restricción establecida. Cada camino tiene un peso. La
solución óptima es el camino de peso mínimo (o máximo).
Solución: En la mayoría de los problemas queremos que la solución sea
más específica que un camino. Es decir, queremos que sea de un tipo S
dado. Para ello, debemos implementar un método que transforme un
GraphPath<V,E> al tipo S indicado.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
90
Problemas y grafos
GraphAlg.dijsktra(graph,e1,e2,…);
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Problema de la mochila
Veamos un ejemplo para ilustrar lo anterior.
El problema de la mochila, ya resuelto anteriormente con otras técnicas,
parte de una lista de objetos 𝐿𝐿𝐿𝐿𝑜𝑜𝑜𝑜 de tamaño 𝑛𝑛𝑛𝑛. A su vez cada objeto 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 de
la lista es de la forma 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 = (𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 , 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 ) dónde 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 , 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 son,
respectivamente su peso, su valor unitario y el número máximo de
unidades permitidas. Además, la mochila tiene una capacidad 𝐶𝐶𝐶𝐶. El
problema busca ubicar en la mochila el máximo número unidades de cada
objeto que quepan en la mochila para que el valor de estos sea máximo. Si
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 es el número de unidades del objeto 𝑖𝑖𝑖𝑖 en la mochila el problema puede
enunciarse como un problema de Programación Lineal de la forma:
91
Problemas y grafos
𝑛𝑛𝑛𝑛−1
La cuestión ahora es: ¿podemos buscar un grafo tal que la solución del
problema sea un camino mínimo en el mismo? Para responder a esa
pregunta, seguimos los mismos pasos que antes:
Vértices. Para encontrar los vértices debemos generalizar el problema y
diseñar un tipo adecuado. Generalizamos el problema para obtener un
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
92
Problemas y grafos
posibles son entonces los enteros 0. . 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 . Pero puede que todas no sean
válidas. Asumimos que cuando cogemos 𝑎𝑎𝑎𝑎 unidades enteras partiendo de
un problema (𝑖𝑖𝑖𝑖, 𝑟𝑟𝑟𝑟) pasamos a plantearnos el problema (𝑖𝑖𝑖𝑖 + 1, 𝑟𝑟𝑟𝑟 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ).
Para que la alternativa sea válida el vértice alcanzado debe ser válido.
Debe cumplirse 𝑖𝑖𝑖𝑖 + 1 ≤ 𝑛𝑛𝑛𝑛, 𝑟𝑟𝑟𝑟 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ≥ 0. Las soluciones para la segunda
𝑟𝑟𝑟𝑟
restricción nos llevan a 𝑎𝑎𝑎𝑎 ≤ y por otra parte 0 ≤ 𝑎𝑎𝑎𝑎 ≤ 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 . De lo que
𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
concluimos que la lista de acciones disponibles (método actions()) debe
𝑟𝑟𝑟𝑟
devolver una lista con los enteros 0. . min(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , )
𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
1. Vecino (método V neighbor(A a)): Asumiendo que la acción es válida el
vecino de (𝑖𝑖𝑖𝑖, 𝑟𝑟𝑟𝑟) siguiendo a será (𝑖𝑖𝑖𝑖 + 1, 𝑟𝑟𝑟𝑟 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ). Observamos que
el tamaño del problema destino (t-1) es más pequeño que el del
problema fuente (t).
Los casos base, vistos más abajo, harán más específicos sus vecinos. El
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
El vértice inicial es (0, 𝐶𝐶𝐶𝐶) y el final cumple el predicado 𝑣𝑣𝑣𝑣 → 𝑣𝑣𝑣𝑣. 𝑖𝑖𝑖𝑖() = 𝑛𝑛𝑛𝑛.
93
Problemas y grafos
@Override
public List<Integer> actions() {
if(this.index == n) return new ArrayList<>();
Integer nu = greedyAction();
if(this.index == n-1)
return new ArrayList<>(nu);
List<Integer> alternativas =
IntStream.rangeClosed(0,nu)
.boxed()
.collect(Collectors.toList());
Collections.reverse(alternativas);
return alternativas;
}
94
Problemas y grafos
@Override
public MochilaVertex neighbor(Integer a) {
MochilaVertex r;
Integer cr = capacidadRestante - a *
DatosMochila.getPeso(index);
if (this.index == MochilaVertex.n – 1 ||
this.capacidadRestante == 0.)
r = MochilaVertex.of(MochilaVertex.n,0);
else r = MochilaVertex.of(index + 1, cr);
return r;
}
@Override
public MochilaEdge edge(Integer a) {
MochilaVertex v = this.neighbor(a);
return MochilaEdge.of(this,v,a);
}
…
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Podemos observar que todos los vértices finales, todos los que cumplen la
condición v.i ==n, tienen solución por lo que la restricción goalHasSolution()
es true.
95
Problemas y grafos
el peso de las aristas está relacionado con cada uno de los sumandos de la
función objetivo. Minimizar o maximizar la función objetivo es
equivalente a encontrar un camino de peso mínimo o máximo.
Pero la función objetivo puede ser más compleja y por eso tenemos que
extender la noción de peso del camino. La primera posibilidad es que en
la función objetivo aparezcan también pesos asociados a los vértices o
incluso al paso por los vértices.
Sea 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 el peso de la arista 𝑖𝑖𝑖𝑖, 𝑧𝑧𝑧𝑧𝑖𝑖𝑖𝑖 el peso asociado al vértice 𝑖𝑖𝑖𝑖, 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑘𝑘𝑘𝑘 el peso
asociado al paso por el vértice 𝑖𝑖𝑖𝑖 asumiendo que se ha llegado a él mediante
la arista 𝑗𝑗𝑗𝑗 y se sale de él mediante la arista 𝑘𝑘𝑘𝑘.
Este peso para un camino aparece en problemas de tráfico aéreo.
Imaginemos que tenemos un conjunto de vuelos, cada vuelo tiene un
origen, un destino y un tiempo de vuelo y queremos calcular el vuelo más
corto desde un origen a un destino posiblemente con varias escalas.
Claramente el problema se puede modelar mediante un grafo y la función
objetivo, el tiempo total, es la suma del tiempo de los vuelos más las
esperas en los aeropuertos para conectar un vuelo con otro. Estas esperas
son los 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑘𝑘𝑘𝑘 anteriores,
Si representamos por 𝐶𝐶𝐶𝐶𝑣𝑣𝑣𝑣 los índices de los vértices de un camino, por 𝐶𝐶𝐶𝐶𝐸𝐸𝐸𝐸
los índices de sus aristas, por 𝐶𝐶𝐶𝐶𝑝𝑝𝑝𝑝 el conjunto de tripletas 𝑖𝑖𝑖𝑖𝑗𝑗𝑗𝑗𝑗𝑗𝑗𝑗 dónde 𝑖𝑖𝑖𝑖 es un
96
Problemas y grafos
vértice del camino, 𝑗𝑗𝑗𝑗 la arista el camino que llega al vértice 𝑖𝑖𝑖𝑖, y 𝑘𝑘𝑘𝑘 la arista
el camino que sale del vértice 𝑖𝑖𝑖𝑖 del camino y por |𝐶𝐶𝐶𝐶| su peso entonces
tenemos.
Cada tripleta 𝑖𝑖𝑖𝑖𝑗𝑗𝑗𝑗𝑗𝑗𝑗𝑗 representa un vértice del camino junto con las aristas del
camino entrante y saliente a ese vértice. Si 𝑧𝑧𝑧𝑧𝑖𝑖𝑖𝑖 , 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑘𝑘𝑘𝑘 son cero entonces el
peso del camino es solo la suma de los pesos de las aristas.
Estos caminos cuyo peso se calcula con el sumatorio anterior los llamamos
caminos de tipo Sum.
Esos pesos en los vértices, el peso del paso por los vértices, el vértice
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
97
Problemas y grafos
suma de los pesos de las aristas más otros pesos adicionales en algunos y
el del segundo el peso del camino es el valor de una propiedad del último
vértice. Según el tipo de sus caminos los grafos extendidos también serán
de tipo Sum o tipo Last.
Veamos las diversas propiedades, su significado y las peculiaridades
según el tipo del camino.
enum PathType{Sum,Last}
interface EGraphPath<V,E> extends GraphPath<V,E> {
E lastEdge();
EGraphPath<V, E> add(E edge);
Double add(E edge,V vertexActual,
Double accumulateValue,E lastEdge);
Estimación del peso del camino desde el vértice inicial hasta el vértice
final pasando por el vértice actual, usando la heurística y asumiendo que
el peso el camino hasta el vértice actual es weight. Este valor se calcula de
forma diferente para los caminos de tipo Sum y los de tipo Last.
98
Problemas y grafos
El valor devuelto por el método boundedValue es una cota para el peso del
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
camino desde el vértice inicial que pasa por la arista edge, usa la heurística
y asume que el peso del camino hasta el vértice actual es weight. A
diferencia del método estimatedWeightToEnd anterior el método
boundedValue asume que tomamos la arista edge tras el vértice actual.
Esto nos permitirá filtrar aristas no intersantes en los algortimos que
buscan caminos óptimos.
Esta cota se calcula de forma diferente para los caminos de tipo Sum que
los de tipo Last. En el primer caso es la suma de weight, el peso hasta el
vértice actual, el peso de la arista edge, la heurística desde el vértice
alcanzado siguiendo edge hasta el final y posiblemente el peso del paso
por el vértice actual.
En el segundo caso es igual a la heurística desde el vértice alcanzado
siguiendo edge hasta el final.
99
Problemas y grafos
Los caminos extendidos son una extensión del tipo GraphPath<V,E>. Las
propiedades de este tipo son:
• List<V> getEdgeList(): Lista de aristas del camino
• List<E> getVertexList(): Lista de vértices del camino
• V getStartVertex(): Primer vértice
• V getEndVertex(): Último vértice
• Double getWeight(): Peso del camino
• Integer getLength(): Longitud del camino (número de aristas)
• Graph<V,E> getGraph(): Grafo al que pertenece el camino
La información necesaria para los caminos extendidos la incorporamos en
el grafo virtual extendido, el tipo Egraph<V,E>, algunas de cuyas
propiedades son:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
100
Problemas y grafos
101
Problemas y grafos
para esta tarea. El builder tiene dos instancias: una para grafos virtuales y
otras para grafos extendidos obtenidos a partir de un grafo real.
102
Problemas y grafos
Tareas y procesadores
El problema se formula de la siguiente manera: Dado una lista de 𝑛𝑛𝑛𝑛 tareas
con duraciones 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 y un conjunto de 𝑚𝑚𝑚𝑚 procesadores buscar la asignación
de tareas a procesadores tal que el tiempo final de ejecución sea mínimo.
Este problema puede ser modelado mediante Programación Lineal Entera
pero aquí vamos a proponer otro modelo para buscar un grafo asociado al
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
min max Σ𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥 𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖 =𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖:0..𝑛𝑛𝑛𝑛−1
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 < 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
La variable 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 indica el procesador al que se asigna la tarea 𝑖𝑖𝑖𝑖. Como vemos
la función objetivo no es un simple sumatorio. Para buscar un grafo lo
generalizamos:
TareasYProcesadoresVertex:
Propiedades:
• i: Integer, basica
• cargas: List<Double> de tamaño m, cargas de los procesadores,
básica
• cm: Double, derivada, carga del procesador más cargado
Invariante:
• 𝑐𝑐𝑐𝑐𝑚𝑚𝑚𝑚 = max 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎[𝑗𝑗𝑗𝑗]
𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
103
Problemas y grafos
Problema final:
1. p.i ==n
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0. . 𝑚𝑚𝑚𝑚 − 1}
Vecino
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 + (𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ))
Con la notación 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 + (𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ) indicamos que el par (𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ) actualiza la
casilla 𝑎𝑎𝑎𝑎 de la lista 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 añadiendole la cantidad 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 .
Peso de la arista: Debido a la forma de la función objetivo elegimos
caminos de tipos Last por facilidad. Si hubiéramos elegido caminos de tipo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
104
Problemas y grafos
105
Problemas y grafos
DirectedWeightedMultigraph<String,Vuelo> graph =
leeGrafo("ficheros/vuelos.txt");
EGraph<String, Vuelo> g =
EGraph.ofGraph(graph,"Sevilla",
v->v.equals(end),PathType.Sum,Type.Min)
.edgeWeight(Vuelo::getDuracion)
.vertexPassWeight(
(v,e1,e2)-> Vuelo.getVertexPassWeight(v,e1,e2))
.build();
106
Problemas y grafos
TriFunction<V,Predicate<V>,V,Double> heuristic
107
Problemas y grafos
108
Problemas y grafos
109
Problemas y grafos
110
Problemas y grafos
Llamaremos peso voraz 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 al peso de una solución voraz. Ambos pesos
cumplen 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 ≥ 𝑤𝑤𝑤𝑤 ∗ si el problema es de minimización y 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 ≤ 𝑤𝑤𝑤𝑤 ∗ si s de
maximización.
Junto al peso voraz disponemos del peso heurístico que llamaremos 𝑤𝑤𝑤𝑤 ℎ =
ℎ(𝑠𝑠𝑠𝑠) . Este peso es igual a la heurística desde el vértice inicial 𝑠𝑠𝑠𝑠 a uno de
los finales 𝑖𝑖𝑖𝑖. Si la heurística es admisible la relación entre los tres pesos es
𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 ≥ 𝑤𝑤𝑤𝑤 ∗ ≥ 𝑤𝑤𝑤𝑤 ℎ si el problema es de minimización y 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 ≤ 𝑤𝑤𝑤𝑤 ∗ ≤ 𝑤𝑤𝑤𝑤 ℎ si es
de maximización. Estas relaciones nos permiten hacer un test a las
soluciones voraces y las heurísticas. Si se cumple 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 = 𝑤𝑤𝑤𝑤 ℎ podemos
asegurar que la solución voraz es la solución óptima. Por otra parte la
distancia entre 𝑤𝑤𝑤𝑤 𝑔𝑔𝑔𝑔 , 𝑤𝑤𝑤𝑤 ℎ nos da una idea de la bondad de la solución voraz
y la heurística. Mejores en la medida que están más cerca.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
111
Problemas y grafos
En los caminos de tipo Sum los pesos anteriores este peso es igual a
𝑤𝑤𝑤𝑤 𝑎𝑎𝑎𝑎,𝑟𝑟𝑟𝑟 = 𝑤𝑤𝑤𝑤1𝑎𝑎𝑎𝑎 + 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖(𝑎𝑎𝑎𝑎, 𝑟𝑟𝑟𝑟). Siendo 𝑤𝑤𝑤𝑤1𝑎𝑎𝑎𝑎 el peso del camino ya recorrido desde el
vértices inicial hasta 𝑎𝑎𝑎𝑎 y 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖(𝑎𝑎𝑎𝑎, 𝑟𝑟𝑟𝑟) el valor de la función de cota del camino
que parte del vértice 𝑎𝑎𝑎𝑎 toma la alternativa hasta 𝑟𝑟𝑟𝑟 y continua hasta el final
𝑖𝑖𝑖𝑖. En el caso de caminos tipo Last 𝑤𝑤𝑤𝑤 𝑎𝑎𝑎𝑎,𝑟𝑟𝑟𝑟 = 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖(𝑎𝑎𝑎𝑎, 𝑟𝑟𝑟𝑟) = ℎ(𝑏𝑏𝑏𝑏).
La relación entre las heurísticas y las funciones de cota la hemos visto
anteriormente. En definitiva en los caminos de tipo Sum 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖(𝑎𝑎𝑎𝑎, 𝑟𝑟𝑟𝑟) =
𝑤𝑤𝑤𝑤(𝑎𝑎𝑎𝑎, 𝑏𝑏𝑏𝑏) + ℎ(𝑏𝑏𝑏𝑏). Siendo 𝑤𝑤𝑤𝑤(𝑎𝑎𝑎𝑎, 𝑏𝑏𝑏𝑏) el peso de la arista de 𝑎𝑎𝑎𝑎 a b posiblemente
con pesos adicionales como hemos visto arriba. En el caso de caminos tipo
Last 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖(𝑎𝑎𝑎𝑎, 𝑟𝑟𝑟𝑟) = ℎ(𝑏𝑏𝑏𝑏).
Los grafos que diseñamos tienen sus vértices definidos de forma implícita
en el sentido de que no estamos interesados en enumerarlos. Los
definimos cono el conjunto de valores de un tipo, el tipo del vértice, que
cumplen una propiedad. Decimos que son vértices válidos si cumplen la
propiedad dada. De la misma forma las acciones válidas son el conjunto
de valores de un tipo, el tipo de la acción, que llevan de un vértice válido a
otro también válido. Cada par vértice-acción válida define una arista del
grafo. El tipo del vértice, la propiedad que deben cumplir los vértices, el
tipo de la arista y la propiedad que cumplen las aristas válidas se deducen
del modelo que hemos hecho para un problema.
Lo anterior permite restringir los caminos posibles que llevan del vértice
inicial a uno de los finales para que coincida con las soluciones del
problema.
En muchos problemas se presentan, además, simetrías que darán lugar a
soluciones distintas pero equivalentes. Estas soluciones equivalentes
tienen el mismo valor de la función objetivo y algunas restricciones
adicionales. Si solo estamos interesados en una de las soluciones
simétricas el grafo puede ser podado. Esto se concretará en que las
acciones disponibles solo escogerán una las posibilidades simétricas.
Para concretar esto asumimos que se puede definir una relación de
equivalencia entre soluciones en la forma de un predicado 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑠𝑠𝑠𝑠1, 𝑠𝑠𝑠𝑠2). En
un grafo virtual podemos asociar a cada vértice una solución parcial por
112
Problemas y grafos
Según esa simetría podemos definir que dos vértices son equivalentes si
el número de procesadores con la misma carga es el mismo en ambos
vértices. Es decir, agrupamos los procesadores según la carga que tienen.
Dos vértices son equivalentes desde este punto de vista si tienen el mismo
número de grupos formados según cada grupo tiene los mismos
elementos.
Para filtrar las acciones las agrupamos según la carga del procesador
asociado a la acción. Dos acciones en el mismo grupo tienen asociado dos
procesadores con la misma carga. Finalmente escogemos solo una acción
de cada grupo descartando las demás.
El código para las acciones quedaría entonces como:
List<Integer> actions() {
if(this.index == DatosTyP.n) return List2.of();
Map<Double, List<Integer>> s =
IntStream.range(0, DatosTyP.m).boxed()
.collect(Collectors
.groupingBy(p -> this.cargas().get(p)));
return s.values().stream()
.map(ls -> ls.get(0))
.collect(Collectors.toList());
}
113
Problemas y grafos
Heurísticas
Como vemos, los grafos virtuales nos permiten asociar un grafo a un
problema de tal manera que los caminos del vértice inicial hasta los finales
definan las soluciones del mismo.
Este grafo puede ser de un tamaño muy grande por lo que es conveniente
filtrar al máximo las aristas para reducir en lo posible el número de
caminos válidos. Esto es lo que hacemos defiendo las acciones válidas, y
por lo tanto las aristas válidas en un vértice. También hacemos esto
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
114
Problemas y grafos
115
Problemas y grafos
Problemas e Hypergrafos
En los casos más generales un vértice tiene varios vecinos tras tomar una
alternativa. En este caso los grafos que se forman son hipergrafos. En
nuestro caso las aristas de esos grafos (hiperedges) tiene un vértice fuente
y varios destinos. Diseñamos hipergrafos virtuales que, de forma similar a
los grafos virtuales, se concretan implementado el tipo de sus vértices, que
deben extender HyperVertex<V,E,A>, y el tipo de sus aristas, que ahora
deben extender HyperEdge<V,A>. Ahora el peso de las aristas vendrá
definido como una función del peso de los vértices.
Estos tipos tienen los métodos:
116
Problemas y grafos
los problemas que modelamos con grafos las soluciones son caminos que
van del vértice inicial a uno final y los casos base se convierten en vértices
con un solo hijo o ninguno.
Como hemos dicho en los problemas modelados con hipergrafos las
soluciones son árboles cuyas hojas son casos base y cuya raíz es el
problema a resolver. Esto cambia complemente el enfoque del problema.
Las soluciones del problema son árboles con peso que modelamos
mediante el tipo GraphTree<V,E,A>. Este tipo es un tipo recursivo con dos
subtipos: Gtb<V,E,A>, Gtr<V,E,A> que representan, respectivamente, los
árboles sin hijos y con hijos.
V vertex() {
A action();
public default Double weight() {…}
public default S solution() {…}
public default List<GraphTree2<V, E, A, S>> children()
{…}
…
}
117
Problemas y grafos
Problema De Floyd
Como ejemplo de estas ideas veamos el problema de encotrar los caminos
mínimos en un grafo g de tipo Graph<V,E> mediante el Algoritmo de Floyd
que llamaremos el Problema de Floyd. Este problema define un hipergrafo
hg con los datos del grafo de entrada y sobre él calcula el árbol mínimo.
Asumimos que cada vértice del grafo está indexado mediante un entero
comprendido en [0, 𝑛𝑛𝑛𝑛 − 1] dónde 𝑛𝑛𝑛𝑛 es el número de vértices. Esto lo
podemos conseguir obteniendo una vista adecuada con
IntegerVertexGraphView. Queremos encontrar el Camino Mínimo entre
dos ciudades dadas que representaremos por 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗. Este problema se puede
resolver de muchas otras formas como hemos visto. Aquí vamos a resolver
el problema generalizándolo a este otro: encontrar el Camino Mínimo de 𝑖𝑖𝑖𝑖
a 𝑗𝑗𝑗𝑗 usando como camino intermedio ciudades cuyos índices estén en el
conjunto [𝑘𝑘𝑘𝑘, 𝑛𝑛𝑛𝑛). Con este planteamiento cada problema lo podemos
representar por (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘). Para que sea válido 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 toman valores en [0, 𝑛𝑛𝑛𝑛 −
1] y 𝑘𝑘𝑘𝑘 en [0, 𝑛𝑛𝑛𝑛]. El valor de 𝑘𝑘𝑘𝑘 = 𝑛𝑛𝑛𝑛 indica que el camino intermedio no
contiene ninguna ciudad. Podemos definir el predicado 𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) ≡
𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] ⋀ 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] ⋀ 𝑘𝑘𝑘𝑘 ∈ [0, 𝑛𝑛𝑛𝑛].
118
Problemas y grafos
119
Problemas y grafos
Hay vértices cuya solución es conocida y por lo tanto su peso. Son los
llamados casos base. Asumiendo que 𝑤𝑤𝑤𝑤𝑔𝑔𝑔𝑔 (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) es el peso de la arista (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗)
en el grafo dado 𝑔𝑔𝑔𝑔, los casos base y los pesos de sus soluciones, cuando las
hay, son:
⊥, (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) ∉ 𝑔𝑔𝑔𝑔, 𝑘𝑘𝑘𝑘 = 𝑛𝑛𝑛𝑛
𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑏𝑏𝑏𝑏 (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) = �
𝑤𝑤𝑤𝑤𝑔𝑔𝑔𝑔 (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗), (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) ∈ 𝑔𝑔𝑔𝑔, 𝑘𝑘𝑘𝑘 = 𝑛𝑛𝑛𝑛
La arista óptima para un vértice (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘), que representaremos por
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘), que no sea un caso base, es 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) = 𝑎𝑎𝑎𝑎𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟 𝑤𝑤𝑤𝑤(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘, 𝑎𝑎𝑎𝑎).
𝑎𝑎𝑎𝑎∈𝐴𝐴𝐴𝐴
El arbol óptimo 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) para un vértice (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) podemos definirlo de
forma recursiva como:
[𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗], 𝑏𝑏𝑏𝑏(𝑣𝑣𝑣𝑣, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)
𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) = � 𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘 + 1) , ! 𝑏𝑏𝑏𝑏(𝑣𝑣𝑣𝑣, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) ⋀ ! 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)
𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑘𝑘𝑘𝑘, 𝑘𝑘𝑘𝑘 + 1) + 𝑠𝑠𝑠𝑠(𝑘𝑘𝑘𝑘, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘 + 1) , ! 𝑏𝑏𝑏𝑏(𝑣𝑣𝑣𝑣, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) ⋀ 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)
Donde [𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗] es el formado por camino los vértices 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 y 𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑘𝑘𝑘𝑘, 𝑘𝑘𝑘𝑘 + 1) +
𝑠𝑠𝑠𝑠(𝑘𝑘𝑘𝑘, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘 + 1) la concatenación de los caminos respectivos.
El arbol óptimo 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) para un vértice (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) se define de forma
similar como
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘), 𝑏𝑏𝑏𝑏(𝑣𝑣𝑣𝑣, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)
𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘) = �
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘, 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)) , ! 𝑏𝑏𝑏𝑏(𝑣𝑣𝑣𝑣, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘)
Donde 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 construye un árbol con un vértice que es caso base y 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 un arbol
definido por un vértice y una de sus acciones posibles.
Los tipos FloydVertex y FloydEgde implementan estas ideas. El tipo de las
acciones es Boolean. El peso de las aristas se calcula a partir del peso de
los vértices vecinos (targets()).
120
Problemas y grafos
@Override
public FloydEdge edge(Boolean a) {
return FloydEdge2.of(this,this.neighbors(a), a);
}
@Override
public Boolean isBaseCase() {
return this.i.equals(this.j) || k == n;
}
@Override
public Double baseCaseSolutionWeight() {
Double r = null;
if(this.i.equals(this.j)) r = 0.;
else if(k ==n && FloydVertex2.graph
.containsEdge(this.i, this.j))
r = FloydVertex2.graph.getEdge(i, j).weight();
else if(k ==n && !FloydVertex2.graph
.containsEdge(this.i, this.j))
r = null;
return r;
}
121
Problemas y grafos
@Override
public Double solutionWeight(List<Double> solutions) {
Double weight = null;
if (!action()) weight = solutions.get(0);
else weight = solutions.get(0) +
solutions.get(1);
return weight;
}
}
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
122
Algoritmos voraces
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Introducción
A
nteriormente hemos visto el camino para obtener un grafo a partir
de un modelo de un problema. La tarea ahora es encontrar el
camino mínimo que va del vértice inicial al final. Los primeros
algoritmos que estudiaremos son los algoritmos voraces, luego veremos
los algoritmos A*, Backtraking, Programación Dinámica de Reducción y
otros.
Un algoritmo voraz, greedy en inglés, es un algoritmo iterativo que
partiendo del vértice inicial toma en cada vértice una sola de las
alternativas posibles definida por una determinada política, que
representaremos por el método getAlternativa(e) que depende del
vértice, y continúa con el vértice vecino hasta que se cumple una condición
de parada. La condición de parada en la mayoría de los casos es que se
haya alcanzado un vértice final o no haya alternativas (aristas)
disponibles.
El esquema sería:
V v = verticeInicial();
A a;
while (!condicionDeParada(v)) {
a = getAlternativa(a)
e = next(a);
}
return r(e);
123
Algoritmos voraces
124
Algoritmos voraces
}
return r;
}
Dado un grafo extendido y una función que nos proporcione la arista voraz
se puede programar de forma genérica la búsqueda del valor voraz o la
solución voraz.
125
Algoritmos voraces
Stream<Gog<V,E>> streamPair() {
return Stream2.of(this);
}
126
Algoritmos voraces
GraphPath<V,E> path(){
EGraphPath<V,E> path = this.graph.initialPath();
this.streamEdges().forEach(e->path.add(e));
return path;
}
List2.randomUnitary(graph.edgesListOf(v)).get(0);
return new GreedyOnGraph<V,E>(graph,nextEdge);
}
127
Algoritmos voraces
GreedyOnGraph<MochilaVertex, MochilaEdge> gs =
GreedyOnGraph.of(graph,MochilaVertex::greedyEdge);
algoritmos es de la forma:
private E state;
private UnaryOperator<E> next;
private Predicate<E> goal;
…
@Override
public boolean hasNext() {
return !this.goal.test(state);
}
@Override
public E next() {
E old = state;
state = this.next.apply(state);
return old;
}
128
Algoritmos voraces
Los algoritmos voraces pueden ser usados en muchos casos para calcular
heurísticas. Para ello, debemos relajar el problema, eliminando algunas
restricciones. Este nuevo problema relajado necesitará posiblemente un
nuevo tipo de vértices, estados, y un conjunto de alternativas diferentes,
que posibemente no respeten las restricciones del problema original. En
el caso de la mochila con la política de escoger el entero 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , 𝑟𝑟𝑟𝑟/𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ),
donde 𝑟𝑟𝑟𝑟 es la capacidad restante entera, 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 también entera, obtenemos un
algoritmo voraz. El peso del camino voraz obtenido nos vale como valor
inicial de la función objetivo.
Busquemos ahora un algoritmo voraz para obtener una heurística.
Escogemos un estado (𝑖𝑖𝑖𝑖, 𝑟𝑟𝑟𝑟), con r real y como política el número real
min(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , 𝑟𝑟𝑟𝑟/𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ). La división 𝑟𝑟𝑟𝑟/𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ahora es real. Con el algoritmo voraz
asociado obtenemos una heurística admisible. Esto significa escoger de
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
129
Algoritmos voraces
public Md next() {
Double a = heuristicAction();
return new Md(index()+1, cr()
- a * DatosMochila.getPeso(index()));
}
public Double weight() {
if(this.index >= DatosMochila.n) return 0.;
return heuristicAction()
*DatosMochila.getValor(index());
}
}
130
Algoritmos voraces
131
Algoritmos voraces
132
Algoritmo A*
L
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Introducción e implementación
El algoritmo A* utiliza una función de evaluación 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣) que representa el
peso del camino desde el vértice inicial hasta el final pasando por el vértice
𝑣𝑣𝑣𝑣 (el peso del camino se calcula de forma diferente según sea tipo Sum o
tipo Last según vimos anteriormente). En ese cálculo interviene ℎ(𝑣𝑣𝑣𝑣) que
representa la heurística desde el vértice actual 𝑣𝑣𝑣𝑣 hasta el final, y 𝑔𝑔𝑔𝑔(𝑣𝑣𝑣𝑣), el
peso del camino recorrido para llegar a dicho nodo 𝑣𝑣𝑣𝑣 desde el nodo inicial.
En el caso de tipo Sum 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣) = 𝑔𝑔𝑔𝑔(𝑣𝑣𝑣𝑣) + ℎ(𝑣𝑣𝑣𝑣) y en el caso Last 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣) = ℎ(𝑣𝑣𝑣𝑣).
Como camino óptimo podemos entender que sea de peso mínimo o
máximo. Asumiremos en general que es mínimo. Si es máximo lo
transformaremos en mínimo por la propiedad 𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝑓𝑓𝑓𝑓(𝑥𝑥𝑥𝑥) = 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 − 𝑓𝑓𝑓𝑓(𝑥𝑥𝑥𝑥).
La heurística tendrá que ser admisible para el caso de minimizar o de
maximizar.
Los algoritmos A* son algoritmos iterativos y como ellos tienen un estado,
una función que nos proporciona el siguiente estado y un criterio de
133
Algoritmo A*
134
Algoritmo A*
Igualmente con h.t, h.d. En el código h.t, h.d se obtendrá mediante los
métodos getKey() y getValue() aplicados a un objeto de tipo H.
El tipo H (Handle) es mutable y tiene los métodos:
• h.key(): Devuelve h.t
• h.value(): Devuelve h.d
• h.decreaseKey(t): Cambia el valor de la componente h.t. El montón
donde está el objeto h queda reordenado.
Un Montón de Fibonacci (Fibonacci<Double,D>) es un conjunto de valores
de tipo H ordenados según la componente demenor a mayor h.t (el valor
𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣) de cada vértice) y con la peculiaridad de que es reordenable
(podemos modificar h.t y el conjunto quedará ordenado de nuevo). Un
montón de Fibonacci es por lo tanto una cola de prioridad reordenable de
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
135
Algoritmo A*
Siendo 𝑣𝑣𝑣𝑣0 el vértice inicial, 𝑖𝑖𝑖𝑖0 la estimación del peso del camino hasta el
final y 𝑤𝑤𝑤𝑤0 el peso del camino formado por el vértice inicial.
136
Algoritmo A*
@Override
public V next() {
Handle<Double, Data<V, E>> hActual = heap.deleteMin();
Data<V, E> dActual = hActual.getValue();
V vertexActual = dActual.vertex;
Double actualDistance = dActual.distanceToOrigin;
E edgeToOrigen = dActual.edge;
if(forget(actualDistance, vertexActual)) return null;
for (E backEdge : graph.edgesListOf(vertexActual)) {
V v = Graphs.getOppositeVertex(
graph,backEdge,vertexActual);
Double newDistanceToOrigin = graph.add(v,
actualDistance,backEdge,edgeToOrigen);
Double newDistanceToEnd= graph.estimatedWeightToEnd(
v,newDistanceToOrigin, goal, end, heuristic);
if (!tree.containsKey(v)) {
Data<V,E> dv=Data.of(v,backEdge,
newDistanceToOrigin);
Handle<Double, Data<V, E>> hv =
heap.insert(newDistanceToEnd, dv);
tree.put(v, hv);
… // sigue abajo
137
Algoritmo A*
Como vemos el algoritmo A*, con lo explicado hasta ahora, parará cuando
quede vacío el Montón de Fibonacci. En ese caso recorrerá todos los
vértices del grafo en el orden establecido: se visita antes cuyo peso
estimado del camino desde el vértice inicial a un final pasando por él sea
menor.
Pero así sin más el algoritmo sería muy lento. Tal como explicaremos
abajo, y esta es la gran ventaja del algortimo A*, podemos parar cuando
encontremos el primer vértice final que tenga solución (que cumpla el
método goalHasSolution()).
138
Algoritmo A*
public E getEdgeToOrigin(V v) {
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
return tree.get(v).getValue().edge;
}
…
V v = endVertex;
if (!tree.containsKey(v)) return Optional.empty();
Handle<Double, Data<V, E>> hav = this.tree.get(v);
Data<V, E> dav = hav.getValue();
Double weight = dav.distanceToOrigin;
E edge = dav.edge;
List<E> edges = new ArrayList<>();
while (edge != null) {
edges.add(edge);
v = Graphs.getOppositeVertex(graph, edge, v);
edge = this.getEdgeToOrigin(v);
}
Collections.reverse(edges);
…
139
Algoritmo A*
obtener una instancia del algoritmo podemos hacerlo con los métodos de
factoría siguientes:
AStar<V, E> of(EGraph<V, E> graph) {
return new AStar<V, E>(graph,null,null);
}
AStar<V, E> of(EGraph<V, E> graph,
Double bestValue,GraphPath<V, E> optimalPath) {
return new AStar<V, E>(graph,bestValue,optimalPath);
}
En muchos casos puede ser interesante visualizar los vértices y aristas que
han sido alcanzados por el algoritmo. Estos vértices y aristas forman un
grafo que puede ser obtenido por el método graph() anterior del tipo
AStar.
public SimpleDirectedGraph<V,E> graph(){
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
SimpleDirectedGraph<V,E> g =
Graphs2.simpleDirectedGraph();
for(V v:tree.keySet()) {
g.addVertex(v);
}for(V v:tree.keySet()) {
E e = tree.get(v).getValue().edge();
if (e != null) {
V source = graph.getEdgeSource(e);
V target = graph.getEdgeTarget(e);
g.addEdge(source, target, e);
}
}
return g;
}
140
Algoritmo A*
En cada paso del algoritmo, el nodo con el valor 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣) más bajo se elimina
del montón, los valores 𝑓𝑓𝑓𝑓, 𝑔𝑔𝑔𝑔 de sus vecinos se actualizan y si estos vecinos
si no estaban se agregan al montón.
Cuando A* termina su búsqueda, ha encontrado una ruta desde el
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
principio hasta un vértice final cuyo coste real es menor o igual que el
costo estimado de cualquier camino desde el inicio hasta el final a través
de cualquier vértice abierto. Cuando la heurística es admisible, el
algoritmo A * puede ignorar los nodos abiertos con seguridad porque no
es posible que conduzcan a una solución de coste más bajo que la que ya
obtenida.
Veamos la justificación de lo anterior.
Supongamos que el algoritmo A* usa una heurística admisible, y que p
corresponde a una solución óptima. Es decir, es p es un vértice final y g(p)
es minimal. Supongamos también que m es un vértice correspondiente a
una solución subóptima. Esto es, m es final y g(m) > g(p). Justificaremos
que m nunca será seleccionado del montón y por lo tanto lo anterior no
podrá ocurrir.
Ha de existir un vértice n en el montón (vértices abiertos) que pertenece
al camino óptimo que acaba en p. Como h es admisible y tanto n como p
están en el mismo camino a la solución, entonces:
141
Algoritmo A*
que 𝑓𝑓𝑓𝑓(𝑚𝑚𝑚𝑚) = 𝑔𝑔𝑔𝑔(𝑚𝑚𝑚𝑚), 𝑓𝑓𝑓𝑓 ∗ (𝑉𝑉𝑉𝑉) = 𝑔𝑔𝑔𝑔(𝑉𝑉𝑉𝑉). Combiando lo anterior concluimos que
𝑔𝑔𝑔𝑔(𝑚𝑚𝑚𝑚) ≤ 𝑔𝑔𝑔𝑔(𝑉𝑉𝑉𝑉).
Pero habíamos supuesto que g(m) > g(p), que está en contradicción con lo
anterior. Luego el algoritmo A* nunca selecciona del montón un vértice
correspondiente a una solución subóptima. En otras palabras, si un vértice
final es seleccionado del montón (y por tanto devuelto como solución)
entonces es una solución óptima.
El algoritmo es una combinación entre búsquedas del tipo primero en
anchura con primero en profundidad: mientras que ℎ(𝑣𝑣𝑣𝑣) tiende a seguir
primero en profundidad, 𝑔𝑔𝑔𝑔(𝑣𝑣𝑣𝑣) tiende a seguir primero en anchura. De este
modo, se cambia de camino de búsqueda dependiendo de los valores de
ℎ(𝑣𝑣𝑣𝑣), 𝑔𝑔𝑔𝑔(𝑣𝑣𝑣𝑣). Es un algoritmo que va haciendo una búsqueda primero el que
tiene mejor valor de 𝑓𝑓𝑓𝑓(𝑣𝑣𝑣𝑣).
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
142
Algoritmo A*
143
Algoritmo A*
DatosMochila.capacidadInicial = 78;
MochilaVertex e1 = MochilaVertex.of(78);
MochilaVertex e2 = MochilaVertex.lastVertex();
EGraph<MochilaVertex, MochilaEdge> graph =
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
EGraph.virtual(e1,MochilaVertex.goal(),PathType.Sum, Type.Max)
.greedyEdge(MochilaVertex::greedyEdge)
.heuristic(MochilaHeuristic::heuristic)
.build();
El grafo usa una función para definir los pesos de las aristas que en este
caso depende del peso definido en MochilaEdge. El grafo tiene caminos de
tipo Sum.
AStar<MochilaVertex, MochilaEdge> ms =
GraphAlg.aStar(graph,bv,gp);
144
Algoritmo A*
GreedyOnGraph<MochilaVertex, MochilaEdge> rr =
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
GreedyOnGraph.of(graph);
AStar<MochilaVertex, MochilaEdge> ms =
AStar.of(graph,gp.getWeight(),gp);
GraphPath<MochilaVertex,MochilaEdge>> path=ms.search().get();
145
Algoritmo A*
146
Algoritmo A*
EGraph<TyPVertex,SimpleEdgeAction<TyPVertex,Integer>> graph =
SimpleVirtualGraph.last(e1,v->v.goal(),v->v.maxCarga());
AStar<TyPVertex,SimpleEdgeAction<TyPVertex, Integer>> ms =
GraphAlg.aStar(graph, Heuristica::heuristic,
AStarType.Min)
GraphPath<TyPVertex,SimpleEdgeAction<TyPVertex,Integer>> path
= ms.search().get();
SolucionTyP s = TyPVertex.getSolucion(path);
===
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
147
Algoritmo A*
148
Algoritmo A*
Para cada arista saliente del vértice actual, se busca el vértice opuesto al
actual y si comprueba si la distancia del vértice opuesto al origen es menor
a través del vértice actual. Si lo es actualiza la distancia mínima del vértice
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
149
Algoritmo A*
List<Integer> acciones(MochilaProblem v) {
List<Integer> ls = new ArrayList<>();
Integer a = this.tree.get(v).getValue().a();
while (a != null) {
ls.add(a);
v = this.tree.get(v).getValue().lastVertex();
a = this.tree.get(v).getValue().a();
}
Collections.reverse(ls);
return ls;
}
150
Esquemas recursivos: Backtracking
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Introducción e implementación
L
os algoritmos de backtracking necesitan un grafo y una definición
del peso asociado a un camino. Ese peso del camino puede tener
una definición muy general como ya hemos visto. Los vértices
deben tener asociado un tamaño y es necesario que los vecinos de un
vértice tengan un tamaño menor. Se necesitan, además, un vértice inicial,
un vértice final y/o la definición de casos base, y es importante disponer
de una heurística, aunque no es necesario.
Los algoritmos de backtracking son adecuados para buscar un camino
mínimo o máximo, también para contar el número de soluciones y para
encontrarlas todas o un número dado de ellas si las hay.
El algoritmo de backtracking va recorriendo el grafo en profundidad de
forma recursiva, desde el vértice inicial. En cada vértice v, el algoritmo
comprueba si v es un vértice final. Si no es un vértice final enumera
recursivamente todos los sus vecinos. Los vértices vecinos que no
satisfagan un criterio de filtro son descartados.
Por lo tanto, el algoritmo recorre solo una parte del grafo. El costo total
del algoritmo es el número de vértices visitados multiplicado por el costo
de obtener y procesar cada vértice.
151
Esquemas recursivos: Backtracking
152
Esquemas recursivos: Backtracking
153
Esquemas recursivos: Backtracking
return r;
}
154
Esquemas recursivos: Backtracking
@Override
public void forward(E edge) {
E lastEdge = edges.isEmpty()?null:List2.last(edges);
this.accumulateValue = this.getGraph().add(
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
this.actualVertex,this.accumulateValue,edge,lastEdge);
this.actualVertex = Graphs.getOppositeVertex(graph,edge,
this.actualVertex);
this.edges.add(edge);
this.weights.add(this.accumulateValue);
}
@Override
public void back(E edge) {
this.actualVertex = Graphs.getOppositeVertex(
graph,edge,this.actualVertex);
this.edges.remove(this.edges.size()-1);
this.weights.remove(this.weights.size()-1);
this.accumulateValue = !this.weights.isEmpty()?
List2.last(this.weights):
graph.initialPath().getWeight();
}
@Override
public EGraphPath<V, E> getPath() {
EGraphPath<V,E> ePath = graph.initialPath();
for(E e:this.edges) {
ePath.add(e);
}
return ePath;
}
155
Esquemas recursivos: Backtracking
SimpleVirtualGraph.endVertexG = e2;
EGraph<MochilaVertex, MochilaEdge> graph =
SimpleVirtualGraph.sum(
e1,MochilaVertex.goal(),x->x.weight());
GreedyOnGraph<MochilaVertex, MochilaEdge> rr =
GreedyOnGraph.of(graph,MochilaVertex::greedyEdge);
156
Esquemas recursivos: Backtracking
BT<MochilaVertex, MochilaEdge,SolucionMochila> ms =
BT.of(graph,
MochilaHeuristic::heuristic,
MochilaVertex::getSolucion,
BTType.Max);
ms.bestValue = path.getWeight();
ms.optimalPath = path;
ms.search();
SolucionMochila s = ms.getSolution().get();
System.out.println(s);
157
Esquemas recursivos: Backtracking
El grafo obtenido puede ser visualizado con las técnicas vistas para todos
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
los grafos.
158
Esquemas recursivos: Backtracking
class StateMochila {
private MochilaProblem vertice;
private Integer valorAcumulado;
private List<Integer> acciones;
private List<MochilaProblem> vertices;
void forward(Integer a) {
this.acciones.add(a);
MochilaProblem vcn = this.vertice().vecino(a);
this.vertices.add(vcn);
this.valorAcumulado = this.valorAcumulado() +
a * DatosMochila.valor(this.vertice().index());
this.vertice = vcn;
}
void back(Integer a) {
this.acciones.remove(this.acciones.size()-1);
this.vertices.remove(this.vertices.size()-1);
this.vertice=this.vertices.get(this.vertices.size()-1);
this.valorAcumulado = this.valorAcumulado() –
a * DatosMochila.valor(this.vertice.index());
}
SolucionMochila solucion() {
return SolucionMochila.of(MochilaBT.start,this.acciones);
}
159
Esquemas recursivos: Backtracking
160
Esquemas recursivos: Backtracking
MochilaBT.estado.forward(a);
btm();
MochilaBT.estado.back(a);
}
}
}
Backtracking aleatorio
Para buscar algunas soluciones en problemas complejos hay una variante
muy eficaz de los algoritmos de backtracking que denominaremos
Backtracking Aleatorio. Muy someramente se trata de elegir
aleatoriamente una de las alternativas para los problemas de tamaño
mayor que un umbral y seguir todas las alternativas para los problemas
de tamaño menor y repetir esta estrategia hasta encontrar el número de
soluciones deseadas.
Este algoritmo es especialmente eficaz para encontrar una solución al
problema de las reinas.
161
Esquemas recursivos: Backtracking
𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖)
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑖𝑖𝑖𝑖)
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑛𝑛𝑛𝑛−1
Recordamos que 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) es una restricción que indica que los valores
𝑛𝑛𝑛𝑛−1
de 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son distintos y 𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖) que los valores de 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 son una
permutación de los valores 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]. La razón para este modelo es
que las filas ocupadas serán los valores de las 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , las diagonales principales
ocupadas serán los valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑖𝑖𝑖𝑖 y las diagonales secundarias ocupadas
serán los valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖 y en los tres casos deben ser diferentes.
Para abordar el problema por Bactracking buscamos un grafo cuyos
vértices sean los problemas obtenidos al generalizar el problema de
partida. Llamemos ReinasVertex al tipo de los vértices de este grafo:
162
Esquemas recursivos: Backtracking
ReinasVertex:
Propiedades:
• i: Integer
• fo: List<Integer>, filas ocupadas
• dp: Set<Integer>, diagonales principales ocupadas, derivada
• ds: Set<Integer>, diagonales secundarias ocupadas, derivada
• vl: Set<Integer>, valores libres de las filas para colocar en columna
i, derivada
• errores(): Número de errores en los problemas finales, derivada.
• n: Integer, compartida, número columnas igual al de filas
• t: Integer, derivada n-i, tamaño
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Invariante:
• 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1 (𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜[𝑖𝑖𝑖𝑖 ] − 𝑖𝑖𝑖𝑖)
• 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1 (𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜[𝑖𝑖𝑖𝑖 ] + 𝑖𝑖𝑖𝑖)
• 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1|𝑝𝑝𝑝𝑝(𝑘𝑘𝑘𝑘) 𝑘𝑘𝑘𝑘, 𝑉𝑉𝑉𝑉(𝑘𝑘𝑘𝑘) ≡ 𝑘𝑘𝑘𝑘 ∉ 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜 ⋀ (𝑘𝑘𝑘𝑘 − 𝑖𝑖𝑖𝑖 ) ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ⋀ (𝑘𝑘𝑘𝑘 + 𝑖𝑖𝑖𝑖) ∉
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
• errores = n-|dp|+n-|fo|+n-|ds|
La propiedad errores no es necesaria pero la usamos para mostrar que la
solución encontrada es adecuada y también para caracterizar los
problemas finales que tienen solución.
Interpretación:
Encontrar la asignación de las reinas desde i hasta el final asumiendo que
ya están bien colocadas, sin amenazarse entre ellas, las reinas de 0..i-1
Igualdad
• Dos problemas son iguales si lo son i, fo
Es válido
• i>=0, i<=n, |fo|=|dp|=|ds|
Factoría:
• inicial(): Crea el problema (0,{}]
• goal(v) = p.i ==n
163
Esquemas recursivos: Backtracking
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: {0. . 𝑛𝑛𝑛𝑛 − 1}|𝑎𝑎𝑎𝑎 ∉ 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜, 𝑎𝑎𝑎𝑎 − 𝑖𝑖𝑖𝑖 ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎 + 𝑖𝑖𝑖𝑖 ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖}
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜 + 𝑎𝑎𝑎𝑎)
Peso de la arista: Peso de la arista 1
Peso del camino: Suma de los pesos de las aristas
Solución Voraz: Escoger aleatoriamente entre las alternativas disponibles.
Heurística: 0.
Solución: Map<Integer,Integer> que recoja la fila donde se colocará la reina
en cada columna.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
@Override
public List<Integer> actions() {
List<Integer> r =
IntStream.range(0,ReinasVertex.n).boxed()
.filter(e->!this.fo.contains(e) &&
!this.dpo.contains(e+this.index) &&
!this.dso.contains(e-this.index))
.collect(Collectors.toList());
return r;
}
164
Esquemas recursivos: Backtracking
@Override
public ReinasVertex neighbor(Integer a) {
Integer index = this.index+1;
List<Integer> fo = new ArrayList<>(this.fo);
fo.add(a);
IntegerSet dpo = this.dpo.addNew(a+this.index);
IntegerSet dso = this.dso.addNew(a-this.index);
return ReinasVertex.of(index, fo, dpo, dso);
}
…
}
soluciones es de la forma:
ReinasVertex.n = 8;
ReinasVertex e1 = ReinasVertex.first();
Predicate<ReinasVertex> goal=v -> v.index() == ReinasVertex.n;
EGraph<ReinasVertex,SimpleEdgeAction<ReinasVertex,Integer>>
graph =
EGraph.virtual(e1,ReinasVertex.goal(),PathType.Last,Type.All)
.goalHasSolution(ReinasVertex.goalHasSolution())
.solutionNumber(1000)
.vertexWeight(v->v.errores().doubleValue())
.build();
BTR<ReinasVertex,ActionSimpleEdge<ReinasVertex,
Integer>, SolucionReinas> ms =
BTR.of(graph,
SolucionReinas::of,v->ReinasVertex.n-v.index(),15);
ms.search();
System.out.println(ms.getSolutions());
165
Esquemas recursivos: Backtracking
Pero para encontrar solo una solución a este tipo de que cumpla las
restricciones, aunque no sea la mejor, es más eficiente usar Bracktracking
aleatorio de la forma:
ReinasVertex.n = 110;
ReinasVertex e1 = ReinasVertex.first();
EGraph<ReinasVertex,SimpleEdgeAction<ReinasVertex,
Integer>> graph =
EGraph.virtual(e1,ReinasVertex.goal(),PathType.Last,
Type.All)
.goalHasSolution(ReinasVertex.goalHasSolution())
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
.solutionNumber(1)
.vertexWeight(v->v.errores().doubleValue())
.build();
BTR<ReinasVertex,SimpleEdgeAction<ReinasVertex,Integer>,
SolucionReinas> ms =
BTR.of(graph,SolucionReinas::of,
v->ReinasVertex.n-v.index(),15);
ms.search();
System.out.println(ms.getSolution());
166
Programación dinámica
L
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
167
Programación dinámica
Implementación
Veamos los detalles del algoritmo de Programación Dinámica de
Reducción. Por cuestiones de eficiencia buscaremos, en primer lugar,
soluciones parciales modeladas por el tipo Sp<A> que representaremos
por sp y son tuplas (𝑎𝑎𝑎𝑎, 𝑤𝑤𝑤𝑤). La primera componente 𝑎𝑎𝑎𝑎 indica la acción que
constituye la solución y 𝑤𝑤𝑤𝑤 el valor de la función objetivo en la solución. La
memoria del algoritmo es de la forma Map<P,Sp<A>>. Donde P es el tipo
que implementa el problema y p, p’ problemas concretos. El tipo P tiene
las propiedades de un vértice del grafo que habíamos diseñado y otros
métodos que veremos abajo. El esquema recursivo del algoritmo para el
caso de Programación Dinámica de Reducción es:
168
Programación dinámica
this.bestValue = bestValue;
this.optimalPath = optimalPath;
this.withGraph = withGraph;
}
169
Programación dinámica
170
Programación dinámica
rs.add(sp);
}
addGraph(actual, edge);
}
if (!rs.isEmpty()) {
r = rs.stream().filter(s->s!=null)
.min(this.comparatorEdges).orElse(null);
this.solutionsTree.put(actual, r);
}
}
return r;
}
El algoritmo tiene una memoria en una variable que para cada vértice
encontrado asocia un par (𝑠𝑠𝑠𝑠, 𝑤𝑤𝑤𝑤): la arista que define el camino solución
desde ese vértice y el valor de la función objetivo de la solución. Este par
lo llamaremos solución parcial. La variable de la memoria es:
Map<V,Sp<E>> solutionsTree;
171
Programación dinámica
r = rs.stream().filter(s->s!=null)
.min(this.comparatorEdges).orElse(null);
Pero hemos de tener en cuenta que todas las soluciones pueden haber sido
filtradas. En ese caso no se guarda ninguna solución en la memoria porque
172
Programación dinámica
173
Programación dinámica
ya que esa solución es óptima para ese vértice. Para ello se ha diseñado el
método newHeuristic que es usado por forget.
camino inicial que sólo contiene ese vértice e ir añadiendo las aristas
óptimas guardadas en el Map para el vértice actual y cada uno de los
vértices vecinos alcanzados siguiendo la arista óptima.
174
Programación dinámica
175
Programación dinámica
176
Programación dinámica
𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
177
Programación dinámica
178
Programación dinámica
179
Programación dinámica
Solución Voraz
𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖
• Si es de maximizar ordenar las monedas por la razón �𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
mayor a menor y tomar la acción 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = con valor inicial V para
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
vr. Es decir, ordenamos las monedas por su peso unitario de mayor
a menor y vamos escogiendo de cada moneda la mayor cantidad
de unidades posibles de las monedas más pesadas
𝑉𝑉𝑉𝑉
• Si es de minimizar ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
menor a mayor y tomar la acción 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = con valor inicial V para
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
vr. Es decir ordenamos las monedas por su peso unitario de menor
a mayor y vamos escogiendo de cada moneda la mayor cantidad
de unidades posibles de las monedas menos pesadas.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Heurística
𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖
• Si es de maximizar ordenar las monedas por la razón �𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de
mayor a menor y diseñar un algoritmo voraz con acción heurística
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = pero siendo ahora vr una cantidad real y la división real.
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
𝑉𝑉𝑉𝑉
• Si es de minimizar ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
menor a mayor y tomar la acción 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = con valor inicial V para vr
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
pero siendo ahora vr una cantidad real y la división real.
180
Programación dinámica
@Override
public List<Integer> actions() {
List<Integer> r;
if(this.index() == MonedaVertex.n)
r = new ArrayList<>();
else if(this.index() == MonedaVertex.n-1 &&
this.valorRestante%Moneda.valor(this.index)==0) {
r = List.of(this.accionVoraz().action());
} else if(this.index() == MonedaVertex.n-1 &&
this.valorRestante%Moneda.valor(this.index)!=0) {
r = new ArrayList<>();
} else {
Integer nue = this.valorRestante()/
Moneda.valor(this.index);
r = IntStream.range(0,nue+1).boxed()
.collect(Collectors.toList());
Collections.reverse(r);
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
}
return r;
}
@Override
public MonedaVertex neighbor(Integer a) {
MonedaVertex r;
if(this.valorRestante() == 0) r = MonedaVertex.last();
else r= MonedaVertex.of(this.index()+1,
this.valorRestante()-a*Moneda.valor(this.index()));
return r;
}
La implementación de la arista
181
Programación dinámica
Solución:
• La solución más adecuada serían dos Multiset<Integer> que indicarían
el número de unidades de cada moneda entregadas y recibidas
• Los dos multiset se obtendrían resolviendo primero un problema de
maximización y luego el mismo pero de minimización
MonedaVertex.datosIniciales("ficheros/monedas3.txt", 36);
MonedaVertex e1 = MonedaVertex.first();
.heuristic(MonedasHeuristica::heuristic)
.build();
GreedySearchOnGraph<MonedaVertex, MonedaEdge> rr =
GreedyOnGraph.of(graph);
182
Programación dinámica
if (s1.isPresent()) System.out.println(
SolucionMonedas.of(s1.get()));
else System.out.println("No hay solucion");
…
Como puede verse tras leer los datos del problema creamos el vértice
inicial y ordenamos las monedas tal como hemos comentado. Tras ello
creamos un grafo virtual derivado de las definiciones de los vértices y
aristas. La primera tarea es calcular una solución voraz que nos pueda
servir como primera aproximación a la solución. Esto lo hacemos con el
método GreedyOnGraph.of que tiene como parámetros:
• Un grafo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
183
Programación dinámica
.collect(Collectors.toList());
Collections.reverse(r);
}
return r;
}
184
Programación dinámica
185
Programación dinámica
.max(Comparator.naturalOrder()).orElse(null);
memory.put(vertex,r);
}
}
return r;
}
186
Programación dinámica
acción que se obtiene a partir de la solución parcial del vecino y del peso
asociado a esa arista. En los casos de caminos Sum, como en este caso, la
relación es:
Spm sp = Spm.of(a,s.weight()+a*Moneda.valor(vertex.index()));
Spm sp = Spm.of(a,s.weight);
Double cota=accumulateValue+MonedasHeuristica.cota(vertex,a);
if(cota < MonedaPD.maxValue) continue;
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
187
Programación dinámica
Programación Dinámica
La Programación Dinámica es muy similar a la Programación Dinámica de
Reducción. Es, también, un algoritmo recursivo con memoria. La
diferencia ahora está en que los problemas pueden tener varios
subproblemas y por lo tanto necesitamos un hipergrafo virtual en lugar de
un grafo. Los hipergrafos que usaremos aquí son los más sencillos como
ya vimos: el cambio está en que cada vértice tras tomar una acción puede
tener varios vecinos.
Otra diferencia importante frente a las técnicas anteriores cuyas
soluciones estaban asociadas a caminos es que ahora las soluciones están
asociadas a árboles. Estos árboles tienen una raíz que es el problema por
resolver, cada vértice tiene otros árboles vecinos según la acción que
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
188
Programación dinámica
alternativa asocida a la arista. Sean 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉) y 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎) los pesos del vértice
asociado al problema 𝑉𝑉𝑉𝑉 y de la solución si tomamos la alternativa 𝑎𝑎𝑎𝑎. Entre
estos pesos existen relaciones recursivas.
Hay problemas cuya solución es conocida y por lo tanto su peso. Son los
llamados casos base. Sea 𝑏𝑏𝑏𝑏(𝑉𝑉𝑉𝑉) un predicado que nos indicada si un
problema es un caso base. El peso de la función objetivo para un caso base
será 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑏𝑏𝑏𝑏 (𝑉𝑉𝑉𝑉) que puede ser null, que representaremos por ⊥, si el
problema no tiene solución.
Si 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉) es el peso óptimo de la función objetivo en el vértice 𝑉𝑉𝑉𝑉 y
𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎) el peso de la arista que parte de 𝑉𝑉𝑉𝑉 tomando la alternativa 𝑎𝑎𝑎𝑎
entonces tenemos las relaciones:
⊥, ⊥∈ 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))
𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎) = �
𝑤𝑤𝑤𝑤(𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))), ⊥∉ 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))
Siendo w(…) una función que combina los pesos de las soluciones de los
vecinos para obtener el pseo de la solución cuando tomamos una
determinada acción 𝑎𝑎𝑎𝑎.
189
Programación dinámica
La acción óptima para un vértice 𝑉𝑉𝑉𝑉 que no sea un caso base, que
representaremos por 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑉𝑉𝑉𝑉), es 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑠𝑠𝑠𝑠) = 𝑎𝑎𝑎𝑎𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎). Si el
𝑎𝑎𝑎𝑎∈𝐴𝐴𝐴𝐴𝑝𝑝𝑝𝑝
problema fuera de maximizar se sustituiría por 𝑎𝑎𝑎𝑎𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑎𝑎𝑎𝑎𝑥𝑥𝑥𝑥 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎).
𝑎𝑎𝑎𝑎∈𝐴𝐴𝐴𝐴𝑝𝑝𝑝𝑝
El arbol óptimo 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑉𝑉𝑉𝑉) para un vértice 𝑉𝑉𝑉𝑉 se define de forma similar como
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑉𝑉𝑉𝑉), 𝑏𝑏𝑏𝑏(𝑉𝑉𝑉𝑉)
𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑉𝑉𝑉𝑉) = �
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑉𝑉𝑉𝑉, 𝑜𝑜𝑜𝑜𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑉𝑉𝑉𝑉)) , ! 𝑏𝑏𝑏𝑏(𝑉𝑉𝑉𝑉)
Donde 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 construye un árbol con un vértice que es caso base y 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 un arbol
definido por un vértice y una de sus acciones posibles.
190
Programación dinámica
191
Programación dinámica
⊥, ⊥∈ 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))
𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎) = �
𝑤𝑤𝑤𝑤(𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))), ⊥∉ 𝑤𝑤𝑤𝑤𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑉𝑉𝑉𝑉, 𝑎𝑎𝑎𝑎))
192
Programación dinámica
193
Programación dinámica
Una implementación posible es diseñar una clase clase Data que contenga
la memoria y los elementos compartidos.
@SuppressWarnings("unchecked")
public static <V extends HyperVertex<V, E, A, ?>,
E extends HyperEdge2<V,E,A,?>,A> Datos<V, E, A> get() {
if(datos == null) datos = new Datos<>();
return (Datos<V, E, A>) datos;
}
private Datos() {
super();
}
private Map<V,Sp<E>> memory = new HashMap<>();
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
194
Programación dinámica
Y en el tipo HyperEdge.
default S solution() {
return solution(this.targets().stream()
.map(v->v.solution()).toList());
}
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
195
Programación dinámica
FloydVertex.graph = graph2;
FloydVertex.n = graph2.vertexSet().size();
FloydVertex p = FloydVertex.initial(origen,destino);
Data.type = Data.DpType.Min;
SimpleDirectedGraph<Union<FloydVertex,FloydEdge>,
DefaultEdge> g = p.datos().graph();
196
Programación dinámica
@Override
public int compareTo(Spf sp) {
return this.weight.compareTo(sp.weight);
}
}
Un tipo que representa los problemas siguiendo las ideas de los vértices
del hipergrafo, pero sin comprometerse a implementar ninguna interface.
197
Programación dinámica
.getEdge(i, j).weight();
r = w;
} else if(k ==n && !FloydPD.graph
.containsEdge(this.i, this.j)) {
r = null;
}
return r;
}
}
198
Programación dinámica
} else {
List<Spf> sps = new ArrayList<>();
for (Boolean a:actual.actions()) {
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
List<Spf> spNeighbors =
new ArrayList<>();
Double s = 0.;
for (FloydProblem neighbor :
actual.neighbors(a)) {
Spf nb = search(neighbor);
if (nb == null) {
spNeighbors = null;
break;
}
spNeighbors.add(nb);
s+=nb.weight();
}
Spf spa = null;
if(spNeighbors != null) {
spa = Spf.of(a,s);
}
sps.add(spa);
}
r = sps.stream()
.filter(s -> s != null)
.min(Comparator.naturalOrder())
.orElse(null);
this.solutionsTree.put(actual, r);
}
return r;
}
199
Programación dinámica
200
LocalSearch y SimulatedAnnealing
J
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
201
LocalSearch y SimulatedAnnealing
202
LocalSearch y SimulatedAnnealing
𝐴𝐴𝐴𝐴 = {(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗), 𝑖𝑖𝑖𝑖 ∈ 1 . . 𝑛𝑛𝑛𝑛 − 2, 𝑗𝑗𝑗𝑗 ∈ 0 . . 𝑛𝑛𝑛𝑛 − 2 | 𝑗𝑗𝑗𝑗 − 𝑖𝑖𝑖𝑖 > 2}
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜�𝑐𝑐𝑐𝑐, (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗)� = 𝑐𝑐𝑐𝑐 [0, 𝑖𝑖𝑖𝑖 ] + 𝑟𝑟𝑟𝑟(𝑐𝑐𝑐𝑐 [𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗]) + 𝑐𝑐𝑐𝑐[𝑗𝑗𝑗𝑗, 𝑛𝑛𝑛𝑛]
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Donde 𝑐𝑐𝑐𝑐 [𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗] es la sublista correspondiente, 𝑟𝑟𝑟𝑟(𝑐𝑐𝑐𝑐) la lista invertida y 𝑐𝑐𝑐𝑐1 +
𝑐𝑐𝑐𝑐2 la concatenación de listas.
Con estas ideas podemos implementar un grafo virtual:
203
LocalSearch y SimulatedAnnealing
EGraph<TravelVertexInteger,TravelEdgeInteger> graph2 =
EGraph.virtual(e1,null)
.vertexWeight(v->v.weight()).build();
LocalSearch<TravelVertexInteger,TravelEdgeInteger> m =
LocalSearch.of(graph,0.,3);
TravelVertexInteger v = m.search().get();
204
LocalSearch y SimulatedAnnealing
SimulatedAnnealingSearch<TravelVertexInteger,
TravelEdgeInteger> m =
GraphAlg.simulatedAnnealing(graph2,e1,e->e.weight);
m.search();
System.out.println(m.bestWeight);
System.out.println(m.bestVertex);
205
Otras búsquedas en grafos
J
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
206
Otras búsquedas en grafos
207
Otras búsquedas en grafos
208
Otras búsquedas en grafos
sus vecinos no visitados (lo que no están en las claves del Map), los añade
a la cola o pila y actualiza el Map.
El postorden también implementa un iterador y usa dos pilas. En primer
lugar, hace el recorrido en preorden y cada vez que saca un vértice de la
primera pila lo apila en la segunda. Terminado este paso comienza a sacar
los vértices de la segunda pila. Para cada vértice hay dos momentos que
se llaman previsita y postvisita que corresponden respectivamente a
cuando se sacan de la primera y la segunda pila.
El recorrido topológico se implementa mediante un recorrido en
postorden inverso. Es en definitiva es como el postorden pero sustituyendo
la segunda pila por una cola. La primera parte es igual, pero en la segunda
se van sacando de la cola.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
209
Cuándo usar cada técnica
L
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
210
Cuándo usar cada técnica
inicial al final. La definición del peso del camino es muy importante como
antes.
Los grafos que necesitamos ahora no pueden tener caminos cerrados y
entre los vértices tiene existir una noción de tamaño tal que el tamaño de
los hijos es menor que el del padre.
El algoritmo nos devuelve una solución del problema, un número dado de
soluciones o todas las soluciones. Es el algoritmo adecuado cuando
queremos calcular más de una solución, todas las soluciones o cuando
queriendo calcular la óptima no hay solapamiento de soluciones o muy
poco.
Los algoritmos de Programación Dinámica de Reducción parten de un
grafo, real o virtual, un vértice inicial, un vértice final y una heurística.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Pueden definirse casos base en vez del vértice final pero ya hemos visto
como añadir aristas al grafo para eliminar los casos base. Usan funciones
de cota que se definen a partir de la heurística. Buscan el camino mínimo,
o máximo, del vértice inicial al final. La definición del peso del camino es
muy importante como antes.
Los grafos que necesitamos ahora no pueden tener caminos cerrados y
entre los vértices tiene existir una noción de tamaño tal que el tamaño de
los hijos es menor que el del padre.
La programación dinámica, a diferencia del Backtracking, usa memoria. Es
adecuada, frente al Backtracking, donde haya muchos caminos del vértice
inicial al final con vértices compartidos.
El algoritmo nos devuelve un GraphPath<V,E> que posteriormente habrá
que transformar en la solución buscada.
Los algoritmos de Programación Dinámica de General parten de un
hipergrafo, real o virtual, un vértice inicial y un conjunto de casos base.
Buscan el árbol mínimo, o máximo, del vértice inicial a los casos bases.
Los hipergrafos que necesitamos ahora no pueden tener caminos cerrados
y entre los vértices tiene que existir una noción de tamaño tal que el
tamaño de los hijos es menor que el del padre.
La programación dinámica general usa memoria.
211
Cuándo usar cada técnica
212
Un catálogo de problemas
V
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Recubrimiento de vértices
El Problema del Recubrimiento de Vértices (Vertex Cover) de un grafo
consiste en buscar un subconjunto mínimo de vértices tal que cada arista
del conjunto es incidente al menos a uno de los vértices escogidos y la
suma de sus pesos es mínima. Escogiendo las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈
[0, 𝑚𝑚𝑚𝑚) que tomarán el valor 1 si el vértice 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 es escogido, el Problema del
Vertex Cover puede ser formulado como:
𝑚𝑚𝑚𝑚−1
Donde por (𝑢𝑢𝑢𝑢, 𝑣𝑣𝑣𝑣) ∈ 𝐸𝐸𝐸𝐸 queremos representar el conjunto de aristas del
grafo y para cada arista sus respectivos extremos.
213
Un catálogo de problemas
𝑚𝑚𝑚𝑚−1 𝑚𝑚𝑚𝑚−1
Problema de la Mochila
El problema de la Mochila parte de una lista de objetos 𝐿𝐿𝐿𝐿 de tamaño. Cada
objeto 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 de la lista es de la forma 𝑜𝑜𝑜𝑜𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖 = (𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 , 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 ) dónde 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 , 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 son,
respectivamente, su peso, su valor unitario y el número de unidades
disponibles. La mochila tiene una capacidad 𝐶𝐶𝐶𝐶. El problema busca ubicar
en la mochila el máximo número unidades de cada objeto, teniendo en
cuenta las disponibles, que quepan en la mochila para que el valor de estos
sea máximo. Si 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 es el número de unidades del objeto 𝑖𝑖𝑖𝑖 en la mochila el
problema puede modelarse como:
214
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖=0
MochilaVertex:
Propiedades:
• i: Integer, básica
• pr: Integer, Pero restante, básica
• n: Integer, compartida
• C: Integer, compartida
• t: Integer, derivada n-i
215
Un catálogo de problemas
Interpretación:
Encontrar la elección de monedas cuyo peso es menor que pr y su valor es
máximo teniendo en cuenta solo los objetos de i hasta el final
Igualdad
• Dos problemas son iguales si lo son i,pr
Es válido
• i>=0,i<=n,pr>=0
Factoría:
• inicial(): Crea el problema (0,C)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Casos base:
1. p.pr == 0
2. p.i == n-1
Acciones:
𝑝𝑝𝑝𝑝𝑟𝑟𝑟𝑟
Sea 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = . Debido a que 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ≥ 0
𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
1. 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0}
2. 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 }
3. 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0. . min(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 )}, caso general
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 )
Casos Base 1: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑛𝑛𝑛𝑛, 0)
Casos Base 2: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 ) = (𝑛𝑛𝑛𝑛, 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 − 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 )
216
Un catálogo de problemas
Peso de la arista
Peso: 𝑤𝑤𝑤𝑤 = 𝑎𝑎𝑎𝑎 ∗ 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
Solución Voraz
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
• Ordenar los objetos por la razón �𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 de mayor a menor
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
• Acción: 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 =
𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
Heurística:
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Solución:
• La solución más adecuada serían dos Multiset<Integer> que indicarían
el número de unidades de cada objeto
• La solución se calcula a partir de los detalles del camino óptimo.
217
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
218
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
Propiedades:
• i: Integer, básica
• vr: Integer, Valor restante, básica
• n: Integer, compartida
• V: Integer, compartida
• t: Integer, derivada n-i
Interpretación:
Encontrar la elección de monedas cuyo valor suma vr y su peso es máximo
teniendo en cuenta solo las monedas de i hasta el final
Igualdad
• Dos problemas son iguales si lo son i,vr
Es válido
• i>=0,i<=n,vr>=0
219
Un catálogo de problemas
Factoría:
• inicial(): Crea el problema (0,V)
• final(): Crea el problema (n,0)
• goal(v) = p.i ==n // también lo podemos llamar caso base
Casos base:
1. p.vr == 0
2. pi. == n-1
Acciones:
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
Sea 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = . Debido a que 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟 − 𝑎𝑎𝑎𝑎 ∗ 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 ≥ 0
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑔𝑔𝑔𝑔ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟 − 𝑎𝑎𝑎𝑎 ∗ 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 )
Casos Base 1: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑛𝑛𝑛𝑛, 0)
Peso de la arista
Peso: 𝑤𝑤𝑤𝑤 = 𝑎𝑎𝑎𝑎 ∗ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
220
Un catálogo de problemas
Solución Voraz
• Maximizar
𝑉𝑉𝑉𝑉
o Ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de mayor a menor
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
o Acción: 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 =
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
• Minimizar
𝑉𝑉𝑉𝑉
o Ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de menor a mayor
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
o Acción: 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 =
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛−1
Con 𝑉𝑉𝑉𝑉′ > 𝑉𝑉𝑉𝑉. Buscaremos el valor más pequeño de 𝑉𝑉𝑉𝑉′ tal que exista una
solución voraz con
𝑛𝑛𝑛𝑛−1
Heurística:
• Maximizar
𝑉𝑉𝑉𝑉
o Ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de mayor a menor
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
o Acción: 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = , valor real y división real
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
221
Un catálogo de problemas
• Minimizar
𝑉𝑉𝑉𝑉
o Ordenar las monedas por la razón 𝑖𝑖𝑖𝑖�𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 de menor a mayor
𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟
o Acción: 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = , valor real y división real
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖
Solución:
• La solución más adecuada serían dos Multiset<Integer> que indicarían
el número de unidades de cada moneda entregadas y recibidas
• La solución se calcula a partir de los detalles del camino óptimo.
El código se puede encontrar en el repositorio
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
222
Un catálogo de problemas
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
𝑚𝑚𝑚𝑚−1
� 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = U
𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 =1
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚−1 𝑚𝑚𝑚𝑚−1
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = − � 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖[𝑗𝑗𝑗𝑗] − 𝑘𝑘𝑘𝑘 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑑𝑑𝑑𝑑𝐴𝐴𝐴𝐴(𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑑𝑑𝑑𝑑[𝑖𝑖𝑖𝑖]=1 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖), 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖=0
EstacionesVertex:
Propiedades:
• i: Integer
• bc: Set<Integer>, Barrios ya cubiertos
• n: Integer, compartida
• t: Integer, derivada n-i
223
Un catálogo de problemas
Interpretación:
Encontrar los barrios desde i hasta el final donde se ubicarán estaciones
de bomberos, cuyo número sea mínimo, con el conjunto bc de barrios ya
cubiertos.
Igualdad
• Dos problemas son iguales si lo son i,bc
Es válido
• i>=0,i<=n
Factoría:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Casos base:
1. p.i ==n
2. pi. == n-1
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0,1}
En el caso base 2 las acciones serían 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {1}, si tiene solución y
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {} si no la tiene.
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑖𝑖𝑖𝑖 + 1, 𝑏𝑏𝑏𝑏𝑐𝑐𝑐𝑐)
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (1) = (𝑖𝑖𝑖𝑖 + 1, 𝑏𝑏𝑏𝑏𝑐𝑐𝑐𝑐 + 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖))
224
Un catálogo de problemas
Pero de la Arista
Peso: 𝑤𝑤𝑤𝑤 = 𝑎𝑎𝑎𝑎
Problema de la asignación
En este problema tenemos una lista de agentes 𝐿𝐿𝐿𝐿 y una lista de tareas 𝑇𝑇𝑇𝑇
ambas del mismo tamaño 𝑛𝑛𝑛𝑛. El coste de que el agente 𝑖𝑖𝑖𝑖 realice la tarea 𝑗𝑗𝑗𝑗
sea 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗). Se pretende asignar a cada agente una tarea y sólo una de tal
forma que se ejecuten todas las tareas con el coste mínimo.
Asumimos las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 toman valor 1 si el agente 𝑖𝑖𝑖𝑖 ejecuta la
tarea 𝑗𝑗𝑗𝑗 y cero si no la ejecuta. El problema puede ser modelado de la forma:
𝑛𝑛𝑛𝑛−1,𝑛𝑛𝑛𝑛−1
Ahora las variables son binarias, toman valores cero y uno y, de nuevo,
tenemos un Problema de Programación Lineal Entera. El primer conjunto
de restricciones indica que cada agente 𝑖𝑖𝑖𝑖 tiene que realizar una tarea y
225
Un catálogo de problemas
sólo una. El segundo conjunto de restricciones indica que cada la tarea 𝑗𝑗𝑗𝑗
tiene que ser realizada por un agente y sólo uno. Este modelo puedo ser
resuelto mediante PLI.
Un segundo modelo para este problema es:
𝑛𝑛𝑛𝑛−1
Interpretación:
Encontrar la asignación con el coste mínimo de las tareas no asignadas a
los agentes desde i hasta el final.
Igualdad
• Dos problemas son iguales si lo son i,tna
Es válido
• i>=0,i<=n
226
Un catálogo de problemas
Factoría:
• inicial(): Crea el problema (0,{0..n-1})
• final(): Crea el problema (n,{})
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
2. pi. == n-1
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑟𝑟𝑟𝑟𝑖𝑖𝑖𝑖 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 − 𝑎𝑎𝑎𝑎)
Peso de la arista
Peso: 𝑤𝑤𝑤𝑤 = 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎)
227
Un catálogo de problemas
min 𝑇𝑇𝑇𝑇
𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1, 𝑗𝑗𝑗𝑗 = 0, … , 𝑚𝑚𝑚𝑚 − 1
𝑛𝑛𝑛𝑛−1
min max Σ𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥 𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖 =𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖:0..𝑛𝑛𝑛𝑛−1
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 < 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
Propiedades:
• i: Integer, básica
• cargas: List<Double> de tamaño m, cargas de los procesadores,
básica
228
Un catálogo de problemas
Invariante:
• 𝑐𝑐𝑐𝑐𝑚𝑚𝑚𝑚 = max 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎[𝑗𝑗𝑗𝑗]
𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
• 𝑛𝑛𝑛𝑛𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑛𝑛𝑛𝑛 = argmin 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎[𝑗𝑗𝑗𝑗]
𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
Interpretación:
Encontrar la asignación a los procesadores de las tareas desde i hasta el
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Igualdad
• Dos problemas son iguales si lo son i, cargas
Es válido
• i>=0, i<=n
Factoría:
• inicial(): Crea el problema (0,[0,0,0…,0])
• final(): Crea el problema (n,[0,0,0…,0])
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
Solución casos Base
1. Tiene solución
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0. . 𝑚𝑚𝑚𝑚 − 1}
229
Un catálogo de problemas
Vecino
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 + (𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ))
Peso del camino: Podemos asignar un peso a un camino completo que sean
una función del último vértice del camino. En concreto la propiedad cm
del último vértice
Solución Voraz: Escoger la acción 𝑎𝑎𝑎𝑎 = 𝑛𝑛𝑛𝑛𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑀𝑛𝑛𝑛𝑛
Heurística: Escoger la acción v.cm
Solución: Un tipo con las propiedades
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Coloreado de grafos
El Problema de Coloreado de Grafos consiste en buscar el mínimo número
de colores tal que dando un color a cada vértice sea distinto el color
asociado a dos vértices vecinos. Para modelarlo como un Problema de
Programación Lineal Entera partimos de las variables binarias 𝑦𝑦𝑦𝑦𝑘𝑘𝑘𝑘 , 𝑘𝑘𝑘𝑘 ∈
[0, 𝑚𝑚𝑚𝑚), que tomarán el valor 1 si el color 𝑘𝑘𝑘𝑘 es usado y 𝑛𝑛𝑛𝑛 es el número de
vértices del grafo. Además, introducimos las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑘𝑘𝑘𝑘 que
serán 1 si el vértice 𝑖𝑖𝑖𝑖 se le asigna el color 𝑘𝑘𝑘𝑘. El modelo del problema es:
𝑚𝑚𝑚𝑚−1
min � 𝑦𝑦𝑦𝑦𝑘𝑘𝑘𝑘
𝑘𝑘𝑘𝑘=0
𝑚𝑚𝑚𝑚−1
230
Un catálogo de problemas
La restricción (1) garantiza que cada vértice tiene color y solo uno. La
restricción (2) indica que si el vértice 𝑖𝑖𝑖𝑖 recibe de color 𝑘𝑘𝑘𝑘 este color es
usado. La restricción (3) que si los vértices 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 son vecinos no pueden
tener el mismo color.
La restricción 𝑎𝑎𝑎𝑎 ≤ 𝑏𝑏𝑏𝑏, asumiendo que las variables toman valores binarios,
tiene como soluciones posibles para (𝑎𝑎𝑎𝑎, 𝑏𝑏𝑏𝑏) los pares {(0,0), (0,1), (1,1)} y
por lo tanto si 𝑎𝑎𝑎𝑎 = 1 implica que 𝑏𝑏𝑏𝑏 = 1.
La restricción 𝑎𝑎𝑎𝑎 + 𝑏𝑏𝑏𝑏 ≤ 1, asumiendo que las variables toman valores
binarios, tiene como soluciones posibles para (𝑎𝑎𝑎𝑎, 𝑏𝑏𝑏𝑏) los pares
{(0,0), (0,1), (1,0)} y, por lo tanto, una de las dos variables toma valor 1 o
ninguna de ellas, pero no las dos a la vez.
Aunque hay otra forma mejor como ahora veremos este problema se
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
min 𝐶𝐶𝐶𝐶𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ∉ 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:𝑁𝑁𝑁𝑁(𝑖𝑖𝑖𝑖) 𝑥𝑥𝑥𝑥𝑘𝑘𝑘𝑘 , 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 < 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
Propiedades:
• i: Integer, básica
• cav: Map<Integer,Integer>, colores asignados a cada vértice en
0..i-1, básica
• ca: Set<Integer>, colores ya asignados, derivada
231
Un catálogo de problemas
Invariante
• 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎 = 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎. 𝑣𝑣𝑣𝑣𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎()
• 𝑛𝑛𝑛𝑛𝑐𝑐𝑐𝑐 = |𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎|
• 𝑐𝑐𝑐𝑐𝑣𝑣𝑣𝑣 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:𝑁𝑁𝑁𝑁(𝑖𝑖𝑖𝑖) 𝑥𝑥𝑥𝑥𝑘𝑘𝑘𝑘
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Interpretación:
Encontrar la asignación del mínimo número de colores a los vértices
desde i hasta el final teniendo en cuenta los colores ya asignados a los
vértices previos
Igualdad
• Dos problemas son iguales si lo son i, cav
Es válido
• i>=0, i<=n, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ∉ 𝑐𝑐𝑐𝑐𝑣𝑣𝑣𝑣
Factoría:
• inicial(): Crea el problema (0,{}]
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
232
Un catálogo de problemas
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0. . 𝑚𝑚𝑚𝑚 − 1} − 𝑐𝑐𝑐𝑐𝑣𝑣𝑣𝑣
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 + (𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎))
Solución Voraz:
• Si |𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎 − 𝑐𝑐𝑐𝑐𝑣𝑣𝑣𝑣| > 0 escoger aleatoriamente una de las acciones del
conjunto 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎 − 𝑐𝑐𝑐𝑐𝑣𝑣𝑣𝑣
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
233
Un catálogo de problemas
𝑚𝑚𝑚𝑚−1
min � 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0
𝑛𝑛𝑛𝑛−1
Un modelo alternativo sería con las variables de tipo entero 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 que indica
el contenedor donde colocar el objeto 𝑖𝑖𝑖𝑖. Asumimos un número de
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
min 𝐶𝐶𝐶𝐶𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛−1
0 ≤ Σ𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥 𝑣𝑣𝑣𝑣 < 𝑉𝑉𝑉𝑉,
𝑖𝑖𝑖𝑖 =𝑘𝑘𝑘𝑘 𝑖𝑖𝑖𝑖
𝑘𝑘𝑘𝑘 = 0, … , 𝑚𝑚𝑚𝑚 − 1
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 < 𝑛𝑛𝑛𝑛, 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖 = 0, … , 𝑛𝑛𝑛𝑛 − 1
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(d)= -𝐶𝐶𝐶𝐶𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖] − 𝑘𝑘𝑘𝑘 ∑𝑛𝑛𝑛𝑛−1
𝑘𝑘𝑘𝑘=0 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 (−𝑉𝑉𝑉𝑉 + 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0|𝑑𝑑𝑑𝑑[𝑖𝑖𝑖𝑖]=𝑘𝑘𝑘𝑘 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 )
PackVertex:
Propiedades:
• i: Integer
• carga: Map<Integer,Integer>, ocupación de los contenedores.
Básica. Las claves serán los contenedores parcial o totalmente
ocupados. Si un contenedor no pertenece a las claves está vacío
234
Un catálogo de problemas
Interpretación:
Encontrar la asignación de los objetos desde i hasta el final en los
contenedores asumiendo que ya están parcialmente ocupados
Igualdad
• Dos problemas son iguales si lo son i, as
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Es válido
• i>=0, i<=n, oc[i] <=V
Factoría:
• inicial(): Crea el problema (0,{}]
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: {0. . 𝑛𝑛𝑛𝑛𝑐𝑐𝑐𝑐 + 1}|𝑜𝑜𝑜𝑜𝑐𝑐𝑐𝑐 [𝑎𝑎𝑎𝑎] + 𝑣𝑣𝑣𝑣(𝑖𝑖𝑖𝑖 ) ≤ 𝑉𝑉𝑉𝑉
235
Un catálogo de problemas
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑎𝑎𝑎𝑎𝑠𝑠𝑠𝑠 + (𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎))
236
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖, 𝑦𝑦𝑦𝑦 = −𝑥𝑥𝑥𝑥 + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖. Los valores mínimos y máximos de dp, ds son por lo
tanto: 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖1 = −𝑛𝑛𝑛𝑛, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 𝑛𝑛𝑛𝑛, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖1 = 0, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 2𝑛𝑛𝑛𝑛. Observando que en las
diagonales extremas sólo hay una casilla los rangos de valores anteriores
pueden ser reducidos a 𝑖𝑖𝑖𝑖𝑉𝑉𝑉𝑉1 = −𝑛𝑛𝑛𝑛 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 𝑛𝑛𝑛𝑛 − 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖1 = 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖2 = 2𝑛𝑛𝑛𝑛 −
1. En este problema no se trata de encontrar un mínimo. Se trata de
encontrar una solución y puede haber muchas.
A partir de este modelo el problema se podría resolver mediante
algoritmos genéticos, aunque hay otros modelos mejores para este
propósito. Se deja como ejercicio la elección del cromosoma y la función
de fitness.
El problema puede ser modelado alternativamente usando las variables
enteras 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖: [0, 𝑛𝑛𝑛𝑛 − 1], que indica que una reina se colocará en la casilla
(𝑖𝑖𝑖𝑖, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) y las restricciones:
𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖)
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑖𝑖𝑖𝑖)
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖: [0, 𝑛𝑛𝑛𝑛 − 1]
La razón es que las filas ocupadas serán los valores de las 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , las diagonales
principales ocupadas serán los valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 − 𝑖𝑖𝑖𝑖 y las diagonales secundarias
ocupadas serán los valores 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖.
237
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = −𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 (𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖] − 𝑖𝑖𝑖𝑖) − 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 (𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖] + 𝑖𝑖𝑖𝑖)
ReinasVertex:
Propiedades:
• i: Integer, básica
• fo: List<Integer>, filas ocupadas, básica
• dp: Set<Integer>, diagonales principales ocupadas, derivada
• ds: Set<Integer>, diagonales secundarias ocupadas, derivada
• vl: Set>Integer, valores libres de las filas para colocar en columna
i, derivada
• n: Integer, compartida, número columnas igual al de filas
• t: Integer, derivada n-i, tamaño
Invariante:
• 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1 (𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜[𝑖𝑖𝑖𝑖 ] − 𝑖𝑖𝑖𝑖)
• 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1 (𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜[𝑖𝑖𝑖𝑖 ] + 𝑖𝑖𝑖𝑖)
• 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 = 𝐴𝐴𝐴𝐴𝑘𝑘𝑘𝑘:0..𝑖𝑖𝑖𝑖−1|𝑝𝑝𝑝𝑝(𝑘𝑘𝑘𝑘) 𝑘𝑘𝑘𝑘, 𝑉𝑉𝑉𝑉(𝑘𝑘𝑘𝑘) ≡ 𝑘𝑘𝑘𝑘 ∉ 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜 ⋀ (𝑘𝑘𝑘𝑘 − 𝑖𝑖𝑖𝑖 ) ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ⋀ (𝑘𝑘𝑘𝑘 + 𝑖𝑖𝑖𝑖) ∉
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
Interpretación:
Encontrar la asignación reinas desde i hasta el final asumiendo que ya
están colocadas las reinas de 0..i-1
238
Un catálogo de problemas
Igualdad
• Dos problemas son iguales si lo son i, fo
Es válido
• i>=0, i<=n, |fo|=|dp|=|ds|
Factoría:
• inicial(): Crea el problema (0,{}]
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: {0. . 𝑛𝑛𝑛𝑛 − 1}|𝑎𝑎𝑎𝑎 ∉ 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜, 𝑎𝑎𝑎𝑎 − 𝑖𝑖𝑖𝑖 ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎 + 𝑖𝑖𝑖𝑖 ∉ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑓𝑓𝑓𝑓𝑜𝑜𝑜𝑜 + 𝑎𝑎𝑎𝑎)
Heurística: 0.
239
Un catálogo de problemas
de casillas tomadas por filas de abajo arriba. Es decir 𝑉𝑉𝑉𝑉 = 𝑖𝑖𝑖𝑖 ∗ 𝑛𝑛𝑛𝑛 + 𝑗𝑗𝑗𝑗 y, a su
𝑝𝑝𝑝𝑝
vez, dado 𝑉𝑉𝑉𝑉, 𝑖𝑖𝑖𝑖 = , 𝑗𝑗𝑗𝑗 = 𝑉𝑉𝑉𝑉%𝑛𝑛𝑛𝑛. Por último cada casilla tiene la propiedad 𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛
que indica si su valor está definido al principio y el valor 𝑣𝑣𝑣𝑣 fijado para esa
casilla.
𝑖𝑖𝑖𝑖
Sea 𝜑𝜑𝜑𝜑(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑖𝑖𝑖𝑖) = {(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗)|𝑖𝑖𝑖𝑖 = � � 𝑘𝑘𝑘𝑘 + 𝑖𝑖𝑖𝑖/𝑘𝑘𝑘𝑘} y escogiendo las variables binarias
𝑘𝑘𝑘𝑘
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑣𝑣𝑣𝑣 que toman valor 1 si la casilla 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 toma el valor 𝑣𝑣𝑣𝑣 el problema puede
ser formulado como:
𝑛𝑛𝑛𝑛−1
240
Un catálogo de problemas
La primera restricción indica que un valor 𝑣𝑣𝑣𝑣, sólo puede aparecer una vez
en la fila 𝑗𝑗𝑗𝑗. La segunda restricción que un valor 𝑣𝑣𝑣𝑣 sólo puede aparece una
vez en la columna 𝑖𝑖𝑖𝑖. La tercera restricción indica que un valor 𝑣𝑣𝑣𝑣 sólo puede
aparece una vez en la subtabla 𝑖𝑖𝑖𝑖. La cuarta que en cada casilla sólo puede
haber un valor. Los problemas de sudokus suelen venir enunciados con un
conjunto de casillas con un valor asignado. Esto añadirá más restricciones
a las propuestas previamente. Así si la casilla (2,3) tiene asignado en el
enunciado del problema un 7 implicará que 𝑥𝑥𝑥𝑥2,3,7 = 1, 𝑥𝑥𝑥𝑥2,3,𝑣𝑣𝑣𝑣 = 0, 𝑣𝑣𝑣𝑣 ≠ 7.
En este problema no se trata de encontrar un mínimo. Se trata de
encontrar una solución y puede haber muchas.
Un modelo alternativo más compacto es:
𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 + 1), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴(𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖)=0|𝜑𝜑𝜑𝜑(𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖,𝑡𝑡𝑡𝑡) 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
Ahora las variables 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 son valores enteros en el rango [1, 𝑛𝑛𝑛𝑛]. La primera
restricción indica que los valores en cada fila deben ser una permutación
del conjunto {1,2,…,n}. La segunda que los valores en cada columna deben
ser una permutación del mismo conjunto y la tercera que los valores en
cada subtabla también.
El problema se puede abordar mediante algoritmos genéticos a partir de
este modelo, pero esta técnica no aporta buenos resultados. En la
literatura siguen apareciendo propuestas para resolver este problema
mediante algoritmos genéticos. Pero este problema se resuelve bien
mediante Programación Lineal Entera con el modelo anterior o mediante
backtracking.
Para resolverlo backtracking lo generalizamos para definir un grafo de
problemas:
Casilla:
• i: Integer, número de la columna
• j: Integer, número de la fila
241
Un catálogo de problemas
SudokuVertex:
Propiedades:
• lc: List<Casilla>, compartida
• i: Integer
• indices: List<Integer>, indices a las casillas libres en lc.
• vl(k): Set<Integer>, k:i..n, valores libres en la casilla de índice
indices[k], derivada
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Invariante
La lista de indices se mantiene ordenada, en primer los indices cuyas
casillas están definidas, luego de menor a mayor según la propiedad
|vl(indices)|.
Las propiedades vl(k) y er se calculan a partir de las casillas ya asignadas
Interpretación:
Encontrar la asignación de valores a casillas cuyos índices están desde
indices[i] hasta indices[n-1], asumiendo que ya están colocados los
valores para las casillas cuyos índices están entre indices[0] e indices[i-1].
Igualdad
• Dos problemas son iguales si lo son i, lc
Es válido
• i>=0, i<=n, |vl(k)|>0,k=i..n-1
242
Un catálogo de problemas
Factoría:
• inicial(): Crea el problema (0,cd]), cd es la lista de casillas definidas
• goal(v) = p.i ==n
Casos base:
1. p.i ==n
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖)
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1), 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖). 𝑣𝑣𝑣𝑣 = 𝑎𝑎𝑎𝑎
Heurística: 0
243
Un catálogo de problemas
Transformación de secuencias
Dadas dos secuencias s1 y s2 encontrar el mínimo número de
transformaciones, que aplicadas secuencialmente, transforman la
primera en la segunda. Por ejemplo transformar "cbrrrarreterb" en
"carretera".
Las transformaciones disponibles son:
• (A): Añadir al final de s1 el carácter i de s2
• (E): Eliminar el carácter en posición i de s1
• (C): Cambiar el carácter que aparece en posición i de s1 por el que
aparece en la misma posición de s2
Los valores de las acciones son los del tipo enumerado 𝐴𝐴𝐴𝐴𝑠𝑠𝑠𝑠 = {𝐴𝐴𝐴𝐴, 𝐸𝐸𝐸𝐸, 𝐶𝐶𝐶𝐶, 𝑀𝑀𝑀𝑀}.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
244
Un catálogo de problemas
Dos vértices son vecinos si hay una arista que los conecte.
𝑟𝑟𝑟𝑟−2
245
Un catálogo de problemas
SecuenciasVertex:
Propiedades:
• k, entero, básica
• s, String, básica
• n, Integer, tamaño de s, derivada
• s1, String, compartida
• s2, String, compartida
• n1, Integer, tamaño de s1, derivadas
• n2, Integer, tamaño de s2, derivada
• nd: Integer, número de caracteres diferentes en s[i:] y s2[i:]
asumiendo que si no existen son considerados como diferentes.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Invariante
𝑠𝑠𝑠𝑠[0: 𝑘𝑘𝑘𝑘] = 𝑠𝑠𝑠𝑠2[0: 𝑘𝑘𝑘𝑘]
Interpretación:
Encontrar los cambios en los caracteres desde k hasta el final para
conseguir que s sea igual a s2.
Igualdad
• Dos problemas son iguales si lo son k, s
Es válido
• Siempre que cumpla el invariante
Factoría:
• inicial(): Crea el problema (0,s1)
• goal(v) = v =(n2,s2)
Casos base:
1. k ==n
246
Un catálogo de problemas
Acciones:
{𝐴𝐴𝐴𝐴}, 𝑛𝑛𝑛𝑛 − 𝑘𝑘𝑘𝑘 = 0, 𝑛𝑛𝑛𝑛2 − 𝑘𝑘𝑘𝑘 >0
⎧
⎪ {𝐸𝐸𝐸𝐸 }, 𝑛𝑛𝑛𝑛 − 𝑘𝑘𝑘𝑘 > 0, 𝑛𝑛𝑛𝑛2 − 𝑘𝑘𝑘𝑘 =0
𝑨𝑨𝑨𝑨𝒊𝒊𝒊𝒊 = {𝐶𝐶𝐶𝐶, 𝐸𝐸𝐸𝐸 }, 𝑛𝑛𝑛𝑛 − 𝑘𝑘𝑘𝑘 > 0, 𝑛𝑛𝑛𝑛2 − 𝑘𝑘𝑘𝑘 > 0, 𝑠𝑠𝑠𝑠[𝑘𝑘𝑘𝑘] ≠ 𝑠𝑠𝑠𝑠2[𝑘𝑘𝑘𝑘]
⎨ { }
⎪ 𝑀𝑀𝑀𝑀 , 𝑛𝑛𝑛𝑛 − 𝑘𝑘𝑘𝑘 > 0, 𝑛𝑛𝑛𝑛2 − 𝑘𝑘𝑘𝑘 > 0, 𝑠𝑠𝑠𝑠[𝑘𝑘𝑘𝑘] = 𝑠𝑠𝑠𝑠2[𝑘𝑘𝑘𝑘]
⎩
Hemos añadido una acción adicional, M, con peso cero para resolver el
problema. Esta acción no estará en la lista solución.
Vecino
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Peso de la arista:
Depende de la acción tomada
1, 𝑎𝑎𝑎𝑎 = 𝐴𝐴𝐴𝐴
1, 𝑎𝑎𝑎𝑎 = 𝐶𝐶𝐶𝐶
𝑤𝑤𝑤𝑤(𝑎𝑎𝑎𝑎) = �
1, 𝑎𝑎𝑎𝑎 = 𝐸𝐸𝐸𝐸
0, 𝑎𝑎𝑎𝑎 = 𝑀𝑀𝑀𝑀
Solución: List<(As,Integer)>
247
Un catálogo de problemas
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 � 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖:0..𝑛𝑛𝑛𝑛−1,𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 𝑐𝑐𝑐𝑐𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗), 𝑖𝑖𝑖𝑖 ∈ (0, 𝑛𝑛𝑛𝑛 − 1), 𝑗𝑗𝑗𝑗 ∈ (0, 𝑛𝑛𝑛𝑛 − 1)
� 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ (0, 𝑚𝑚𝑚𝑚 − 1)
𝑖𝑖𝑖𝑖:0..𝑛𝑛𝑛𝑛−1
248
Un catálogo de problemas
𝑀𝑀𝑀𝑀 se alinea con uno de 𝑌𝑌𝑌𝑌 como máximo. La última restricción indica que si
alinean los pares (𝑖𝑖𝑖𝑖1, 𝑗𝑗𝑗𝑗1) y (𝑖𝑖𝑖𝑖2, 𝑗𝑗𝑗𝑗2) tienen que estar en el mismo orden. Es
decir si no están en el mismo orden, 𝑖𝑖𝑖𝑖2 > 𝑖𝑖𝑖𝑖1, 𝑗𝑗𝑗𝑗2 < 𝑗𝑗𝑗𝑗1, solo uno de los pares
puede estar alineado.
El modelo anterior es adecuado para resolver el problema mediante PLI.
Veamos como modelar el problema mediante un grafo. Sea Vs el tipo de
los vértices cuyas propiedades son:
Propiedades de Vs
Integer m, El tamaño de la cadena X
Integer n, El tamaño de la cadena Y
Integer i, Un índice en la cadena X
Integer j, Un índice en la cadena Y
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Tomamos el par (i,j) como propiedades del vértice del grafo. Los valores
de las acciones son los del tipo enumerado 𝐴𝐴𝐴𝐴𝑠𝑠𝑠𝑠 = {𝐴𝐴𝐴𝐴, 𝐵𝐵𝐵𝐵, 𝐶𝐶𝐶𝐶}.
El resultado del método actions() se determina con las reglas: si 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖
sólo tenemos una alternativa posible que llamaremos C. Si 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≠ 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 hay dos
alternativas disponibles que representaremos por A y B según
incrementemos el índice de una u otra lista.
El método neighbor(a):
249
Un catálogo de problemas
ScmlVertex:
Propiedades:
• i: Integer, básica
• j: Integer, básica
• X: List<E>, compartida
• Y: List<E>, compartida
• n: Integer, compartida, tamaño de X
• m: Integer, compartida, tamaño de Y
• t: Integer, derivada, t = n-i+m-j
Interpretación:
Encontrar la subsecuencia común máxima entre X[i:n] y Y[j:n]
Igualdad
• Dos problemas son iguales si lo son i, j
Es válido
• i>=0, i<m, j>=0, j<n,
250
Un catálogo de problemas
Factoría:
• inicial(): Crea el problema (0,0)
Problema Final:
1. (i,m) o (n,j)
Acciones:
{𝐴𝐴𝐴𝐴, 𝐵𝐵𝐵𝐵}, 𝑀𝑀𝑀𝑀[𝑖𝑖𝑖𝑖 ] ≠ 𝑌𝑌𝑌𝑌[𝑖𝑖𝑖𝑖 ], 𝑖𝑖𝑖𝑖 < 𝑛𝑛𝑛𝑛, 𝑗𝑗𝑗𝑗 < 𝑚𝑚𝑚𝑚
𝑨𝑨𝑨𝑨𝒊𝒊𝒊𝒊 = �
{𝐶𝐶𝐶𝐶 }, 𝑀𝑀𝑀𝑀[𝑖𝑖𝑖𝑖 ] = 𝑌𝑌𝑌𝑌[𝑖𝑖𝑖𝑖 ], 𝑖𝑖𝑖𝑖 < 𝑛𝑛𝑛𝑛, 𝑗𝑗𝑗𝑗 < 𝑚𝑚𝑚𝑚
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Vecino
(𝑖𝑖𝑖𝑖 + 1, 𝑗𝑗𝑗𝑗), 𝑎𝑎𝑎𝑎 = 𝐴𝐴𝐴𝐴
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑎𝑎𝑎𝑎) = � (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 + 1), 𝑎𝑎𝑎𝑎 = 𝐵𝐵𝐵𝐵
(𝑖𝑖𝑖𝑖 + 1, 𝑗𝑗𝑗𝑗 + 1), 𝑎𝑎𝑎𝑎 = 𝐶𝐶𝐶𝐶
0, 𝑎𝑎𝑎𝑎 = 𝐴𝐴𝐴𝐴
Peso de la arista: Depende de la acción tomada 𝑤𝑤𝑤𝑤(𝑎𝑎𝑎𝑎) = �0, 𝑎𝑎𝑎𝑎 = 𝐵𝐵𝐵𝐵
1, 𝑎𝑎𝑎𝑎 = 𝐶𝐶𝐶𝐶
Heurística: min(n-i,m-j)
Solución: List<E>
251
Un catálogo de problemas
Problema jarras
Se dispone de dos jarras inicialmente vacías, J1 y J2, de capacidades en
litros C1, C2. Se desea que las jarras contengan una cierta cantidad de
litros de agua: CFJ1 y CFJ2.
Como las jarras no poseen marcas de medida, la única manera de
conseguirlo es haciendo trasvases de agua entre las mismas. Las
operaciones posibles son:
252
Un catálogo de problemas
Los valores de las acciones son los del tipo enumerado AccionJarras que
representaremos por Aj. Siendo Aj el tipo enumerado:
Cada acción válida del conjunto Aj(v) define la arista: e(v,a) que parte de
v tomando la acción válida a.
Por otra parte, dos vértices son vecinos si hay una arista que los conecte.
253
Un catálogo de problemas
Generalizamos el problema
JarrasVertex:
Propiedades:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Invariante
0 ≤ 𝑐𝑐𝑐𝑐1 ≤ 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃1 && 0 ≤ 𝑐𝑐𝑐𝑐2 ≤ 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃2
Interpretación:
Encontrar los cambios en los caracteres desde i hasta el final para
conseguir que s sea iguala s2.
Igualdad
• Dos problemas son iguales si lo son c1, c2
Es válido
• Siempre que cumpla el invariante
254
Un catálogo de problemas
Factoría:
• inicial(): Crea el problema (cI1,cI2)
• final(): Crea el problema (cF1,cF2)
Casos base:
1. Si es el problema final
Acciones:
{𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉1}, 𝑐𝑐𝑐𝑐1 > 0,
⎧
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Vecino
Caso general:
(0, 𝑐𝑐𝑐𝑐2), 𝑎𝑎𝑎𝑎 = 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉1
⎧
(𝑐𝑐𝑐𝑐1,0), 𝑎𝑎𝑎𝑎 = 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉2
⎪
⎪ (0, 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2, 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃2)), 𝑎𝑎𝑎𝑎 = 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉1𝑉𝑉𝑉𝑉2
⎪
(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2, 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃1), 0), 𝑎𝑎𝑎𝑎 = 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉2𝑉𝑉𝑉𝑉1
𝑛𝑛𝑛𝑛𝑏𝑏𝑏𝑏(𝑐𝑐𝑐𝑐1, 𝑐𝑐𝑐𝑐2, 𝑎𝑎𝑎𝑎)
⎨(𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎(0, 𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2 − 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃2), 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2, 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃2)), 𝑎𝑎𝑎𝑎 = 𝐸𝐸𝐸𝐸𝑉𝑉𝑉𝑉1𝑉𝑉𝑉𝑉2
⎪(𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2, 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃1), 𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎(0, 𝑐𝑐𝑐𝑐1 + 𝑐𝑐𝑐𝑐2 − 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃1)), 𝑎𝑎𝑎𝑎 = 𝐸𝐸𝐸𝐸𝑉𝑉𝑉𝑉2𝑉𝑉𝑉𝑉1
⎪
⎪ (𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃1, 𝑐𝑐𝑐𝑐2), 𝑎𝑎𝑎𝑎 = 𝐿𝐿𝐿𝐿𝐿𝐿𝐿𝐿𝑉𝑉𝑉𝑉1
⎩ (𝑐𝑐𝑐𝑐1, 𝑐𝑐𝑐𝑐𝑃𝑃𝑃𝑃2), 𝑎𝑎𝑎𝑎 = 𝐿𝐿𝐿𝐿𝐿𝐿𝐿𝐿𝑉𝑉𝑉𝑉2
255
Un catálogo de problemas
Heurística: 0
Solución: List<AccionJarras>
Detalles de implementación.
Cuando las acciones disponibles en un vértice, los vértices vecinos o
ambos están especificados por una secuencia compleja de if-then-else es
más conveniente diseñar una clase que represente las acciones
específicas del problema. Esta clase debe extender la interface:
interface Action<V> {
public V neighbor(V v);
public boolean isApplicable(V v);
public Double weight(V v);
}
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Robots
Se tienen n=4 tipos de materiales (A, B, C y D). Para cada uno, existe un
robot que lo puede generar, pero fabricar ese robot tiene un coste en
materiales. Cuando se fabrica un robot, éste produce 1 unidad de su
material por minuto indefinidamente. Se empieza con 1 robot de A y 0
unidades de todos los materiales.
256
Un catálogo de problemas
Sea m𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗), 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛) la cantidad de material j necesaria para construir
un robots de tipo i. Usaremos la notación mt(i) para indicar el material
necesario para construir el robots i.
Ej: mt = [[4,0,0,0],[2,0,0,0],[3,14,0,0],[3,0,7,0]]
Para modelar el problema escogemos un estado definido por el tipo Vr
cuyas variables básicas son: List<Integer> r, List<Integer> x, Integer t. Las
listas r y x tienen el mismo tamaño n, indican el número de robots y la
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑣𝑣𝑣𝑣, 𝑎𝑎𝑎𝑎)
(𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟, 𝑣𝑣𝑣𝑣. 𝑥𝑥𝑥𝑥 + 𝑛𝑛𝑛𝑛𝑚𝑚𝑚𝑚(𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟), 𝑖𝑖𝑖𝑖 + 1), 𝑎𝑎𝑎𝑎 = −1
=�
(𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟 + 𝑛𝑛𝑛𝑛𝑟𝑟𝑟𝑟(𝑎𝑎𝑎𝑎), 𝑥𝑥𝑥𝑥 + 𝑛𝑛𝑛𝑛𝑚𝑚𝑚𝑚(𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟) − 𝑚𝑚𝑚𝑚𝑐𝑐𝑐𝑐(𝑎𝑎𝑎𝑎), 𝑖𝑖𝑖𝑖 + 1), 𝑎𝑎𝑎𝑎 ≥ 0
Donde 𝑛𝑛𝑛𝑛𝑚𝑚𝑚𝑚(𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟) = 𝑣𝑣𝑣𝑣. 𝑟𝑟𝑟𝑟 son las unidades producidas por los robots
existentes a razón de una unidad por año, 𝑚𝑚𝑚𝑚𝑐𝑐𝑐𝑐 (𝑎𝑎𝑎𝑎) = 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 (𝑎𝑎𝑎𝑎) es la cantidad
de material consumido al crear un robot de tipo a y por 𝑛𝑛𝑛𝑛𝑟𝑟𝑟𝑟(𝑎𝑎𝑎𝑎) un vector
con un 1 en la posición a.
257
Un catálogo de problemas
Dos vértices son vecinos si hay una arista que los conecte.
Estantería
Se tiene una colección de libros que se quieren colocar en una estantería
compuesta por distintos estantes. Cada libro tiene una altura y una
anchura determinada, mientras que todos los estantes tienen la misma
anchura, pero distinta altura. Determinar una distribución de libros en los
estantes de forma que se maximice el número de libros colocados.
Datos
Libro:
• ℎ𝑖𝑖𝑖𝑖 : altura del libro 𝑖𝑖𝑖𝑖
• 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 : anchura del libro 𝑖𝑖𝑖𝑖
• 𝑛𝑛𝑛𝑛: número de libros
258
Un catálogo de problemas
Estante:
• 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖 : altura del estante 𝑗𝑗𝑗𝑗
• 𝐴𝐴𝐴𝐴: anchura los estantes
• 𝑚𝑚𝑚𝑚: número de estantes, de 0 a m-1
Primer Modelo
Variables
• Dependiendo de los datos los libros podrán ser colocados en los
estantes o no. Sean las variables 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 con valor uno, cero indicando
si el libro 𝑖𝑖𝑖𝑖 se ha colocado en el estante 𝑗𝑗𝑗𝑗.
Modelo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1,m−1
max Σ𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
ℎ𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚 − 1]
𝑛𝑛𝑛𝑛−1
Σ𝑖𝑖𝑖𝑖=0 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 𝐴𝐴𝐴𝐴, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚 − 1]
𝑚𝑚𝑚𝑚
Σ𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 1, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚 − 1]
Segundo Modelo
Variables
• Dependiendo de los datos los libros podrán ser colocados en los
estantes o no. Sean las variables 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 cuyo valor indicará donde se
ha colocado el libro 𝑖𝑖𝑖𝑖. Los valores de estas variables verifican 0 ≤
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑚𝑚𝑚𝑚. Si 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = 𝑚𝑚𝑚𝑚 el libro no estará colocado en ningún estante.
Modelo
𝑛𝑛𝑛𝑛−1
min�𝐶𝐶𝐶𝐶𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝1(𝑖𝑖𝑖𝑖) 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 �
ℎ𝑖𝑖𝑖𝑖 ≤ 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚 − 1], 𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗)
𝑛𝑛𝑛𝑛−1
(Σ𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝2(𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖) 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ) ≤ 𝐴𝐴𝐴𝐴, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚 − 1]
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
259
Un catálogo de problemas
En lo anterior usamos el predicado 𝑉𝑉𝑉𝑉2(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) ≡ (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = 𝑗𝑗𝑗𝑗) que indica si el libro
𝑖𝑖𝑖𝑖 está en el estante 𝑗𝑗𝑗𝑗 y el 𝑉𝑉𝑉𝑉1(𝑖𝑖𝑖𝑖 ) ≡ (𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = 𝑚𝑚𝑚𝑚) que indica si no está colocado
en ningún estante.
La restricción del sumatorio nos invita a introducir un acumulador del
espacio restante en cada estante. Sea este acumulador r(j).
El problema generalizado tendrá las propiedades: índice i, acumuladores
r(i).
El problema será válido si 0<=i<=n, r(i)>=0.
La alternativa a, en principio en el rango 0..m, se filtrará, porque no
conduce a un problema válido, si r(j) se hace negativo o el libro i no cumple
la restricción ℎ𝑖𝑖𝑖𝑖 ≤ 𝑔𝑔𝑔𝑔𝑎𝑎𝑎𝑎 . Siempre estará disponible la alternativa a =m.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Tareas solapadas
Se tienen n tareas y cada una está representada por los siguientes 3
elementos: hora de inicio, duración, ganancia asociada. Encontrar el
subconjunto de tareas que sin solaparse proporcionen la ganancia
máxima.
Datos
Tarea:
• 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 : inicio
• 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 : duración
• 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖 : ganancia
• 𝑛𝑛𝑛𝑛: número de tareas
• 𝑠𝑠𝑠𝑠𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) = max(𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 − 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 0). Solapamiento entre las tareas i,j.
Compartida.
• Las tareas se mantienen ordenadas por 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
Variables:
• 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 con valores 1,0 indicando si se ha escogido la tarea i o no.
260
Un catálogo de problemas
Modelo:
𝑛𝑛𝑛𝑛−1
max Σ𝑖𝑖𝑖𝑖=0 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖 ∗ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 +𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 1, 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], 𝑖𝑖𝑖𝑖 < 𝑗𝑗𝑗𝑗, 𝑠𝑠𝑠𝑠𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) > 0,
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
TareasSolapadasVertex:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Propiedades:
• index: Integer, básica
• te: Set<Integer>, básica, tareas ya escogidas
• n: Integer, compartida, número de tareas
Interpretación:
Encontrar la elección de tareas, desde index hasta el final, que maximicen
las ganancias totales y n se solapen
Igualdad
• Dos problemas son iguales si lo son index, te
Es válido
• index>=0,index<=n
Factoría:
• inicial(): Crea el problema (0,{})
• goal(v) = p.index ==n
Casos base:
1. p.i == n-1
261
Un catálogo de problemas
Acciones:
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={0} si ∑𝑖𝑖𝑖𝑖:𝑡𝑡𝑡𝑡𝑡𝑡𝑡𝑡 𝑠𝑠𝑠𝑠𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠, 𝑗𝑗𝑗𝑗) > 0
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={0,1} en otro caso
Vecino
Caso general:
• 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑖𝑖𝑖𝑖 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖)
• 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (1) = (𝑖𝑖𝑖𝑖 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 + 𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Peso de la arista
a*𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖
Solución Voraz
• Acción Voraz: max(a: 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 )
Heurística:
• ∑𝑖𝑖𝑖𝑖:𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛..𝑛𝑛𝑛𝑛−1 𝑔𝑔𝑔𝑔𝑖𝑖𝑖𝑖
Solución:
• La solución más adecuada sería Set<Integer> que indicarían las tareas
elegidas. En la solución habría que incluir las propiedades derivadas
adecuadas
262
Un catálogo de problemas
Recubrimiento de conjuntos
Se tiene un conjunto 𝑈𝑈𝑈𝑈 (llamado el universo) de 𝑛𝑛𝑛𝑛 elementos de tipo
entero 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], y un conjunto 𝐴𝐴𝐴𝐴 de 𝑚𝑚𝑚𝑚 conjuntos 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , cuya unión es
igual al universo. Cada conjunto 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 tiene un peso 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 ≥ 0 asociado. El
problema de cobertura de conjuntos consiste en identificar el subconjunto
de 𝐴𝐴𝐴𝐴 cuya unión es igual al universo 𝑈𝑈𝑈𝑈 y la suma de los pesos de los
conjuntos escogidos es mínima.
Definiendo las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 que indicarán si se ha escogido o no el
conjunto 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 podemos modelar el problema como
𝑚𝑚𝑚𝑚−1
𝑖𝑖𝑖𝑖=0
𝑚𝑚𝑚𝑚−1
� 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≥ 1, 𝑗𝑗𝑗𝑗 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑖𝑖𝑖𝑖=0|𝑡𝑡𝑡𝑡𝑗𝑗𝑗𝑗 ∈𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑚𝑚𝑚𝑚−1 𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚−1
𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = − � 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖[𝑗𝑗𝑗𝑗] − 𝑘𝑘𝑘𝑘 � 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(−1 + � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )
𝑖𝑖𝑖𝑖=0|𝑡𝑡𝑡𝑡𝑗𝑗𝑗𝑗 ∈𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖=0
263
Un catálogo de problemas
𝑚𝑚𝑚𝑚−1
min Σ𝑖𝑖𝑖𝑖=0 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛−1
∪𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 =1 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 = U
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑚𝑚𝑚𝑚)
ConjuntosVertex:
Propiedades:
• i: Integer, básica
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Interpretación:
Encontrar la elección de conjuntos, desde index hasta el final, que
minimicen el peso total asumiendo es el conjunto de los elementos ya
elegidos
Igualdad
• Dos problemas son iguales si lo son index, ec
Es válido
• i>=0,i<=n
Factoría:
• inicial(): Crea el problema (0,{})
• goal(v) = p.i ==n
Casos base:
1. p.i == n-1
264
Un catálogo de problemas
Acciones:
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {0} si 𝑖𝑖𝑖𝑖 = 𝑛𝑛𝑛𝑛 − 1, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 = 𝑈𝑈𝑈𝑈
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {1} si 𝑖𝑖𝑖𝑖 = 𝑛𝑛𝑛𝑛 − 1, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ≠ 𝑈𝑈𝑈𝑈, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ∪ 𝑠𝑠𝑠𝑠𝑛𝑛𝑛𝑛−1 = 𝑈𝑈𝑈𝑈
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={} si 𝑖𝑖𝑖𝑖 = 𝑛𝑛𝑛𝑛 − 1, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ≠ 𝑈𝑈𝑈𝑈, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ∪ 𝑠𝑠𝑠𝑠𝑛𝑛𝑛𝑛−1 ≠ 𝑈𝑈𝑈𝑈
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={0} si 𝑖𝑖𝑖𝑖 < 𝑛𝑛𝑛𝑛 − 1, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ∩ 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 = ∅
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={1,0} si 𝑖𝑖𝑖𝑖 < 𝑛𝑛𝑛𝑛 − 1, 𝑐𝑐𝑐𝑐𝑠𝑠𝑠𝑠 ∩ 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 ≠ ∅
Vecino
Caso general:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Peso de la arista
a*𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖
Solución Voraz
• Acción Voraz: max(a: 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 )
Heurística:
• 0
Solución:
• La solución más adecuada sería Set<Integer> que indicarían los
indices de los conjuntos elegidos. En la solución habría que incluir las
propiedades derivadas adecuadas
265
Un catálogo de problemas
Multiconjunto de enteros
Dado un conjunto de 𝑚𝑚𝑚𝑚 números enteros estrictamente positivos,
encontrar el multiconjunto (se pueden repetir varias veces cada número)
formado por números del conjunto anterior que sume exactamente N, y
que tenga el menor tamaño. El tamaño de un multiconjunto es la suma de
todas las multiplicidades para cada uno de sus elementos.
Sean 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑚𝑚𝑚𝑚 − 1] los números enteros dados y 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 el número de veces
que se repetirá cada uno, sus multiplicidades. El modelo del problema es
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑚𝑚𝑚𝑚−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 = 𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑚𝑚𝑚𝑚)
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
MultiConjuntosVertex:
Propiedades:
• index: Integer, básica
• sr: Integer, suma restante
• n: Integer, compartida, número de elementos
Es válido
• index>=0,index<=m
Factoría:
• inicial(): Crea el problema (0,{},N)
• goal(v) = p.index ==m
Casos base:
1. p.index == m-1
266
Un catálogo de problemas
Acciones:
2. 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={a} si p.index == m-1, sr es divisible por 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑎𝑎𝑎𝑎 = 𝑠𝑠𝑠𝑠𝑟𝑟𝑟𝑟�𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={} si p.index == m-1, sr no es divisible por 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 ={0..𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 } caso general, 𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 = 𝑠𝑠𝑠𝑠𝑟𝑟𝑟𝑟�𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
Vecino
Caso general:
• 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑔𝑔𝑔𝑔ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (i + 1, sr − a ∗ ei )
Peso de la arista
a
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Solución Voraz
• Acción Voraz: max(a: 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 )
Heurística:
• 0
Solución:
• La solución más adecuada sería MultiSet<Integer> que indicarían el
número de veces que se elige cada número. En la solución habría que
incluir las propiedades derivadas adecuadas.
Academia
Una academia de ingles tiene 𝑛𝑛𝑛𝑛 alumnos a ser repartidos en 𝑚𝑚𝑚𝑚 grupos (𝑛𝑛𝑛𝑛
múltiplo de 𝑚𝑚𝑚𝑚). Cada grupo tiene distinto horario y profesor. De cada
alumno se conoce la afinidad que tiene para pertenecer a cada uno de los
grupos (valor entero en el rango [0,5]). Se desea conocer el reparto de
alumnos en grupos, de forma que todos los grupos deben tener el mismo
267
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑎𝑎𝑎𝑎𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛
𝐶𝐶𝐶𝐶𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥 =𝑖𝑖𝑖𝑖,𝑎𝑎𝑎𝑎(𝑖𝑖𝑖𝑖,𝑥𝑥𝑥𝑥 )>0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑖𝑖𝑖𝑖 𝑖𝑖𝑖𝑖 𝑚𝑚𝑚𝑚
0 ≤ 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 < 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
AcademiaVertex:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Propiedades:
• index: Integer, básica
• pl: Integer[m], básica, plazas libres por grupos
• n: Integer, compartida, número de alumnos
• m: Integer, compartida, número de grupos
• Neg: Integer, compartida, número estudiantes por grupo
Interpretación:
Encontrar la asignación de estudiantes a grupos, desde index hasta el final,
que optimicen la afinidad.
Igualdad
• Dos problemas son iguales si lo son index, plazasLibres
Es válido
• index>=0,index<=n,(plazasLibres[i]>=0, i>=0,i<m)
Factoría:
• inicial(): Crea el problema (0,[Neg,…,Neg])
• goal(v) = p.i ==n // también lo podemos llamar caso base
268
Un catálogo de problemas
Casos base:
3. p.i == n-1
Acciones:
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎}, si p.i == n-1 y 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉[𝑎𝑎𝑎𝑎] − 1 = 0 𝑦𝑦𝑦𝑦 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉[𝑏𝑏𝑏𝑏] = 0, 𝑏𝑏𝑏𝑏 ≠ 𝑎𝑎𝑎𝑎
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {}, si p.i == n-1 y si |{𝑎𝑎𝑎𝑎: 0. . 𝑚𝑚𝑚𝑚 − 1|𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 [𝑎𝑎𝑎𝑎] > 0}| ≠ 1
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎: 0. . 𝑚𝑚𝑚𝑚 − 1|𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 [𝑎𝑎𝑎𝑎] > 0}
Vecino
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖 ′ ), 𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖 ′ [𝑎𝑎𝑎𝑎] = 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 [𝑎𝑎𝑎𝑎] − 1
Peso de la arista
Peso: 𝑤𝑤𝑤𝑤 = 𝑎𝑎𝑎𝑎𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎)
Solución Voraz
• Acción Voraz: argmax(𝑎𝑎𝑎𝑎𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠, 𝑗𝑗𝑗𝑗) |𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 [𝑗𝑗𝑗𝑗] > 0)
𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
Heurística:
𝑛𝑛𝑛𝑛−1
• ∑𝑖𝑖𝑖𝑖=𝑖𝑖𝑖𝑖 𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 max (𝑎𝑎𝑎𝑎𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) |𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉 [𝑗𝑗𝑗𝑗] > 0)
𝑖𝑖𝑖𝑖:0..𝑚𝑚𝑚𝑚−1
Solución:
• La solución más adecuada sería List<Integer> que indicarían el
número del grupo al que se asigna cada alumno
269
Un catálogo de problemas
Bufete
Un bufete de abogados cuenta con un equipo de 𝑛𝑛𝑛𝑛 personas que deben
analizar 𝑚𝑚𝑚𝑚 casos relacionados entre sí (𝑚𝑚𝑚𝑚 ≥ 𝑛𝑛𝑛𝑛), y deben terminar dicho
análisis global lo antes posible para lo que trabajaran en paralelo. Cada
caso será analizado por un único abogado, y cada abogado puede analizar
varios casos. Se conoce el tiempo (en horas) que se estima que tarda cada
abogado en analizar cada caso concreto (dicho tiempo puede diferir para
cada caso en función de que abogado realice el análisis). Determine cual
es la mejor asignación de casos a abogados para conseguir el objetivo
indicado (terminar de analizar todos los casos lo antes posible).
Un modelo del problema es
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
min 𝑇𝑇𝑇𝑇
𝑛𝑛𝑛𝑛−1
Donde asumimos las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 toman valor 1 si el abogado 𝑖𝑖𝑖𝑖
analiza el caso 𝑗𝑗𝑗𝑗 y cero si no lo analiza y 𝑐𝑐𝑐𝑐(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) el tiempo que tarda el
abogado 𝑖𝑖𝑖𝑖 en analizar el caso 𝑗𝑗𝑗𝑗.
Otro modelo para este problema es:
𝑚𝑚𝑚𝑚−1
En este modelo la variable 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 indica el abogado al que se asigna el caso 𝑗𝑗𝑗𝑗.
270
Un catálogo de problemas
BufeteVertex:
Propiedades:
• index: Integer, básica
• ca: Integer[n], básica, carga de abogados
• cMax: Integer, carga del abogado más cargado
• aMin, Integer, abogado menos cargado
• n: Integer, compartida, número de abogados
• m: Integer, compartida, número de tareas
Interpretación:
Encontrar la asignación de tareas a abogados, desde index hasta el final,
que minimicen cMax.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Igualdad
• Dos problemas son iguales si lo son index, ca
Es válido
• index>=0,index<=n
Factoría:
• inicial(): Crea el problema (0,new Integer[n])
• goal(v) = p.i ==n
Casos base:
1. p.i == n-1
Acciones:
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎}, 𝑎𝑎𝑎𝑎 = a = arg_min(𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎[𝑎𝑎𝑎𝑎] + 𝑐𝑐𝑐𝑐(𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖)) , si p.i = n-1
𝑎𝑎𝑎𝑎
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎: 0. . 𝑛𝑛𝑛𝑛 − 1}, caso general
271
Un catálogo de problemas
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎′), 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎′[𝑎𝑎𝑎𝑎] = 𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎[𝑎𝑎𝑎𝑎] + 𝑐𝑐𝑐𝑐(𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖)
Peso de la arista
No se le puede asignar peso a la arista
Solución Voraz
• Acción Voraz: a = arg_min(𝑐𝑐𝑐𝑐𝑎𝑎𝑎𝑎[𝑎𝑎𝑎𝑎] + 𝑐𝑐𝑐𝑐(𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖))
𝑎𝑎𝑎𝑎
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Heurística:
• La propiedad cMax del último vértice
Solución:
• La solución más adecuada sería Map<Integer,Integer> que indicarían
el abogado al que se asigna cada tarea. En la solución habría que
incluir las propiedades derivadas adecuadas
Productos y precios
Se tienen 𝑛𝑛𝑛𝑛 productos, cada uno de los cuales tiene un precio y presenta
una serie de funcionalidades (el mismo producto puede tener más de una
funcionalidad). Se desea diseñar un lote con una selección de dichos
productos que cubran un conjunto de funcionalidades deseadas entre
todos productos seleccionados al menor precio.
Sea un conjunto finito de funcionalidades 𝐹𝐹𝐹𝐹 que podemos representar por
una lista de tamaño 𝑑𝑑𝑑𝑑 de funcionalidades y un conjunto de índices 𝐴𝐴𝐴𝐴 =
{𝑖𝑖𝑖𝑖0 , 𝑖𝑖𝑖𝑖1 , … , 𝑖𝑖𝑖𝑖𝑚𝑚𝑚𝑚−1 } de tamaño 𝑚𝑚𝑚𝑚 que indique los índices a las
funcionalidades deseadas.
272
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
Sean las variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] que indica si se ha elegido el
producto 𝑖𝑖𝑖𝑖. A su vez 𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖 y 𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖 sean el precio y las funcionalidades ofrecidas,
un conjunto de índices, del producto 𝑖𝑖𝑖𝑖.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
ProductosVertex:
Propiedades:
• index: Integer, básica
• fc: Set<Integer>, básica, las funcionalidades ya cubiertas
• n: Integer, compartida, número de productos
• m: Integer, compartida, número de funcionalidades
Interpretación:
Encontrar la elección adecuada de productos, desde index hasta el final,
que minimicen el precio total y cubran todas las funcionalidades
273
Un catálogo de problemas
Igualdad
• Dos problemas son iguales si lo son index, fc
Es válido
• index>=0,index<=n
Factoría:
• inicial(): Crea el problema (0,{})
• goal(v) = p.i ==n
Casos base:
1. p.i == n-1
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Acciones:
• En el caso p.i = n-1
o {0} Si 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ⊇ 𝐴𝐴𝐴𝐴
o {1} Si ! (𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ⊇ 𝐴𝐴𝐴𝐴), 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ∪ 𝑓𝑓𝑓𝑓𝑛𝑛𝑛𝑛−1 ⊇ 𝐴𝐴𝐴𝐴
o {} Si 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ∪ 𝑓𝑓𝑓𝑓𝑛𝑛𝑛𝑛−1 ⊇ 𝐴𝐴𝐴𝐴
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎: 0. .1}, caso general
o 0 siempre
o 1 si |𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ∪ 𝑓𝑓𝑓𝑓𝑛𝑛𝑛𝑛−1 | > 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐
Vecino
Caso general:
• 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑖𝑖𝑖𝑖 + 1, 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐)
• 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (1) = (𝑖𝑖𝑖𝑖 + 1, 𝑓𝑓𝑓𝑓𝑐𝑐𝑐𝑐 ∪ 𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖 )
Peso de la arista
a*𝑉𝑉𝑉𝑉𝑖𝑖𝑖𝑖
274
Un catálogo de problemas
Solución Voraz
• Acción Voraz: max(a: 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 )
Heurística:
• 0
Solución:
• La solución más adecuada sería Set<Integer> que indicarían los
productos elegidos. En la solución habría que incluir las propiedades
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
derivadas adecuadas
Partición de conjuntos
Dado un conjunto de enteros determinar si puede particionarse en tres
subconjuntos de manera que la suma de elementos en los tres
subconjuntos sea la misma, y que el tamaño de uno de ellos sea lo menor
posible.
Sean 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] los 𝑛𝑛𝑛𝑛 elementos de tipo entero del conjunto de
partida 𝑈𝑈𝑈𝑈 y 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], 𝑗𝑗𝑗𝑗 ∈ {0,1,2} variables binarias que indican a
que el elemento 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 se asigna al subconjunto 𝑗𝑗𝑗𝑗. Un modelo del problema es
𝑛𝑛𝑛𝑛−1
min � 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖=0|𝑖𝑖𝑖𝑖=0
2
275
Un catálogo de problemas
Otro modelo del problema con 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1] variables enteras que
1 𝑛𝑛𝑛𝑛−1
indiquen a que subconjunto se asigna el elemento 𝑖𝑖𝑖𝑖. Sea 𝑖𝑖𝑖𝑖 = ∑𝑖𝑖𝑖𝑖=0 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 un
3
modelo puede ser:
𝑛𝑛𝑛𝑛−1
min 𝐶𝐶𝐶𝐶𝑖𝑖𝑖𝑖=0|𝑥𝑥𝑥𝑥 𝑥𝑥𝑥𝑥
𝑖𝑖𝑖𝑖 =0 𝑖𝑖𝑖𝑖
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 2, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑛𝑛𝑛𝑛−1
ParticionVertex:
Propiedades:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Interpretación:
Encontrar la elección adecuada de productos, desde index hasta el final,
que minimicen el precio total y cubran todas las funcionalidades
Igualdad
• Dos problemas son iguales si lo son index, fc
Es válido
• index>=0,index<=n, vr[i]>=0, i en 0..2
Factoría:
• inicial(): Crea el problema (0,[N,N,N])
• goal(v) = p.i ==n
Casos base:
1. p.i == n-1
276
Un catálogo de problemas
Acciones:
• En el caso p.i = n-1
o {a} Si 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟[𝑖𝑖𝑖𝑖 ] = 0, 𝑖𝑖𝑖𝑖 ≠ 𝑎𝑎𝑎𝑎, 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟[𝑎𝑎𝑎𝑎] − 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 = 0
o {} En otro caso
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎: 0. .2|𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟[𝑎𝑎𝑎𝑎] − 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 ≥ 0}, caso general
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (0) = (𝑖𝑖𝑖𝑖 + 1, 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟′), 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟 ′ [𝑎𝑎𝑎𝑎] = 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟[𝑎𝑎𝑎𝑎] − 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Peso de la arista
a==0?1:0
Solución Voraz
• Acción Voraz: argmax 𝑣𝑣𝑣𝑣𝑟𝑟𝑟𝑟[𝑎𝑎𝑎𝑎]
𝑖𝑖𝑖𝑖:𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖
Heurística:
• 0.
Solución:
• La solución más adecuada sería Map<Integer,Integer> que indicarían
los conjuntos asignados a cada elemento. En la solución habría que
incluir las propiedades derivadas adecuadas
277
Un catálogo de problemas
Camino cerrado
Se tiene un grafo g y un predicado sobre sus aristas. Se desea saber cuál es
el camino simple y cerrado más corto que pase por todos los vértices del
grafo una sola vez y que contiene al menos una arista que cumple el
predicado.
Asumimos que los vértices pueden ser representados por números
enteros. Sea 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 una variable entera que representa el vértice en la posición
i del recorrido, 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 una variable binaria que indica si se ha escogido la
arista (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗). Sea 𝜑𝜑𝜑𝜑 el conjunto de las aristas que cumplen el predicado dado
y 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 el peso de la arista (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗).
El problema puede ser enfocado como una red de flujo en grafos con ciclos.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
278
Un catálogo de problemas
𝑛𝑛𝑛𝑛−1
� 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≥ 1
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖=0|𝜑𝜑𝜑𝜑(𝑖𝑖𝑖𝑖,𝑖𝑖𝑖𝑖)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1], 𝑗𝑗𝑗𝑗 ∈ [1, 𝑛𝑛𝑛𝑛 − 1], (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) ∈ 𝑔𝑔𝑔𝑔
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 � 𝑤𝑤𝑤𝑤(𝑠𝑠𝑠𝑠(𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ))
𝑖𝑖𝑖𝑖=0
∃𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 (𝑠𝑠𝑠𝑠(𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) ∈ 𝜑𝜑𝜑𝜑)
𝑛𝑛𝑛𝑛−1
𝐶𝐶𝐶𝐶𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛 − 1]
Con 𝑠𝑠𝑠𝑠(𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ) la arista que conecta el vértice 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 del recorrido con el siguiente
𝑥𝑥𝑥𝑥(𝑖𝑖𝑖𝑖+1)%𝑛𝑛𝑛𝑛 , 𝑤𝑤𝑤𝑤 (𝑠𝑠𝑠𝑠(𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )) su peso y 𝜑𝜑𝜑𝜑 el conjunto de aristas que cumplen la
𝑛𝑛𝑛𝑛−1
propiedad. La restricción 𝐶𝐶𝐶𝐶𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 indica que las variables forman un
camino simple cerrado del grafo. La restricción con el operador ∧ puede
𝑛𝑛𝑛𝑛−1
ser reformulada como ∑𝑖𝑖𝑖𝑖=0|𝑡𝑡𝑡𝑡(𝑥𝑥𝑥𝑥 𝑖𝑖𝑖𝑖 ) ∈𝜑𝜑𝜑𝜑
1≥1
279
Un catálogo de problemas
ParticionVertex:
Propiedades:
• cm: List<Integer>, básica, camino ya escogido
• na: Integer, numero de aristas que cumplen la restricción
• n: Integer, compartida, número de vértices
• g: Graph<Integer,SimpleEdge<Integer>, grafo
Interpretación:
Encontrar el camino cerrado mínimo más corto, desde index hasta el final,
considerando que se ya se ha recorrido cm.
Igualdad
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Es válido
• cm es un camino
Factoría:
• inicial(): Crea el problema (0,[0],0)
• goal(v) = |cm| ==n+1
Casos base:
1. |cm| == n
Acciones:
• En el caso |cm| == n
o {a} Si 𝑎𝑎𝑎𝑎 ∈ 𝑔𝑔𝑔𝑔𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟𝑟ℎ. 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 (𝑛𝑛𝑛𝑛 − 1), (𝑛𝑛𝑛𝑛𝑎𝑎𝑎𝑎 ≥ 1 𝑜𝑜𝑜𝑜 𝑎𝑎𝑎𝑎 ∈ 𝜑𝜑𝜑𝜑)
o {} En otro caso
• 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑎𝑎𝑎𝑎: 𝑔𝑔𝑔𝑔. 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠)|𝑜𝑜𝑜𝑜𝑣𝑣𝑣𝑣(𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠) ∉ 𝑐𝑐𝑐𝑐𝑚𝑚𝑚𝑚}, caso general
280
Un catálogo de problemas
Vecino
Caso general:
𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑜𝑜𝑜𝑜𝑣𝑣𝑣𝑣 (𝑎𝑎𝑎𝑎, 𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 ), 𝑐𝑐𝑐𝑐𝑚𝑚𝑚𝑚 + 𝑜𝑜𝑜𝑜𝑣𝑣𝑣𝑣(𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠), 𝑎𝑎𝑎𝑎 ∈ 𝜑𝜑𝜑𝜑: 𝑛𝑛𝑛𝑛𝑎𝑎𝑎𝑎 + 1: 𝑛𝑛𝑛𝑛𝑎𝑎𝑎𝑎)
Peso de la arista
g.getEdgeWeight(a)
Solución Voraz
•
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Heurística:
• 0.
Solución:
• La solución más adecuada sería Lis<Integer> que indicarían el orden
de los vértices que forma el camino. En la solución habría que incluir
las propiedades derivadas adecuadas
Ruta de tren
Hay n estaciones en la ruta de un tren. El tren va desde la estación 0 hasta
la n-1. El coste del billete para el par de estaciones (i, j) es c(i, j). Además,
se cumple que c(i, k) > c(i, j) para todo k > j. Diseñe un algoritmo que
minimice el coste para llegar desde el origen al destino, indicando también
los billetes que habría que comprar. Los datos de los costes y los posibles
caminos vienen proporcionados en un grafo dirigido cuyos pesos de las
aristas dan los costes y sólo existen aristas de vértices i, j que cumplen j>i.
Es, por lo tanto, un grafo dirigido.
281
Un catálogo de problemas
Datos
• g: Graph<Integer,SimpleEdge<Integer>>
• c(i,j): coste del billete de i a j, dado por c(i,j) =
graph.getEdge(i,j).getEdgeWeight()
• n, número de estaciones, n = graph.vertexSet().size();
Un primer modelo
Variables:
• 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 con valores 1,0 indicando si coge la arista que va del vértice 𝑖𝑖𝑖𝑖 al
𝑗𝑗𝑗𝑗.
Modelo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
El modelo está inspirado en una red de flujo donde se obliga a que el flujo
por las aristas sea cero o uno y la fuente y sumidero crean y consumen uno
respectivamente. Hemos usado el predicado 𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) ≡ (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) →
𝑔𝑔𝑔𝑔. 𝑐𝑐𝑐𝑐𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) y los coste 𝑐𝑐𝑐𝑐 (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) desde el vértice i al j.
Segundo Modelo
Variables:
• 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 con valores enteros indicando el número de una de las
estaciones donde se para.
• 𝑟𝑟𝑟𝑟 es el número de ciudades del trayecto
282
Un catálogo de problemas
Modelo
𝑟𝑟𝑟𝑟−2
Elementos y contenedores
Se tiene un conjunto de elementos y un conjunto de contenedores. De cada
contenedor se conoce su tipo y su tamaño. De cada elemento se conocen
los tipos de contenedores en los que se puede ubicar, y su tamaño. Se
desea ocupar totalmente el mayor número de contenedores posible
haciendo uso de los elementos de los que se dispone.
Se quiere saber los elementos que ubican cada uno de los contenedores
seleccionados, es decir, en qué contenedor está ubicado cada elemento
seleccionado.
Modelo
De lo anterior consideramos necesarias variables binarias 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛),
𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) que indican si el elemento 𝑖𝑖𝑖𝑖 se ha ubicado en el contenedor 𝑗𝑗𝑗𝑗.
Consideremos también variables binarias 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) que indican si el
contenedor 𝑗𝑗𝑗𝑗 está totalmente ocupado. Sea 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛) el tamaño del
elemento 𝑖𝑖𝑖𝑖. Sea 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) el tamaño del contenedor 𝑗𝑗𝑗𝑗. Sea 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈
283
Un catálogo de problemas
[0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) una función booleana que indica si el elemento 𝑖𝑖𝑖𝑖 puede
ubicarse en el contenedor 𝑗𝑗𝑗𝑗. El modelo del problema sería:
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 𝑤𝑤𝑤𝑤𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑛𝑛𝑛𝑛−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 = 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑚𝑚𝑚𝑚−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 ≤ 1, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑏𝑏𝑏𝑏𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑦𝑦𝑦𝑦𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
Modelo alternativo
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Consideremos variables enteras 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛], que indican el contenedor
en el que se ha ubicado elemento 𝑖𝑖𝑖𝑖, de forma que si 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 = 𝑚𝑚𝑚𝑚, el i-ésimo
elemento no será ubicado en ningún contenedor. Sea 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛) el
tamaño del elemento 𝑖𝑖𝑖𝑖. Sea 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) el tamaño del contenedor 𝑗𝑗𝑗𝑗. Sea
𝑧𝑧𝑧𝑧(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗), 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) una función booleana que devuelve true si el
elemento 𝑖𝑖𝑖𝑖 no puede ubicarse en el contenedor 𝑗𝑗𝑗𝑗. Consideremos también
𝑛𝑛𝑛𝑛−1
funciones 𝜑𝜑𝜑𝜑(𝑗𝑗𝑗𝑗) ≡ 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 | 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 =𝑖𝑖𝑖𝑖 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 = 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚), que indican si el
contenedor 𝑗𝑗𝑗𝑗 está totalmente ocupado. El modelo del problema sería:
𝑚𝑚𝑚𝑚−1
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝐶𝐶𝐶𝐶𝑖𝑖𝑖𝑖=0|𝜑𝜑𝜑𝜑(𝑖𝑖𝑖𝑖) 1
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑚𝑚𝑚𝑚, 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛]
∀𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑧𝑧𝑧𝑧(𝑖𝑖𝑖𝑖, 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 )
𝑛𝑛𝑛𝑛−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 | 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 =𝑖𝑖𝑖𝑖 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 ≤ 𝑐𝑐𝑐𝑐𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚)
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛]
284
Un catálogo de problemas
VertexContenedores:
Propiedades:
• i: Integer
• cR: List<Integer>, la capacidad restante en cada contenedor
• cC: Set<Integer>, derivada, contenedores completos.
Interpretación:
Encontrar la asignación de los elementos desde i hasta el final en los
contenedores que quepan, puedan ublicarse y no estén llenos.
Igualdad
• Dos problemas son iguales si lo son i, capRest
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Es válido
• i>=0, i<=n, capRest[i]>=0
Factoría:
• inicial(): Crea el problema (0,[c0,c1,…]). Donde c0, etc., son la
capacidades de los contenedores
• goal(v) = v.i ==n
Caso base:
• v.i ==n
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: {0. . 𝑚𝑚𝑚𝑚}|𝑎𝑎𝑎𝑎 ∉ 𝑐𝑐𝑐𝑐𝐶𝐶𝐶𝐶, 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎), 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) ≤ 𝑐𝑐𝑐𝑐𝑅𝑅𝑅𝑅(𝑎𝑎𝑎𝑎)}
Donde 𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) es el tamaño del elemeno i y 𝑉𝑉𝑉𝑉𝑉𝑉𝑉𝑉(𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎) indica que el elemento i
se puede ubicar en el contenedor a.
285
Un catálogo de problemas
Vecino
Solución Voraz:
• Escoger aleatoriamente una alternativa de entre las posibles.
Repetir varias veces y quedarse con la mejor.
• Escoger el contenedor que queda más lleno tras incorporar el
elemento (o alternativamente el más vacío). Repetir varias veces
y quedarse con la mejor.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Productos y componentes
Una fábrica elabora distintos tipos de componentes. La unidad de cada
tipo de componente tarda un tiempo de producción más un tiempo
manual en elaborarse. Cada semana, el tiempo total de producción y el
tiempo total de acabado manual no deben exceder unos límites
establecidos (Tp y Te, respectivamente). Finalmente, estos componentes
se ensamblan para obtener ciertos productos acabados. Cada producto
está compuesto de varias unidades de cada componente, y se venden por
precios diferentes. Cada semana se pueden vender como máximo unas
286
Un catálogo de problemas
Modelo
De lo anterior consideramos necesarias variables enteras 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
que indican cuantas unidades del producto 𝑖𝑖𝑖𝑖 serán fabricadas. Sea 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈
[0, 𝑛𝑛𝑛𝑛) el precio de venta del producto 𝑖𝑖𝑖𝑖. Sea 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛) el máximo
número de unidades del producto 𝑖𝑖𝑖𝑖 que pueden venderse semanalmente.
Sea 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛), 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) el número de unidades del componente 𝑗𝑗𝑗𝑗
que requiere el producto 𝑖𝑖𝑖𝑖. Sea 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) el tiempo de producción del
componente 𝑗𝑗𝑗𝑗. Sea 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 , 𝑗𝑗𝑗𝑗 ∈ [0, 𝑚𝑚𝑚𝑚) el tiempo de elaboración manual del
𝑚𝑚𝑚𝑚−1 𝑚𝑚𝑚𝑚−1
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
componente 𝑗𝑗𝑗𝑗. Sea 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 y 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) = 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 𝑢𝑢𝑢𝑢𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖
𝑛𝑛𝑛𝑛−1
𝑚𝑚𝑚𝑚𝑎𝑎𝑎𝑎𝑎𝑎𝑎𝑎 𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖
𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
𝑛𝑛𝑛𝑛−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑇𝑇𝑇𝑇𝑉𝑉𝑉𝑉
𝑛𝑛𝑛𝑛−1
𝛴𝛴𝛴𝛴𝑖𝑖𝑖𝑖=0 𝑖𝑖𝑖𝑖𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖) 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 ≤ 𝑇𝑇𝑇𝑇𝑠𝑠𝑠𝑠
𝑖𝑖𝑖𝑖𝑛𝑛𝑛𝑛𝑖𝑖𝑖𝑖 𝑥𝑥𝑥𝑥𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖 ∈ [0, 𝑛𝑛𝑛𝑛)
VertexProductos:
Propiedades:
• i: Integer
• tpR: List<Integer>, tiempo de producción restante
• teR: Set<Integer>, tiempo de elaboración restante
287
Un catálogo de problemas
Interpretación:
Encontrar el número de unidades a producir de lo s productos desde i
hasta el final teniendoo en cuenta que los tiempos restantes de producción
y elaboración son tpR y teR.
Igualdad
• Dos problemas son iguales si lo son i, tpR,TeR
Es válido
• i>=0, i<=n, tpR>=0, teR>=0
Factoría:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Caso base:
• v.i ==n
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑎𝑎𝑎𝑎: {0. . 𝑚𝑚𝑚𝑚𝑖𝑖𝑖𝑖 }|𝑎𝑎𝑎𝑎 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) ≤ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖) ≤ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖}
Vecino
Caso general: 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜 (𝑎𝑎𝑎𝑎) = (𝑖𝑖𝑖𝑖 + 1, 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 − 𝑎𝑎𝑎𝑎 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖), 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖 − 𝑎𝑎𝑎𝑎 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖(𝑖𝑖𝑖𝑖))
288
Un catálogo de problemas
Solución Voraz:
Escogemos como acción voraz la mayor cantidad de unidades del
producto i que pueden ser fabricadas y elaboradas en el tiempo restante
de fabricación y elaboración.
Ordenamos los productos según el beneficio esperado al comienzo. El
beneficio esperado es la cantidad máxima de unidades anterior
multiplicada por su precio.
Algoritmo de Floyd
Veamos el algoritmo de Floyd para calcular el camino mínimo en un grafo.
Es un algoritmo de Programación Dinámica General donde tras tomar una
acción nos encontramos con dos subproblemas y por lo tanto nos aparece
un hipergrafo en lugar de un grafo.
Asumimos que cada vértice del grafo está indexado mediante un entero
comprendido en [0, 𝑛𝑛𝑛𝑛 − 1] dónde 𝑛𝑛𝑛𝑛 es el número de vértices. Esto siempre
lo podemos conseguir mediante una vista de un grafo cuyos vértices sean
enteros y la arista del tipo SimpleEdge<Integer> por ejemplo. Queremos
encontrar el Camino Mínimo entre dos ciudades dadas c1, c2. Este
problema se puede resolver de muchas otras formas como hemos visto.
Aquí vamos a resolver el problema para que sirva de ejemplo de uso del
algoritmo de Programación Dinámica. Para ello lo generalizamos a este
otro: encontrar el Camino Mínimo de 𝑖𝑖𝑖𝑖 a 𝑗𝑗𝑗𝑗 usando como camino intermedio
ciudades cuyos índices estén en el conjunto [0, 𝑘𝑘𝑘𝑘). Con este planteamiento
cada problema lo podemos representar por (𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘, 𝑔𝑔𝑔𝑔). Donde 𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗 toman
valores en [0, 𝑛𝑛𝑛𝑛 − 1] y 𝑘𝑘𝑘𝑘 en [0, 𝑛𝑛𝑛𝑛]. El valor de 𝑘𝑘𝑘𝑘 = 𝑛𝑛𝑛𝑛 indica que el camino
intermedio no contiene ninguna ciudad.
Un camino en el grafo anterior lo vamos a representar por una secuencia
de ciudades conectadas cada una a la siguiente mediante una arista. Cada
289
Un catálogo de problemas
camino tiene una longitud que es la suma del valor de sus aristas. La no
existencia de camino lo representaremos por ⊥.
Representamos por 𝑔𝑔𝑔𝑔 el grafo de partida y por 𝑠𝑠𝑠𝑠(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) la arista entre 𝑖𝑖𝑖𝑖 y 𝑗𝑗𝑗𝑗,
si la hay, y por 𝑤𝑤𝑤𝑤(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗) su peso. La primera decisión es escoger el tipo de
alternativas. Para cada problema tenemos dos alternativas {𝑌𝑌𝑌𝑌, 𝑖𝑖𝑖𝑖}. La
primera alternativa representa pasar por la ciudad 𝑘𝑘𝑘𝑘. La segunda no pasar.
Veamos los detalles del problema:
FloydVertex:
Propiedades:
• i: Integer
• j: Integer
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
• k: Integer
• g: Graph<Integer,SimpleEdge<Integer>, compartida
• v1: Integer, compartida, ciudad inicial
• v2: Integer, compartida, ciudad final
• t: Integer, derivada, t = n-k
• n: Integer, compartida, número de vértices del grafo
Interpretación:
Encontrar en el grafo g el Camino Mínimo de 𝑖𝑖𝑖𝑖 a 𝑗𝑗𝑗𝑗 usando en el camino
intermedio ciudades cuyos índices estén en el conjunto [𝑘𝑘𝑘𝑘, 𝑛𝑛𝑛𝑛).
Igualdad
• Dos problemas son iguales si lo son i, j, k
Es válido
• i>=0, i<n, j>=0, j<n, k>=0, k<=n
Factoría:
• inicial(): Crea el problema (v1,v2,0)
290
Un catálogo de problemas
Casos base:
1. (i,j,n)
2. (i,j,k) y e(i,j) pertenece al grafo
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = {𝑌𝑌𝑌𝑌, 𝑖𝑖𝑖𝑖}
Vecino
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Caso general:
o 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑖𝑖𝑖𝑖) = [(𝑖𝑖𝑖𝑖, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘 + 1)]
o 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑌𝑌𝑌𝑌) = [(𝑖𝑖𝑖𝑖, 𝑘𝑘𝑘𝑘, 𝑘𝑘𝑘𝑘 + 1), (𝑘𝑘𝑘𝑘, 𝑗𝑗𝑗𝑗, 𝑘𝑘𝑘𝑘 + 1)]
291
Un catálogo de problemas
Programación Dinámica.
La solución que vamos buscando es un String con los paréntesis puestos
adecuadamente para multiplicar las matrices de la lista.
El problema generalizado será:
Matriz:
• f: Integer, numero de filas
• c: Integer, numero de columnas
MatrizVertex:
Propiedades:
• i: Integer
• j: Integer
• lm: List<Matriz>, compartida
• t: Integer, derivada, t = j-i
• n: Integer, compartida, n= número de matrices en lm
292
Un catálogo de problemas
Interpretación:
Encontrar una forma de agrupar las matrices en ls[i,j] que minimice el
número de multiplicaciones necesarias para conseguir el producto de
todas las matrices en ls[i,j].
Igualdad
• Dos problemas son iguales si lo son i, j
Es válido
• i>=0, i<n, j>i, j<n
Factoría:
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Casos base:
1. n-i =0
2. n-i = 1
3. n-i=2
Acciones:
𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖 = 𝑖𝑖𝑖𝑖 + 1. . 𝑗𝑗𝑗𝑗 − 1
Vecino
Caso general:
o 𝑛𝑛𝑛𝑛𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠ℎ𝑏𝑏𝑏𝑏𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜𝑜(𝑎𝑎𝑎𝑎) = [(𝑖𝑖𝑖𝑖, 𝑎𝑎𝑎𝑎), (𝑎𝑎𝑎𝑎, 𝑗𝑗𝑗𝑗)]
293
Un catálogo de problemas
Peso del árbol: Las soluciones de este problema son árboles. Cada árbol
tiene asociado un vértice del hipergrafo virtual, un peso, una acción y
unos vecinos siguiendo esa acción. En este caso será un caso base o tendrá
dos hijos. El peso de un árbol 𝑖𝑖𝑖𝑖 con hijos 𝑖𝑖𝑖𝑖0 , 𝑖𝑖𝑖𝑖1 es
𝑖𝑖𝑖𝑖. 𝑛𝑛𝑛𝑛 = 𝑖𝑖𝑖𝑖0 . 𝑛𝑛𝑛𝑛 + 𝑖𝑖𝑖𝑖1 . 𝑛𝑛𝑛𝑛 + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖0 . 𝑖𝑖𝑖𝑖 ]. 𝑓𝑓𝑓𝑓 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖0 . 𝑖𝑖𝑖𝑖 ]. 𝑓𝑓𝑓𝑓 ∗ 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖[𝑖𝑖𝑖𝑖1 . 𝑗𝑗𝑗𝑗]. 𝑐𝑐𝑐𝑐
Solución: String
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
294
Notación y catálogo de restricciones de uso
general
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
P
ara uso en todo el libro usaremos una notación compacta que tiene
una equivalencia directa en Java. Esta notación cuenta con tipos de
datos conocidos y operadores sobre los mismos.
Tipos de datos
Los tipos de datos que usaremos serán listas, conjuntos, multiconjuntos,
diccionarios y bidiccionarios.
Las listas son agregados de elementos que pueden repetirse y que están
indexados por un entero en un rango. El orden es importante en las listas.
Las representaremos como 𝑟𝑟𝑟𝑟 = [𝑠𝑠𝑠𝑠0 , … , 𝑠𝑠𝑠𝑠𝑛𝑛𝑛𝑛−1 ]. El tipo que representa las
listas en Java es List<T>.
Los conjuntos son agregados de elementos que no se pueden repetir, no
están indexados y donde no importa el orden. Los representamos como
𝑠𝑠𝑠𝑠 = {𝑠𝑠𝑠𝑠0 , … , 𝑠𝑠𝑠𝑠𝑛𝑛𝑛𝑛−1 }. El tipo que representa los conjuntos en Java es Set<T>.
Los multiconjuntos son agregados de elementos que se pueden repetir, no
están indexados y donde no importa el orden. Los representamos como
𝑚𝑚𝑚𝑚𝑠𝑠𝑠𝑠 = {𝑠𝑠𝑠𝑠0 : 𝑓𝑓𝑓𝑓0 , … , 𝑠𝑠𝑠𝑠:𝑛𝑛𝑛𝑛−𝑖𝑖𝑖𝑖 : 𝑓𝑓𝑓𝑓𝑛𝑛𝑛𝑛−1 }. Donde las 𝑓𝑓𝑓𝑓𝑖𝑖𝑖𝑖 son enteros que indican el
número de veces que se repite el elemento 𝑠𝑠𝑠𝑠𝑖𝑖𝑖𝑖 El tipo que representa los
295
Notación y catálogo de restricciones de uso general
valor que modelan una función biyectiva entre el conjunto de las claves,
los primeros elementos de los pares, y el de los valores, los segundos
elementos. Los representamos como 𝑖𝑖𝑖𝑖 = {𝑘𝑘𝑘𝑘0 : 𝑣𝑣𝑣𝑣0 , … , 𝑘𝑘𝑘𝑘:𝑛𝑛𝑛𝑛−𝑖𝑖𝑖𝑖 : 𝑣𝑣𝑣𝑣𝑛𝑛𝑛𝑛−1 }. Donde
las 𝑘𝑘𝑘𝑘𝑖𝑖𝑖𝑖 son las claves y los 𝑣𝑣𝑣𝑣𝑖𝑖𝑖𝑖 los valores asociados. En este tipo las claves y
los valores no pueden estar repetidos. El tipo que representa los
diccionarios en Java es BiMap<K,V>. Este tipo tiene el método inverse() que
devuelve un BiMap<V,K> que es el inverso del anterior asMap() que nos
devuelve un objeto de tipo Map<K,V>.
Los flujos de datos los representaremos según los casos por los tipos de
Java Iterable<E> o el tipo Stream<E>. Usaremos uno u otro según la
conveniencia en cada caso, pero preferiremos el uso del segundo frente al
primero.
Los grafos de distintos tipos los representaremos por el tipo Graph<V,E>
de la librería JGraphT.
Los caminos en los grafos los se representan por el tipo GraphPath<V,E>
también de JGraphT.
Junto con los tipos usaremos una notación para designar de forma
compacta las propiedades y operaciones más usuales.
• |𝑟𝑟𝑟𝑟|: Tamaño del conjunto, lista, número de claves de un diccionario
o número de elementos distintos en un multiconjunto.
296
Notación y catálogo de restricciones de uso general
correspondiente.
• 𝑟𝑟𝑟𝑟[𝑘𝑘𝑘𝑘], 𝑟𝑟𝑟𝑟𝑘𝑘𝑘𝑘 : Elemento de índice k en una lista si k es un entero, el valor
asociado a la clave k en un diccionario o el número de apariciones
de k en un multiconjunto.
• 𝑟𝑟𝑟𝑟[𝑖𝑖𝑖𝑖: 𝑗𝑗𝑗𝑗]: Sublista desde la casilla i a la j sin incluir la j.
• 𝑟𝑟𝑟𝑟[: 𝑖𝑖𝑖𝑖]: Sublista de 0 a i sin incluir la i.
• 𝑟𝑟𝑟𝑟[𝑖𝑖𝑖𝑖: ]: Sublista desde i hasta el final.
• 𝑟𝑟𝑟𝑟1 + 𝑟𝑟𝑟𝑟2: Concatenar listas, unir dos conjuntos, multiconjuntos o
dos diccionarios teniendo en cuenta, en este último caso, si hay
claves comunes en r1 y r2 los valores asociados a la clave común
serán los de r2.
• 𝑟𝑟𝑟𝑟1 − 𝑟𝑟𝑟𝑟2: Eliminar de r1 los elementos en r2. Si son conjuntos es la
diferencia de conjuntos. Si son diccionarios el diccionario cuyas
claves son la diferencia de las claves. Si son multiconjuntos el
formado por los elementos de r1 restándole las de r2 con un
mínimo de cero. Si son listas se elimina de la primera todas las
ocurrencias de los elementos de la segunda.
• 𝑟𝑟𝑟𝑟1 ⨁ 𝑟𝑟𝑟𝑟2: Concatenar dos listas tales que el último elemento de la
primera coincide con el primero de la segunda y manteniendo solo
una vez el elemento repetido.
• Los tipos de las funciones los expresaremos de forma compacta
indicando los tipos de los parámetros y el resultado. Así 𝐸𝐸𝐸𝐸 × 𝑇𝑇𝑇𝑇 →
297
Notación y catálogo de restricciones de uso general
𝑅𝑅𝑅𝑅 es el tipo de una función, o una lambda expresión que toma dos
parámetros de tipos 𝐸𝐸𝐸𝐸, 𝑇𝑇𝑇𝑇 y devuelve un resultado de tipo R.
Operadores
Prácticamente todo el código en java con streams puede compactarse en
operadores con la forma:
𝑂𝑂𝑂𝑂𝑡𝑡𝑡𝑡:𝑑𝑑𝑑𝑑|𝑝𝑝𝑝𝑝 𝑓𝑓𝑓𝑓(𝑠𝑠𝑠𝑠)
operador 𝑂𝑂𝑂𝑂.
En la mayoría de los casos cuando el dominio es un rango simple de
enteros se explicita:
𝑛𝑛𝑛𝑛
𝑂𝑂𝑂𝑂𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖)
𝑛𝑛𝑛𝑛−1
Sum: Σ𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0,n).boxed().filter(p).mapToInt(f).sum();
IntStream.range(0,n).boxed().map(f).max(cmp).get();
IntStream.range(0,n).boxed().map (f).min(cmp).get();
298
Notación y catálogo de restricciones de uso general
IntStream.range(0,n).boxed()
.max(Comparator.comparing(f).get();
IntStream.range(0,n).boxed()
.min(Comparator.comparing(f).get();
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝐔𝐔𝐔𝐔𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧ó𝐧𝐧𝐧𝐧: ∪𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0, n).boxed().filter(pd)
.map(f).reduce((s1,s2)->Set2.union(s1,s2)).get();
𝐈𝐈𝐈𝐈𝐧𝐧𝐧𝐧𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐀𝐀𝐀𝐀𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐧𝐧𝐧𝐧ó𝐧𝐧𝐧𝐧: ∩𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0, n).boxed().filter(pd)
.map(f)
.reduce((s1,s2)->Set2.intersection(s1,s2)).get();
𝐓𝐓𝐓𝐓𝐧𝐧𝐧𝐧𝐓𝐓𝐓𝐓𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧: 𝐿𝐿𝐿𝐿𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0,n).boxed().filter(p)
map(f).toList();
𝑛𝑛𝑛𝑛−1
𝐓𝐓𝐓𝐓𝐧𝐧𝐧𝐧𝐓𝐓𝐓𝐓𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈: 𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0,n).boxed().filter(p)
map(f).collect(Collectors.toSet());
𝑛𝑛𝑛𝑛−1
𝐓𝐓𝐓𝐓𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈: 𝑀𝑀𝑀𝑀𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
Multiset.of(IntStream.range(0,n).boxed().filter(p).
map(f).collect(Collectors.toList()));
299
Notación y catálogo de restricciones de uso general
𝑛𝑛𝑛𝑛−1
Grouping: 𝐺𝐺𝐺𝐺𝑖𝑖𝑖𝑖=0|𝑘𝑘𝑘𝑘𝑡𝑡𝑡𝑡𝑘𝑘𝑘𝑘 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Forma grupos a partir de 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) con la misma clave
key(f(i)). Si los tipos de las funciones f, key son respectivamente 𝐼𝐼𝐼𝐼𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 →
𝐸𝐸𝐸𝐸 y 𝐸𝐸𝐸𝐸 → 𝐾𝐾𝐾𝐾 el resultado es de tipo Map<K,List<E>>
IntStream.range(0,n).boxed().map(f)
.collect(Collectors.groupingBy(key));
𝑛𝑛𝑛𝑛−1
Grouping y transformación: 𝐺𝐺𝐺𝐺𝑇𝑇𝑇𝑇𝑖𝑖𝑖𝑖=0|𝑘𝑘𝑘𝑘𝑡𝑡𝑡𝑡𝑘𝑘𝑘𝑘,𝑡𝑡𝑡𝑡 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Forma grupos a partir de
𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) con la misma clave key(f(i)) y los transforma con la función t. Si los
tipos de las funciones f, key,t son respectivamente 𝐼𝐼𝐼𝐼𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 → 𝐸𝐸𝐸𝐸, 𝐸𝐸𝐸𝐸 → 𝐾𝐾𝐾𝐾 y
𝐸𝐸𝐸𝐸 → 𝑇𝑇𝑇𝑇 el resultado es de tipo Map<K,List<T>>.
IntStream.range(0, n).boxed().map(f)
.collect(Collectors.groupingBy(key,
Collectors.mappping(f,Collectors.toList()));));
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1
Grouping, transformación y reducción: 𝐺𝐺𝐺𝐺𝑅𝑅𝑅𝑅𝑖𝑖𝑖𝑖=0|𝑘𝑘𝑘𝑘𝑡𝑡𝑡𝑡𝑘𝑘𝑘𝑘,𝑡𝑡𝑡𝑡,𝑏𝑏𝑏𝑏𝑏𝑏𝑏𝑏 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Forma
grupos a partir de 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) con la misma clave key(f(i)). Transforma cada
grupo e del grupo con la función t(e) y finalmente agrupa los elementos de
cada grupo según el operador binario bo. Si los tipos de las funciones f,
key,t,bo son respectivamente 𝐼𝐼𝐼𝐼𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛𝑛 → 𝐸𝐸𝐸𝐸, 𝐸𝐸𝐸𝐸 → 𝐾𝐾𝐾𝐾, 𝐸𝐸𝐸𝐸 → 𝑅𝑅𝑅𝑅 y 𝑅𝑅𝑅𝑅 × 𝑅𝑅𝑅𝑅 → 𝑅𝑅𝑅𝑅 el
resultado es de tipo Map<K,R>.
IntStream.range(0, n).boxed().map(f)
.collect(Collectors.groupingBy(key,
Collectors.collectingAndThen(
Collectors.mapping(t,
Collectors.reducing(bo)),
r -> r.get())));
𝑛𝑛𝑛𝑛−1
𝐂𝐂𝐂𝐂𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐂𝐂𝐂𝐂𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧: 𝐶𝐶𝐶𝐶𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Cuenta el número de elementos que cumplen el
predicado
IntStream.range(0,n).boxed().filter(i->p(i)).count();
𝑛𝑛𝑛𝑛−1
𝐂𝐂𝐂𝐂𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐂𝐂𝐂𝐂𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧 𝐂𝐂𝐂𝐂𝐂𝐂𝐂𝐂 𝐂𝐂𝐂𝐂𝐂𝐂𝐂𝐂𝐝𝐝𝐝𝐝𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈: 𝐶𝐶𝐶𝐶𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖):
IntStream.range(0,n).boxed().filter(i->p(i))
.map(i->f(i)).distinct().count();
300
Notación y catálogo de restricciones de uso general
Restricciones
Las restricciones son funciones que devuelven un valor de tipo lógico tras
operar sus resultados.
Las restricciones se pueden combinar para formar restricciones más
complejas con los operadores lógicos ∧, ∨, ! (and, or y not
respectivamente), y los operadores: ⇒, ⇔ (de implicación y doble
implicación respectivamente. En muchos casos una simple coma
sustituirá al operador ∧.
Con el símbolo ≡ indicaremos que dos restricciones son equivalentes.
La restricciones relacionales más conocidas son >, ≥, =, <, ≤ que
denominaremos, respectivamente GT,GE,EQ,LT,LE: mayor que, mayor o
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
igual, igual, menor que, menor o igual. Incluimos aquí también las de
inclusión de conjuntos y multiconjuntos: ⊆, ⊂, ⊃, ⊇ y los de pertenencia ∈.
Un multiconjuntos está incluido en otro cuando el número de repeticiones
de cada elemento en el primero es menor o igual que en el segundo.
Otras restricciones muy usadas son las siguientes:
AllMatch: ∀𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): La restricción obliga a que todos los 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son
verdaderos.
IntStream.range(0,n).boxed().map(f).allMatch(f);
𝐀𝐀𝐀𝐀𝐧𝐧𝐧𝐧𝐀𝐀𝐀𝐀𝐌𝐌𝐌𝐌𝐀𝐀𝐀𝐀𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐀𝐀𝐀𝐀: ∃𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): La restricción obliga a que alguno de los 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) es
verdadero.
IntStream.range(0,n).boxed().map(f).anyMatch(f)
𝐍𝐍𝐍𝐍𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧: ∄𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): La restricción obliga a que ninguno de los 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) es
verdadero.
IntStream.range(0,n).boxed().map(f).noneMatch(f);
𝑛𝑛𝑛𝑛−1
𝐕𝐕𝐕𝐕𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀: 𝐼𝐼𝐼𝐼𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥, 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖)): Es una restricción que obliga a que la variable x
tome uno de los valores de la lista L𝑛𝑛𝑛𝑛−1 𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖).
𝑛𝑛𝑛𝑛−1
𝐼𝐼𝐼𝐼𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥, 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖)) ≡ ∃𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 (𝑥𝑥𝑥𝑥 = 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖))
301
Notación y catálogo de restricciones de uso general
𝐀𝐀𝐀𝐀𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐀𝐀𝐀𝐀𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐧𝐀𝐀𝐀𝐀𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈: AD𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Es una restricción que toma una lista de
valores enteros filtrada por un predicado y los obliga a que sean distintos.
𝑛𝑛𝑛𝑛−1
𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ≡ |𝐿𝐿𝐿𝐿𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖)| = |𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0|𝑝𝑝𝑝𝑝(𝑖𝑖𝑖𝑖) 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖)|
𝑛𝑛𝑛𝑛−1,𝑚𝑚𝑚𝑚−1
𝐏𝐏𝐏𝐏𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈𝐈ó𝐧𝐧𝐧𝐧: 𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0,𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖 ), 𝑔𝑔𝑔𝑔(𝑗𝑗𝑗𝑗)). Es una restricción que obliga a la
lista de 𝐿𝐿𝐿𝐿𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓 (𝑖𝑖𝑖𝑖 ), de n elementos, que sea una permutación de un prefijo
de la lista 𝐿𝐿𝐿𝐿𝑚𝑚𝑚𝑚−1
𝑖𝑖𝑖𝑖=0 𝑣𝑣𝑣𝑣 (𝑖𝑖𝑖𝑖 ), de m elementos, con n <=m.
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
𝑛𝑛𝑛𝑛−1 ( ) 𝑛𝑛𝑛𝑛−1
𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓 𝑖𝑖𝑖𝑖 , 𝑖𝑖𝑖𝑖) ≡ 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), ∀𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 0 ≤ 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) < 𝑛𝑛𝑛𝑛
𝑛𝑛𝑛𝑛−1
Open Path: 𝑂𝑂𝑂𝑂𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖): Los valores de 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son los vértices de un grafo g
y forman un camino abierto en el mismo. Esta restricción puede ser
expresada en función de las anteriores en la forma:
𝑛𝑛𝑛𝑛−1
𝑂𝑂𝑂𝑂𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ≡ AD𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−2
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), ∀𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ∈ 𝑔𝑔𝑔𝑔𝑣𝑣𝑣𝑣 , ∀𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), f(i + 1)) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
Si podemos afirmar con otras restricciones que los valores 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son
vértices del grafo entonces podemos simplificar lo anterior a:
𝑛𝑛𝑛𝑛−1
𝑂𝑂𝑂𝑂𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ≡ AD𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−2
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), ∀𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), f(i + 1)) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
302
Notación y catálogo de restricciones de uso general
𝑛𝑛𝑛𝑛−1
𝐂𝐂𝐂𝐂𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐓𝐂𝐂𝐂𝐂 𝐏𝐏𝐏𝐏𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀𝐀: 𝐶𝐶𝐶𝐶𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖). Los valores de f(i) son los vértices de un grafo
g y forman un camino cerrado en el mismo. Esta restricción puede ser
expresada en función de las anteriores en la forma:
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝐶𝐶𝐶𝐶𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ≡ 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), ∀𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ∈ 𝑔𝑔𝑔𝑔𝑣𝑣𝑣𝑣 , ∀𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), 𝑓𝑓𝑓𝑓((𝑖𝑖𝑖𝑖 + 1)%𝑛𝑛𝑛𝑛)) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
Si podemos afirmar con otras restricciones que los valores 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son
vértices del grafo entonces podemos simplificar lo anterior a:
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝐶𝐶𝐶𝐶𝑃𝑃𝑃𝑃𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) ≡ 𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝐴𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), ∀𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), 𝑓𝑓𝑓𝑓((𝑖𝑖𝑖𝑖 + 1)%𝑛𝑛𝑛𝑛)) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
Distancias a restricciones
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
303
Notación y catálogo de restricciones de uso general
304
Notación y catálogo de restricciones de uso general
Distancia a Permutación: dP
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
Si podemos afirmar con otras restricciones que los valores 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son
vértices del grafo entonces podemos simplificar lo anterior a:
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−2
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖 + 1)) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
Si podemos afirmar con otras restricciones que los valores 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) son
vértices del grafo entonces podemos simplificar lo anterior a:
𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0|𝑔𝑔𝑔𝑔 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖 ) =
𝑛𝑛𝑛𝑛−1 𝑛𝑛𝑛𝑛−1
𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖) + 𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖𝑖=0 (𝑓𝑓𝑓𝑓(𝑖𝑖𝑖𝑖), 𝑓𝑓𝑓𝑓((𝑖𝑖𝑖𝑖 + 1)%𝑛𝑛𝑛𝑛) ∈ 𝑔𝑔𝑔𝑔𝑡𝑡𝑡𝑡
305
Bibliografía
P
Problemas, modelos, grafos y algoritmos // Miguel Toro Bonilla
306
Bibliografía
307
Miguel Toro es doctor en Ingeniero Industrial por la Universidad de
Sevilla y catedrático del Departamento de Lenguajes y Sistemas In-
formáticos de la misma universidad.
Ha sido director de la Oficina de Transferencia de Resultados de
la Investigación (OTRI) y Director General de Investigación, Tecno-
logía y Empresa de la Junta de Andalucía.
Ha tenido un papel activo en la Agencia Andaluza de Evaluación
de la Calidad y Acreditación Universitaria (AGAE), ha sido miembro
del Consejo Asesor de la Agencia Nacional de Evaluación de la Ca-
lidad y Acreditación (ANECA) y colabora asiduamente con varias
agencias nacionales de evaluación universitaria.
Ha sido el Presidente de Sistedes (Sociedad Nacional de Ingenie-
ría del Software y Tecnologías de Desarrollo de Software) y el Pre-
sidente de la Sociedad Científica Informática de España (SCIE) que
engloba a los informáticos de las universidades españolas.
Ha recibido el Premio Fama de la Universidad de Sevilla, el Pre-
mio Sistedes de la Sociedad Nacional de Ingeniería del Software y
Tecnologías de Desarrollo de Software en reconocimiento a su labor
de promoción y consolidación de la Informática en España.
Ha recibido el Premio Nacional de Informática José García San-
tesmases a la trayectoria profesional otorgado por la Sociedad Cien-
tífica Informática de España.
Actualmente es director del Instituto de Ingeniería Informática
de la Universidad de Sevilla
Este libro está especialmente orientado a la enseñanza de la asigna-
tura Análisis y Diseño de Datos y Algoritmos, que es la continuación
natural de Fundamentos de Programación. Este volumen de Proble-
mas, modelos, grafos y algoritmos sigue al de Análisis y diseño de al-
goritmos y tipos de datos, también publicado en esta colección. El
lector interesado puede consultar, además, Fundamentos de progra-
mación: Python y Fundamentos de programación: Java. Para abordar
el diseño de algoritmos, es necesario tener asimilados los elementos
de la programación en algún lenguaje. El dominio de Java y sus pe-
culiaridades se hace imprescindible para acometer el seguimiento
del contenido. Tras las técnicas revisadas en el volumen anterior,
Análisis y Diseño de Algoritmos y Tipos de Datos, sobre diseño de al-
goritmos iterativos y recursivos, abordamos ahora un conjunto de
técnicas algorítmicas de uso general: Programación Lineal Entera,
Algoritmos Genéticos, Algoritmos A*, Backtracking, Programación
Dinámica y otras. Al final del manual, se incluye una serie de ejem-
plos, muchos de ellos resueltos a partir del uso de las técnicas des-
critas. Este texto es el resultado de la experiencia de varios años de
enseñanza de la asignatura Análisis y Diseño de Algoritmos en la
Universidad de Sevilla.