У меня полусложная проблема планирования смены в прологе. Из того, что я видел, это можно решить с помощью CLF, но я не настолько хорошо знаком, и ресурсы в Интернете мне не очень помогли.
В задаче говорится, что в компании 50 сотрудников, и каждый сотрудник может работать либо в утреннюю смену (M), либо в вечернюю смену (E), в ночную смену (N), либо в день отдыха (R). Задача имеет 2 ограничения: не менее 15 сотрудников должны работать в утреннюю смену (M), 10 в вечернюю смену (E) и 8 в ночную смену (N), и что ни один сотрудник не может работать в ночную смену (N). и иметь утреннюю смену (M) на следующий день. Также в течение 7 дней у работника должно быть не менее 2 выходных, например, с 1 по 7 день, не менее двух R и столько же со 2 до 8.
Требуется создать 30-дневный график, удовлетворяющий вышеуказанным ограничениям и при наличии множества решений.
Какой может быть способ решить проблему и как я могу реализовать ее, используя код в прологе?
Большое спасибо!
Вот решение без последней задачи
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.
list_has_ge_elements_being(Min, Elem, MaxLen, L) :-
list_has_ge_elements_being_(L, Min, Elem, MaxLen).
list_has_ge_elements_being_(L, Min, Elem, Min) :-
!,
length(L, Min),
maplist(=(Elem), L).
list_has_ge_elements_being_(_L, 0, _Elem, _MaxLen).
list_has_ge_elements_being_([H|T], Min, Elem, MaxLen) :-
Min @> 0,
MaxLen @> Min,
( H = Elem,
Min0 is Min - 1
; Min0 = Min
),
MaxLen0 is MaxLen - 1,
list_has_ge_elements_being_(T, Min0, Elem, MaxLen0).
Вот часть головоломки - я уверен, что вы можете понять остальное:
split_list_length_excl_rem(SegLen, L, Segs) :-
length(L, Len),
between(1, Len, SegLen),
split_list_length_excl_rem_(SegLen, Len, L, Segs).
split_list_length_excl_rem_(SegLen, Len, L, Segs) :-
( Len @< SegLen
-> Segs = []
; length(Seg, SegLen),
split_list_length_excl_rem_seg_(Seg, L, R),
Segs = [Seg|SegsT],
Len0 is Len - SegLen,
split_list_length_excl_rem_(SegLen, Len0, R, SegsT)
).
split_list_length_excl_rem_seg_([], R, R).
split_list_length_excl_rem_seg_([H|T], [H|LT], R) :-
split_list_length_excl_rem_seg_(T, LT, R).
Результаты в swi-прологе:
?- numlist(1, 30, NL), split_list_length_excl_rem(7, NL, S).
NL = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
S = [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21], [22, 23, 24, 25, 26, 27, 28]].
К сожалению, неправильно прочитал неделю, это более уместно:
sliding_segment(SegLen, L, Seg) :-
length(L, Len),
between(1, Len, SegLen),
length(Seg, SegLen),
sliding_segment_(L, Seg).
sliding_segment_([H|T], Seg) :-
fill_list_copy(Seg, [H|T]).
sliding_segment_([_|T], Seg) :-
sliding_segment_(T, Seg).
fill_list_copy([], _).
fill_list_copy([H|T], [H|R]) :-
fill_list_copy(T, R).
Результаты в swi-прологе:
?- numlist(1, 30, NL), sliding_segment(7, NL, Seg).
Seg = [1, 2, 3, 4, 5, 6, 7] ;
Seg = [2, 3, 4, 5, 6, 7, 8] ;
Seg = [3, 4, 5, 6, 7, 8, 9] ;
...
Seg = [23, 24, 25, 26, 27, 28, 29] ;
Seg = [24, 25, 26, 27, 28, 29, 30] ;
false.
я могу понять, что ваш код разбивает список из 30 на весь возможный список из 7. Но я не могу понять, как я могу связать ваш код со своим, поэтому в прологе добавлено правило иметь как минимум 2 дня отдыха в период 7. Я Я могу понять, почему вы это сделали, но я просто не могу объединить эти коды для получения окончательного результата.
Подсказка: можно использовать forall
или foreach
, чтобы убедиться, что каждый 7-дневный сегмент содержит 2 дня отдыха. У вас уже есть list_has_ge_elements_being
.
Просто для информации - этот код взят с сайта stackoverflow.com/a/70600935