Desde hace varios años mi esposa y yo habíamos querido organizar unas vacaciones familiares. Estoy hablando de vacaciones familiares en grande! Logramos reservar un espacio con la capacidad suficiente y una ubicación central para los diferentes planes en la region. Ahora el lío era cuadrar dónde iba a dormir cada uno.
Las personas confirmadas fueron:
listado <-read.csv("./listado.csv")listado
persona edad grupo
1 Nelsy 75 1
2 Hernando 74 2
3 Ofelia 63 3
4 Liliana 63 4
5 John Jairo 61 4
6 Jimmy 45 5
7 Luis 42 6
8 Linda 41 6
9 Mercedes 40 5
10 Sandra 35 3
11 Estefania 33 7
12 Juan 31 7
13 Cristian 32 8
14 Claudia 31 8
15 Daniela 30 4
16 Felipe 17 6
17 Nicolas 11 5
18 Emiliano 8 7
19 Gabriel 4 5
20 Emma 2 7
En el lugar habían 3 tipos de cama: sencilla, doble y camarote. Las camas estaban distribuidas en 8 dormitorios. Solo 3 dormitorios contaban con baño privado.
Aquí realmente en la sala habia un sofa cama, pero sirve como un espacio para dormir una persona. Los camarotes, cada uno cuenta con 2 espacios para dormir.
Nuestro reto: acomodar
Como reglas básicas pensamos:
Los grupos familiares no se deben mezclar entre sí.
Los matrimonios necesitan dormir en el mismo dormitorio.
Los niños menores, en caso de ser necesario, pueden mezclarse.
Por condiciones de salud, Ofelia y John Jairo deben dormir en un dormitorio con baño.
Modelo de asignación
La idea es introducir una variable binaria \(x_{i,j}\) que sea \(1\) si una persona \(i\) es asignada al dormitorio \(j\). Como un objetivo (y puede ser el caso ajustarse a cualquier otro) podemos tratar de garantizar las reglas otorgando un peso. Aqui claramente la capacidad del lugar es suficiente para la cantidad de personas en la familia.
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Vamos a establecer, como administradores, las reglas basicas en cuanto a preferencias. A cada uno de los integrantes vamos a definirle 3 dormitorios que cumplan con nuestras reglas.
# Definir el orden de las preferencias por personadatos_preferencias <-setNames(list(c(2,3,4), # Nelsy no tiene problemac(2,3,4), # Hernando no tiene problemac(4,6,8), # Ofelia requiere un bañoc(4,8,6), # Liliana va con su esposo en dormitorio con bañoc(4,6,8), # John requiere un bañoc(3,7,8), # Jimmy quiere dormir con toda su familiac(2,3,7), # Luis quiere dormir con su padre e hijo tambiénc(2,3,7), # Linda quiere dormir con su esposoc(3,7,8), # Mercedes quiere dormir con toda su familiac(4,6,8), # Sandra quiere dormir con la madrec(3,7,8), # Estefania quiere dormir con toda su familiac(3,7,8), # Juan quiere dormir con toda su familiac(2,4,6), # Cristian quiere dormir con su esposac(2,4,6), # Claudia quiere dormir con su esposoc(4,8,5), # Daniela quiere dormir sola o con sus padresc(4,8,5), # Felipe quiere dormir solo o con sus padresc(5,7,8), # Nicolas no quiere dormir solo o con sus padresc(5,7,8), # Emiliano no quiere dormir solo o con sus padresc(3,7,8), # Gabriel quiere dormir con sus padresc(3,7,8) # Emma quiere dormir con sus padres ),listado$personas)# Definir funcion para extraer vector de preferencia por personabuscar_preferencia <-function(persona) datos_preferencias[[persona]]# Entregar prefencia con el par persona-dormitorio(hab)preferencias <-function(persona, hab) { p <-which(as.numeric(hab) ==buscar_preferencia(as.numeric(persona)))as.integer(if (length(p) ==0) {-100000 } else { p })}
Podríamos haber definido 4 o 5 opciones, pero el punto es entender que, para éste ejemplo, lo que hemos hecho es establecer nosotros, administradores, las prioridades. Podríamos haber hecho una encuesta, enviarle un link a cada participante, para recolectar los datos y de esa forma obtener otros valores. Seguramente la solución del modelo no apuntaría a las reglas básicas que establecimos con mi esposa, y eso esta bien, es una solución mas, otra forma de resolver el problema de asignación.
personas <-length(listado$persona)hab <-length(dormitorios$dormitorio)modelo <-MIPModel() %>%# 1 si la persona i se asigna al dormitorio jadd_variable(x[i,j], i =1:personas, j =1:hab, type ="binary") %>%# maximixar las preferenciasset_objective(sum_over(preferencias(i,j) * x[i,j], i =1:personas, j =1:hab), sense =c("max")) %>%# no podemos exceder la capacidad de algun dormitorioadd_constraint(sum_over(x[i,j], i =1:personas) <= dormitorios$capacidad[j], j =1:hab) %>%# cada persona debe asignarse a un dormitorioadd_constraint(sum_over(x[i,j], j =1:hab) ==1, i =1:personas) %>%# los matrimonios no pueden dormir separadosadd_constraint(x[4,j] == x[5,j], j =1:hab) %>%add_constraint(x[6,j] == x[9,j], j =1:hab) %>%add_constraint(x[7,j] == x[8,j], j =1:hab) %>%add_constraint(x[11,j] == x[12,j], j =1:hab) %>%add_constraint(x[13,j] == x[14,j], j =1:hab) modelo
Mixed integer linear optimization problem
Variables:
Continuous: 0
Integer: 0
Binary: 180
Model sense: maximize
Constraints: 74
Las asignaciones de dormitorios para las personas son diferentes para cada modelo, aún cuando ambos arrojaron la solución óptima.
resultado_glpk
Status: success
Objective value: 49
resultado_symphony
Status: success
Objective value: 49
Como esas dos soluciones óptimas, podrian existir muchas mas, después de todo éste es un problema de optimización combinatorio.
Conclusiones
Voy a ir con las dos soluciones a planteárselas a mi familia para nuestras vacaciones. Seguramente el plan no va a ejecutarse al pie de la letra, siguiendo los resultados de la solución obtenida, pero será un punto de partida interesante para organizar la solución definitiva, pues tenemos un problema con 180 variables binarias para jugar.
Además, tenemos en ambas soluciones, tenemos dos dormitorios sin usar. esto sucede porque:
Dentro de las preferencias establecidas no se eligen todos los dormitorios, dejamos de listar el dormitorio # 1.
Las restricciones declaradas en el código son suficientes para resolver la maximización de esas preferencias, entonces pueden quedar dormitorios libres.
Podría ser que la función objetivo establecida no se realmente útil para nuestro problema. Sin embargo, existen formas de equilibrar y contrastar problemas de optimización lineal con varios objetivos al tiempo.
Cómo citar
BibTeX
@online{chiquito_valencia2024,
author = {Chiquito Valencia, Cristian},
title = {El Secreto Para Organizar a Tu Familia},
date = {2024-11-26},
url = {https://cchiquitovalencia.github.io/posts/2024-11-26-organizar_a_tu_familia/},
langid = {en}
}