Prolog company/employee 调度问题
Prolog company/employee scheduling problem
我在 prolog 中有一个半复杂的班次安排问题。
据我所知,它可以用 CLF 解决,但我不是很熟悉,网上的资源也没有真正帮助我。
问题是公司有50名员工,每个员工要么上早班(M),要么晚班(E)、夜班(N)或休息日(R) .
该问题有 2 个约束条件:至少 15 名员工必须在早班工作 (M),10晚上一个(E)和8个晚上一个(N)并且没有员工可以上夜班(N) 第二天要上早班(M).
它要求通过满足上述约束并存在多个解决方案来生成 30 天的时间表。
有什么方法可以解决这个问题,我如何使用 prolog 中的代码来实现它?
非常感谢!
这是一个完整的解决方案(使用 swi-prolog):
days_in_month(30).
employees_num(50).
go :-
days_in_month(Days),
length(M, Days),
days(M),
show_days(M).
days([D1, D2|T]) :-
two_days(D1, D2),
(T = [] ; days([D2|T])).
other_day_constraints(D) :-
day_constraint(10, e, D),
maplist(rest_if_not_work, D).
day_constraint(Min, Element, Lst) :-
employees_num(EmpsNum),
list_has_ge_elements_being(Min, Element, EmpsNum, Lst).
two_days(D1, D2) :-
% Set the full number of employees, otherwise prevent_double_shift can shorten the list
employees_num(EmpsNum),
length(D1, EmpsNum),
length(D2, EmpsNum),
% Pass the 2-day constraint first
day_constraint(8, n, D1),
prevent_double_shift(D1, D2),
day_constraint(15, m, D2),
% Remainder of the day constraints
day_constraint(15, m, D1),
day_constraint(8, n, D2),
other_day_constraints(D1),
other_day_constraints(D2).
prevent_double_shift([], []).
prevent_double_shift([H1|T1], [H2|T2]) :-
(H1 == n -> dif(H2, m) ; true),
prevent_double_shift(T1, T2).
rest_if_not_work(E) :-
(var(E) -> E = r ; true).
show_days([]).
show_days([D|T]) :-
show_day(D),
show_days(T).
show_day(D) :-
forall(member(E, D), (upcase_atom(E, U), write(U))),
nl.
% Potentially use E as the first element
list_length_abs(N, E, [E|_]) :-
N > 1.
list_length_abs(0, _, []).
list_length_abs(1, E, [E]).
list_has_ge_elements_being(0, _E, MaxLen, L) :-
!,
MaxLen >= 0,
% Don't need E
list_length_abs(MaxLen, _, L).
list_has_ge_elements_being(Min, E, MaxLen, [H|T]) :-
must_be(integer, MaxLen),
MaxLen >= 1,
must_be(integer, Min),
Min >= 1,
Min =< MaxLen,
list_has_ge_elements_being_(Min, E, MaxLen, [H|T]).
list_has_ge_elements_being_(Min, E, MaxLen, L) :-
Min =:= MaxLen ->
length(L, Min),
maplist(=(E), L)
; MaxLen =:= 1 ->
list_has_ge_elements_being_maxlen1_(Min, L, E)
; list_has_ge_elements_being_flex_(Min, E, MaxLen, L).
list_has_ge_elements_being_maxlen1_(0, [_], _).
list_has_ge_elements_being_maxlen1_(1, [E], E).
list_has_ge_elements_being_flex_(Min, H, MaxLen, [H|T]) :-
% Loop while unifying the element
succ(Min0, Min),
succ(MaxLen0, MaxLen),
% Check whether can terminate the list early
(Min0 =:= 0 -> true ; list_has_ge_elements_being_(Min0, H, MaxLen0, T)).
list_has_ge_elements_being_flex_(Min, E, MaxLen, [H|T]) :-
% Ensure that the element is not already the intended value
\+ E == H,
succ(MaxLen0, MaxLen),
list_has_ge_elements_being_(Min, E, MaxLen0, T).
我在 prolog 中有一个半复杂的班次安排问题。 据我所知,它可以用 CLF 解决,但我不是很熟悉,网上的资源也没有真正帮助我。
问题是公司有50名员工,每个员工要么上早班(M),要么晚班(E)、夜班(N)或休息日(R) . 该问题有 2 个约束条件:至少 15 名员工必须在早班工作 (M),10晚上一个(E)和8个晚上一个(N)并且没有员工可以上夜班(N) 第二天要上早班(M).
它要求通过满足上述约束并存在多个解决方案来生成 30 天的时间表。
有什么方法可以解决这个问题,我如何使用 prolog 中的代码来实现它?
非常感谢!
这是一个完整的解决方案(使用 swi-prolog):
days_in_month(30).
employees_num(50).
go :-
days_in_month(Days),
length(M, Days),
days(M),
show_days(M).
days([D1, D2|T]) :-
two_days(D1, D2),
(T = [] ; days([D2|T])).
other_day_constraints(D) :-
day_constraint(10, e, D),
maplist(rest_if_not_work, D).
day_constraint(Min, Element, Lst) :-
employees_num(EmpsNum),
list_has_ge_elements_being(Min, Element, EmpsNum, Lst).
two_days(D1, D2) :-
% Set the full number of employees, otherwise prevent_double_shift can shorten the list
employees_num(EmpsNum),
length(D1, EmpsNum),
length(D2, EmpsNum),
% Pass the 2-day constraint first
day_constraint(8, n, D1),
prevent_double_shift(D1, D2),
day_constraint(15, m, D2),
% Remainder of the day constraints
day_constraint(15, m, D1),
day_constraint(8, n, D2),
other_day_constraints(D1),
other_day_constraints(D2).
prevent_double_shift([], []).
prevent_double_shift([H1|T1], [H2|T2]) :-
(H1 == n -> dif(H2, m) ; true),
prevent_double_shift(T1, T2).
rest_if_not_work(E) :-
(var(E) -> E = r ; true).
show_days([]).
show_days([D|T]) :-
show_day(D),
show_days(T).
show_day(D) :-
forall(member(E, D), (upcase_atom(E, U), write(U))),
nl.
% Potentially use E as the first element
list_length_abs(N, E, [E|_]) :-
N > 1.
list_length_abs(0, _, []).
list_length_abs(1, E, [E]).
list_has_ge_elements_being(0, _E, MaxLen, L) :-
!,
MaxLen >= 0,
% Don't need E
list_length_abs(MaxLen, _, L).
list_has_ge_elements_being(Min, E, MaxLen, [H|T]) :-
must_be(integer, MaxLen),
MaxLen >= 1,
must_be(integer, Min),
Min >= 1,
Min =< MaxLen,
list_has_ge_elements_being_(Min, E, MaxLen, [H|T]).
list_has_ge_elements_being_(Min, E, MaxLen, L) :-
Min =:= MaxLen ->
length(L, Min),
maplist(=(E), L)
; MaxLen =:= 1 ->
list_has_ge_elements_being_maxlen1_(Min, L, E)
; list_has_ge_elements_being_flex_(Min, E, MaxLen, L).
list_has_ge_elements_being_maxlen1_(0, [_], _).
list_has_ge_elements_being_maxlen1_(1, [E], E).
list_has_ge_elements_being_flex_(Min, H, MaxLen, [H|T]) :-
% Loop while unifying the element
succ(Min0, Min),
succ(MaxLen0, MaxLen),
% Check whether can terminate the list early
(Min0 =:= 0 -> true ; list_has_ge_elements_being_(Min0, H, MaxLen0, T)).
list_has_ge_elements_being_flex_(Min, E, MaxLen, [H|T]) :-
% Ensure that the element is not already the intended value
\+ E == H,
succ(MaxLen0, MaxLen),
list_has_ge_elements_being_(Min, E, MaxLen0, T).