Performance prolog如何只花几分钟检查一组可能的组合中的答案?
下面的示例程序在匹配某些条件时生成一个计划 条件:Performance prolog如何只花几分钟检查一组可能的组合中的答案?,performance,prolog,combinatorics,Performance,Prolog,Combinatorics,下面的示例程序在匹配某些条件时生成一个计划 条件: 每班分配一名员工 任何员工不得连续轮班工作 不得为员工安排个人假期 该计划提供了有效的解决方案,但大约需要4分钟,尽管只有128种可能的组合(7班2名员工)。 该程序是在单核虚拟机中使用gprolog实现执行的 这怎么可能?发生了什么事 % which shifts have to be assigned to employees shift(1, monday). shift(2, tuesday). shift(3, wednesda
% which shifts have to be assigned to employees
shift(1, monday).
shift(2, tuesday).
shift(3, wednesday).
shift(4, thursday).
shift(5, friday).
shift(6, saturday).
shift(7, sunday).
% available employees
employee(xerxes).
employee(yasmin).
% holidays specified in advance
unavailable(xerxes, shift(1, monday)).
get_all_employees(Employees) :-
findall(employee(E), employee(E), Employees).
get_all_shifts(Shifts) :-
findall(shift(D, N), shift(D, N), Shifts).
get_random_assignment(A) :-
get_all_employees(Es), member(E, Es),
get_all_shifts(Ss), member(S, Ss),
findall(assign(E,S), (E,S), A).
init_schedule(Schedule) :-
length(Ss, N),
get_all_shifts(Ss),
length(Schedule, N),
get_init_schedule_(Schedule).
get_init_schedule_([]).
get_init_schedule_([H|Schedule]) :-
get_random_assignment(A),
member(H, A),
get_init_schedule_(Schedule).
get_schedule(Schedule) :-
init_schedule(Schedule),
one_employee_per_shift(Schedule),
no_consecutive_shifts(Schedule),
no_shift_on_personal_holiday(Schedule).
dif_assignment(assign(employee(_), shift(D, N)), assign(employee(_), shift(D2, N2))) :-
D \== D2,
N \== N2.
alldiff([]).
alldiff([E|Es]) :-
maplist(dif_assignment(E), Es),
alldiff(Es).
%
% 1. condition
%
one_employee_per_shift(Schedule) :-
alldiff(Schedule).
%
% 2. Condition
%
no_consecutive_shift_(assign(employee(E), shift(D, _)), assign(employee(E2), shift(D2, _))) :-
D_TMP is D + 1,
(D2 =:= D_TMP -> E \== E2 ; true).
no_consecutive_shifts([]).
no_consecutive_shifts([A|As]) :-
maplist(no_consecutive_shift_(A), As),
no_consecutive_shifts(As).
%
% 3. Condition
%
no_shift_on_personal_holiday([]).
no_shift_on_personal_holiday([assign(employee(E), shift(D, N))|Schedule]) :-
(unavailable(E2, shift(D, N)) -> E \== E2 ; true),
no_shift_on_personal_holiday(Schedule).
虽然应该只有128种可能的组合,但大约需要4分钟(7班2名员工)
的确,这应该是搜索空间,但你可以探索更多。从你的谓词中考虑前两个答案:
?- get_schedule(S).
S = [assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(xerxes), shift(6, saturday)), assign(employee(yasmin), shift(1, monday)), assign(employee(yasmin), shift(3, wednesday)), assign(employee(yasmin), shift(5, friday)), assign(employee(yasmin), shift(7, sunday))] ;
S = [assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(xerxes), shift(6, saturday)), assign(employee(yasmin), shift(1, monday)), assign(employee(yasmin), shift(3, wednesday)), assign(employee(yasmin), shift(7, sunday)), assign(employee(yasmin), shift(5, friday))] .
这些都是相同的时间表(即,相同的人员轮班分配),元素只是以不同的方式安排。您不是在探索包含128个计划的解决方案空间,而是在探索类似于128个七元素列表的所有排列的集合。这太过分了
即使这很快,给出所有这些多余的答案并不是很好,这些答案都是非直观的排序。因此,解决办法是打破这种对称性,只尝试按人类预期的方式“正确排序”的日程安排
您的get_all_shift/1
已经为我们提供了我们期望的班次:
?- get_all_shifts(Shifts).
Shifts = [shift(1, monday), shift(2, tuesday), shift(3, wednesday), shift(4, thursday), shift(5, friday), shift(6, saturday), shift(7, sunday)].
我们只需要将init_schedule/1
更改为只生成以这种方式排列班次的时间表。换句话说,我们只需要让员工按照给定的班次进行配对:
init_schedule(Schedule) :-
get_all_shifts(Shifts),
shifts_schedule(Shifts, Schedule).
shifts_schedule([], []).
shifts_schedule([Shift | Shifts], [Assignment | Assignments]) :-
employee(Employee),
Assignment = assign(employee(Employee), Shift),
shifts_schedule(Shifts, Assignments).
现在只有128个解决方案:
?- time(findall(S, init_schedule(S), AllSchedules)), length(AllSchedules, N).
% 544 inferences, 0.000 CPU in 0.000 seconds (100% CPU, 3239908 Lips)
AllSchedules = [[assign(employee(xerxes), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(3, wednesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(xerxes), shift(5, friday)), assign(employee(xerxes), shift(6, saturday)), assign(employee(...), shift(..., ...))], [assign(employee(xerxes), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(3, wednesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(xerxes), shift(5, friday)), assign(employee(...), shift(..., ...)), assign(..., ...)], [assign(employee(xerxes), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(3, wednesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(...), shift(..., ...)), assign(..., ...)|...], [assign(employee(xerxes), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(xerxes), shift(3, wednesday)), assign(employee(...), shift(..., ...)), assign(..., ...)|...], [assign(employee(xerxes), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(...), shift(..., ...)), assign(..., ...)|...], [assign(employee(xerxes), shift(1, monday)), assign(employee(...), shift(..., ...)), assign(..., ...)|...], [assign(employee(...), shift(..., ...)), assign(..., ...)|...], [assign(..., ...)|...], [...|...]|...],
N = 128.
其余的工作与预期一样,可以非常快速地生成单个解决方案:
?- time(get_schedule(S)).
% 8,664 inferences, 0.003 CPU in 0.002 seconds (108% CPU, 3426483 Lips)
S = [assign(employee(yasmin), shift(1, monday)), assign(employee(xerxes), shift(2, tuesday)), assign(employee(yasmin), shift(3, wednesday)), assign(employee(xerxes), shift(4, thursday)), assign(employee(yasmin), shift(5, friday)), assign(employee(xerxes), shift(6, saturday)), assign(employee(yasmin), shift(7, sunday))] ;
% 3,610 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 4158272 Lips)
false.
您是否注意到:
|?-init|u时间表(schedule)。计划=[分配(员工(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工)(员工?是
是,这是初始解向量。执行get_计划时。你应该根据应用的条件得到正确的解决方案。