如何使用prolog指定软约束? (有软约束的拼图)

时间:2014-08-24 09:53:28

标签: prolog

我被赋予了使用prolog解决难题的任务。

作为prolog的初学者,在过去的几个小时里刚刚阅读了几本关于prolog的在线教程,我不知道如何开始这个...

这个难题规模要大得多,并且有更多限制,但我会在这里简化它,因为我只是想得到这个想法,以便我可以稍后进行扩展。

这个谜题是这样的: 您需要为本周的2名员工A和B安排轮班。(包括周末) 规则:

  1. 在一天内,A或B(但不是两者)必须正常工作
  2. A必须在星期二工作
  3. 没有人可以连续工作超过2天
  4. 软约束:

    1. A喜欢星期四工作(如果A在星期四工作,则加2分)
    2. B不喜欢星期三工作(如果B在星期三不工作则加5分)
    3. 图表:

      |--------|--------|--------|---------|-----------|----------|--------|----------| 
      |        | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday |
      |--------|--------|--------|---------|-----------|----------|--------|----------| 
      |    A   |        |        |         |           |          |        |          |
      |--------|--------|--------|---------|-----------|----------|--------|----------|
      |    B   |        |        |         |           |          |        |          |
      |--------|--------|--------|---------|-----------|----------|--------|----------|
      

      如何安排这样才能达到最高点?

      一种可能的解决方案(有几种)将是:

      |--------|--------|--------|---------|-----------|----------|--------|----------| 
      |        | Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday |
      |--------|--------|--------|---------|-----------|----------|--------|----------| 
      |    A   |        |        |    X    |     X     |          |        |     X    |
      |--------|--------|--------|---------|-----------|----------|--------|----------|
      |    B   |    X   |    X   |         |           |     X    |    X   |          |
      |--------|--------|--------|---------|-----------|----------|--------|----------|
      

      我们可以获得的最大值是5分。因为没有解决方案可以在满足所有规则的同时获得7分的满分。

      我的问题:

      1. 我们如何设置软约束规则?由于不可能在上述软约束条件下遵守所有规则,因此必须根据最高点来决定采用哪一条规则。
      2. 如何在prolog中表示上面的二维数组?我已阅读有关List的信息,但不知道如何表示2d List。
      3. 提前致谢!

2 个答案:

答案 0 :(得分:2)

Plain Prolog可能不是这里的最佳选择。这些问题最容易使用0/1整数编程建模,并使用IP或有限域求解器求解,这是几个增强的Prolog所提供的。这是ECLiPSe中的解决方案(免责声明:我是合作开发人员)。软约束通过目标函数处理。

:- lib(ic).
:- lib(ic_global_gac).
:- lib(branch_and_bound).

schedule(Points, As, Bs) :-
    As = [_ASu,_AMo, ATu,_AWe, ATh,_AFr,_ASa],      
    Bs = [_BSu,_BMo,_BTu, BWe,_BTh,_BFr,_BSa],      

    As :: 0..1,
    Bs :: 0..1,
    ( foreach(A,As), foreach(B,Bs) do A+B #= 1 ),    % Rule 1
    ATu = 1,                                         % Rule 2
    sequence(0, 2, 3, As),            % 0..2 out of 3, Rule 3
    sequence(0, 2, 3, Bs),

    Points #= 2*ATh + 5*(1-BWe),                     % Soft

    Cost #= -Points,
    append(As, Bs, ABs),
    minimize(labeling(ABs), Cost).


?- schedule(P, As, Bs).
P = 5
As = [0, 0, 1, 1, 0, 0, 1]
Bs = [1, 1, 0, 0, 1, 1, 0]
Yes (0.03s cpu)

答案 1 :(得分:2)

在Prolog中,对于简单的问题,我们可以尝试应用一个简单的模式',简单的generate and test,其简单性很有趣。我们只是'提供适当的域生成器和测试。

generate(D0, D) :-
    length(D0, DL),
    length(D, DL),
    maplist(dom_element, D).
dom_element(a).
dom_element(b).

test(D, Score, D) :-
    D = [_Sunday, _Monday, Tuesday, Wednesday, Thursday, _Friday, _Saturday],

    % 1. In a single day, either A or B (but not both) must be working
    %    Here True by construction

    % 2. A must work on Tuesday
    Tuesday = a,

    % 3. no one can work continuously for more than 2 days
    no_more_than_2(D),

    % soft 1. A prefers to work on Thursday (add 2 points if A works on Thursday )
    ( Thursday = a -> S1 is 2 ; S1 is 0 ),

    % soft 2. B dislike to work on Wednesday (add 5 points if B doesn't work on Wednesday)
    ( Wednesday \= b -> S2 is 5 ; S2 is 0 ),

    Score is S1 + S2.

% edit: jshimpfs suggestion, beside being much better, highlights a bug
no_more_than_2(D) :-
    nth0(I, D, E),
    length(D, L),
    J is (I+1) mod L,
    nth0(J, D, E),
    K is (J+1) mod L,
    nth0(K, D, E),
    !, fail.
no_more_than_2(_).

solve(D0, Best) :-
    D0 = [sun,mon,tue,wed,thu,fri,sat],
    setof(Score/Sol, D^(generate(D0, D), test(D, Score, Sol)), All),
    last(All, Best).

试验:

?- solve(_,L).
L = 5/[b, b, a, a, b, b, a].