es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Problema de programación de horarios de la empresa/empleado en Prolog.

Tengo un problema de planificación de turnos semi complejo en Prolog.
Según lo que vi, puede resolverse con CLP, pero no estoy muy familiarizado y los recursos en línea no me ayudaron realmente.

El problema establece que la empresa tiene 50 empleados y que cada empleado puede trabajar en el turno de la mañana (M), el turno de la tarde (E), el turno de la noche (N) o tener un día de descanso (R).
El problema tiene 2 restricciones: que al menos 15 empleados deben trabajar en el turno de la mañana (M), 10 en el turno de la tarde (E) y 8 en el turno de la noche (N), y que ningún empleado puede trabajar en el turno de la noche (N) y tener un turno de la mañana (M) al día siguiente.

Se pide producir un horario de 30 días que cumpla con las restricciones mencionadas y que existan múltiples soluciones.

¿Cuál podría ser una forma de abordar el problema y cómo podría implementarlo usando código en Prolog?

Muchas gracias.

Tags:  ,

Answer

  1. Avatar for davy.ai

    Una forma de abordar el problema es utilizar un solucionador de problemas de satisfacción de restricciones (CSP, por sus siglas en inglés) en Prolog, como CLP(FD) o CHR. La primera restricción se puede expresar como una suma de los turnos de cada empleado, mientras que la segunda restricción se puede codificar como una restricción entre los turnos de cada empleado en días consecutivos. Una posible forma de implementar esto en Prolog usando CLP(FD) es:

    :- use_module(library(clpfd)).
    
    schedule(Schedule) :-
        employee_shifts(Shifts),
        length(Schedule, 30), % el horario es una lista de 30 días
        maplist(same_length(Shifts), Schedule), % cada día tiene la misma cantidad de turnos que los empleados
        flatten(Schedule, FlatSchedule),
        FlatSchedule ins 1..4, % cada turno es M, E, N o R
        sum_list(Shifts, MCount), MCount #>= 15, % al menos 15 empleados trabajan en el turno de la mañana
        count(2, Shifts, ECount), ECount #>= 10, % al menos 10 empleados trabajan en el turno de la tarde
        count(3, Shifts, NCount), NCount #>= 8, % al menos 8 empleados trabajan en el turno de la noche
        no_consecutive_nights(FlatSchedule), % ningún empleado trabaja N y M en días consecutivos
        labeling([], FlatSchedule),
        group_shifts(Schedule).
    
    % generar una lista de 50 turnos de empleados
    employee_shifts(Shifts) :-
        length(Shifts, 50),
        Shifts ins 1..3, % los empleados pueden trabajar M, E o N
        global_cardinality(Shifts, [1-_, 2-_, 3-_]).
    
    % restricción: ningún empleado trabaja N y M en días consecutivos
    no_consecutive_nights([]).
    no_consecutive_nights([_]).
    no_consecutive_nights([S1, S2|R]) :-
        (S1 #= 3 #/\ S2 #= 1) #==> false, % N seguido de M es inválido
        no_consecutive_nights([S2|R]).
    
    % restricción: agrupar los turnos por empleado
    group_shifts(Schedule) :-
        transpose(Schedule, ShiftsByDay),
        maplist(group_shifts_helper, ShiftsByDay).
    
    group_shifts_helper(Shifts) :-
        selectchk(1, Shifts, Ms, Rest), % empleados trabajando en el turno de la mañana
        selectchk(2, Rest, Es, Ns), % empleados trabajando en el turno de la tarde
        selectchk(3, Ns, Ns, Rs), % empleados trabajando en el turno de la noche
        append([Ms, Es, Ns, Rs], Shifts). % agrupar los turnos por empleado
    

    En este código, employee_shifts/1 genera una lista de 50 turnos de empleados, cada uno representado por un número entero entre 1 (M) y 3 (N). El predicado schedule/1 genera un horario de 30 días creando una lista de listas con la misma cantidad de turnos que los empleados, aplanándolo en una lista única de turnos y imponiendo las restricciones sobre la cantidad de empleados que trabajan en cada turno y la ausencia de turnos N y M consecutivos. El predicado labeling/2 encuentra una solución para las restricciones y el predicado group_shifts/1 agrupa los turnos por empleado. Hay que tener en cuenta que esta implementación asume que cada empleado debe trabajar exactamente un turno por día, pero esto se puede modificar fácilmente para permitir múltiples turnos por empleado (por ejemplo, agregando una restricción sobre el número máximo de turnos por día).

Comments are closed.