Estoy enfrentando un problema que no estoy seguro de cómo abordar. Tengo que generar un calendario para los empleados, cada uno de ellos con limitaciones laborales específicas (algunas personales, otras comunes)
Con lo que estoy trabajando:
- Tengo doctores
- Cada médico tiene que trabajar 5 días / semana.
- Cada médico tiene que trabajar 1 noche / semana.
- Cada médico tiene que trabajar la misma cantidad de noches en comparación con otros médicos (o lo más cerca posible)
- Cada médico tiene que trabajar la misma cantidad de jueves y domingos en comparación con otros médicos (o lo más cerca posible)
- Algunos médicos no pueden trabajar ciertos días / noches (entrada del usuario)
- A algunos médicos les gustaría trabajar ciertos días / noches (entrada del usuario)
- A algunos médicos les gustaría no trabajar ciertos días / noches (entrada del usuario)
El usuario en cuestión es la persona que se ocupa del calendario, estoy tratando de crear una solución que genere automáticamente un calendario que obedezca todas las restricciones. La solución es solo una entrada de configuración grande "agregar médicos" y "agregar restricciones" para cada médico, luego un botón "generar calendario". Es realmente básico para el usuario.
Mi problema :
No estoy seguro de cómo generar la planificación real, he estado leyendo sobre redes neuronales, algoritmos genéticos, etc., y todos parecen la solución correcta, pero en realidad no.
Cuando miro los GA, están hechos para encontrar una solución con una población dada (mi problema), pero la población inicial ya debe obedecer el conjunto de restricciones dado, que luego se optimizaría. En ese caso, mi población inicial ya es la solución. No necesito que esté "optimizado". No importa que una sola persona trabaje 3 noches de lunes seguidas, siempre que sea correcto y que otros trabajen la misma cantidad, eso significa que otros también trabajarán 3 noches de lunes en algún momento y está bien. Lo que me hace pensar que los GA son demasiado "avanzados" para mí, ya que mi problema ya está resuelto con el punto de partida de un GA.
Pero, de nuevo, los GA realmente parecen estar hechos para esto, así que ¿podría no estar entendiéndolo correctamente?
De todos modos, como nunca he usado GA (o redes neuronales, o algo por el estilo), me gustaría estar seguro de que voy por el enfoque correcto antes de participar en una curva de aprendizaje como esa.
Mi pregunta :
¿Cuál crees que es un buen enfoque / algoritmo / técnica para un problema como el mío? ¿Gas? ¿Redes neuronales? ¿Algo más completamente diferente?
Soy todo oídos y abierto para más detalles si es necesario, pero creo que me he dejado bastante claro :)
Respuestas:
Los algoritmos genéticos y las redes neuronales no son adecuados aquí. Son metaheurísticas para encontrar una solución aproximada suficientemente buena a un problema. Notablemente, ambos requieren que encuentre una función de costo para calificar las soluciones candidatas. Una vez que tenga dicha función de costo, podría ser más fácil encontrar manualmente un algoritmo que se optimice para este costo.
Este es un pensamiento importante: dados dos cronogramas, necesitamos una forma de decidir si el cronograma A o el cronograma B es "mejor". Has enumerado varios criterios, pero no está claro cómo se relacionan. ¿El incumplimiento de un criterio falla la solución completa? ¿O fallar parcialmente una restricción solo la convierte en una solución peor que otras?
En el nivel más básico, puede dividir la semana en franjas horarias discretas y aplicar fuerza bruta a todas las combinaciones de ranuras y médico. Sin embargo, puede usar restricciones que fallan para reducir este espacio de búsqueda a un tamaño más manejable. Las restricciones sobre el horario de trabajo y los turnos nocturnos parecen ser adecuadas para dicha limitación de espacio de búsqueda. Luego te quedan cientos de soluciones candidatas.
Para seleccionar la mejor solución candidata, deberá clasificarlos. Esto es bastante fácil si una restricción blanda tiene una precedencia clara sobre todas las demás restricciones blandas, por ejemplo, si un médico no puede trabajar un cierto turno, se le da más importancia que un médico que no quiere trabajar ese turno. Pero no puedo decidir estas reglas por usted, esa es una decisión administrativa. Es más difícil si dos restricciones blandas no tienen una precedencia clara, en cuyo caso tendrá que encontrar algún tipo de función de costo que unifique la importancia de dos restricciones en una sola métrica.
Probablemente construiría un algoritmo codicioso que llene una tabla de tiempo en blanco de acuerdo con algunos criterios priorizados. Puede que esta no sea la solución más óptima, pero es mucho más fácil que filosofar sobre lo que realmente significa "óptimo".
Como primer paso, puede completar los turnos nocturnos los fines de semana y tratar de seleccionar aquellos médicos que no hayan realizado turnos nocturnos durante el fin de semana durante más tiempo, también teniendo en cuenta los deseos del usuario "No puedo trabajar allí" . Suponiendo que estos deseos son por semana y no continuos, esto significa que un médico que no puede trabajar en las noches de fin de semana durante una semana sería elegido la próxima semana.
Se puede utilizar un procedimiento similar para las otras noches: después de tratar de respetar los deseos de los usuarios, debe completar los médicos de acuerdo con quién no ha estado haciendo turnos nocturnos durante el mayor tiempo. El procedimiento se repite de manera similar para el tercer tipo de intervalo de tiempo, los turnos de día. Si no se pueden conciliar los deseos de dos usuarios, puede realizar un seguimiento de la frecuencia con la que se concedió un deseo de los usuarios y luego priorizar al médico con menos deseos concedidos.
Desafortunadamente, puedo ver un par de formas de jugar este sistema: por ejemplo, si un médico fuera elegido para trabajar en un turno de noche de fin de semana pero presenta una solicitud de "no puede trabajar allí", su selección se retrasaría una semana, reduciendo su frecuencia de turnos nocturnos de fin de semana a costa de sus colegas. Si se implementa un procedimiento de resolución de deseos que analiza la cantidad de solicitudes rechazadas, un usuario podría realizar un par de solicitudes imposibles para impulsar una solicitud que desea procesar. Sin embargo, suponiendo una buena fe (y la flexibilidad para que los médicos intercambien turnos entre sí), dicho algoritmo debería dar como resultado una solución lo suficientemente buena.
fuente
Puede usar recocido simulado .
Hice algo así antes de conseguir mi primer trabajo: consulte https://vimeo.com/20610875 (demostración a partir de las 2:50, algoritmo explicado a partir de las 6:15).
El recocido simulado es un tipo de algoritmo genético, y tal vez no era adecuado en teoría (como @amon mantiene en su respuesta ), pero funcionó muy bien en la práctica, y fue casi del mismo caso de uso que el suyo.
El código fuente está disponible (C #), pero aunque funciona, es terrible, me temo, fue hace unos años y, al ser un autodidacta, no sabía nada sobre el mantenimiento. Sin embargo, arrojó resultados muy buenos.
Cómo funciona en pocas palabras:
Genere 1 horario posible (puede que no sea muy bueno, pero físicamente posible) como punto de partida. El algoritmo genético no es necesario en este punto: puede simplemente abrirse paso hasta la primera solución que pueda encontrar. Solía retroceder . La complejidad computacional se puede superar resolviendo la rotación de cada día por separado. Si no hay una solución en absoluto (según sea el caso), es en este punto cuando la detecta.
Haga un grupo de soluciones, por ejemplo, 100 copias de esta solución de nivel de entrada para empezar.
Mute todas las soluciones al azar: haga que los médicos intercambien turnos entre ellos, retire a un médico aleatorio de su turno y coloque a una persona disponible al azar, etc.
Evaluar cada solución con una función de aptitud que determina qué tan buena es. ¿Un chico trabaja más noches que otro? Restar puntos de penalización. ¿Alguien quería hacer el lunes pero ellos no? Restar puntos de penalización nuevamente.
Tome, digamos, las 20 mejores soluciones y cópielas 5 veces, sobrescribiendo las 80 restantes con ellas, llevándolas así a la próxima generación. Supervivencia del más apto.
Enjuague y repita.
Los números son obviamente arbitrarios, es posible que deba manipular los parámetros para encontrar la configuración óptima para su escenario.
En cuanto a la mutación de una solución, el recocido simulado introduce algo llamado temperatura. Básicamente significa que al principio debe mutar sus soluciones con bastante fuerza (por ejemplo, hacer siempre 10 intentos de cambio de turnos de una sola vez) y gradualmente volverse menos agresivo con las iteraciones posteriores, por lo que se vuelven más afinadas (por ejemplo, hacia abajo a solo 2 intentos de ajustes por generación).
fuente
Los algoritmos genéticos se aplican aquí. Durante mi programa de pregrado, uno de mis colegas escribió un artículo sobre un problema muy similar al suyo.
Puede buscar la programación de la tienda de empleo y también la programación de la tienda abierta o la programación de la tienda de flujo pueden ser puntos de partida interesantes
Para usar un algoritmo genético no necesita una solución perfecta, puede comenzar con N candidatos aleatorios y aplicar una función de aptitud física a cada uno de ellos, por ejemplo:
Generando N candidatos, elegiría la X mejor de ellos , serían los que menos impondrían las restricciones. Trabajando con ellos, cruzando y mutando a lo largo de varias generaciones, uno puede terminar con una buena solución.
Habiendo hablado de todo eso, cada vez que usaba un Algoritmo Genético que se basaba más en la mutación que en el cruce, podía desarrollar un Recocido Simulado que funcionaría mucho mejor, con una implementación más fácil. El costo / aptitud y la función de mutación para el algoritmo genético probablemente serán muy similares a las utilizadas en un recocido simulado. Comenzaría allí, mira la respuesta de @Konrad Morawski
La búsqueda de Google encuentra buenos resultados para Job Shop y GA
fuente