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

下面的示例程序在匹配某些条件时生成一个计划

条件:

  • 每班分配一名员工
  • 任何员工不得连续轮班工作
  • 不得为员工安排个人假期
  • 该计划提供了有效的解决方案,但大约需要4分钟,尽管只有128种可能的组合(7班2名员工)。 该程序是在单核虚拟机中使用gprolog实现执行的

    这怎么可能?发生了什么事

    % 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_计划时。你应该根据应用的条件得到正确的解决方案。