Prolog限制将错误最小化。标签/ 2参数1错误

时间:2018-12-18 18:42:23

标签: prolog clpfd sicstus-prolog

我有一个在学校开发的应用程序,其中包括为某个主题生成工作组。 我必须满足的限制如下:

  • 具有类似GPA的学生应该在同一组中。
  • 以前一起工作过的学生应该属于不同的小组。
  • 组大小应具有最小和最大大小。

我的谓词当前如下:

groups(Students, GPAs, PreviouslyWorkedTogether, [MinSize,MaxSize],Groups).

学生是学生ID的列表(例如:。[1,2,3,4])。

GPA是GPA的列表(例如:。[4.0,3.5,2.0,3.7])。

上面的列表相互关联,因此ID = 1的学生的GPA为4.0,ID = 2的GPA = 3.5,依此类推。

PreviouslyWorkedTogether是对的列表,其中每个元素具有两个以前一起工作的学生的ID。 (例如[[1,3],[2,4]]-学生1与学生3合作,学生2与学生4合作)。

分组是期望的结果。它具有与学生列表相同的大小。对于每个学生,应使用该学生所属的组ID填充变量。 (例如:。[1,2,1,2]->这意味着学生1和3在第1组中,学生2和4在第2组中。)

我已经成功实现了(我认为)组大小边界。 但是,我在GPA和WorkedTogether部分遇到了麻烦。

由于它们看起来彼此非常相似,所以我只会接近其中之一。

下一个代码段是我当前解决GPA问题的方法。

getGPAs(_, [], []).
getGPAs(GPAs, [H|T], [GroupGPAsH | GroupGPAsT]):-
    element(H, GPAs, GPA),   
    GroupGPAsH #= GPA,
    getGPAs(GPAs, T, GroupGPAsT).

constrain_GPA(_, _, MaxGroupID, GroupID, []):- GroupID #> MaxGroupID, !.
constrain_GPA(GPAs, Groups, MaxGroupID, GroupID, [DiffsH | DiffsT]):-
    findall(Index, element(Index,Groups,GroupID),GroupElems),
    getGPAs(GPAs, GroupElems, GroupGPAs),
    minimum(MinGPA, GroupGPAs),
    maximum(MaxGPA, GroupGPAs),
    NextID is GroupID + 1,
    DiffsH #= MaxGPA - MinGPA,
    constrain_GPA(GPAs, Groups, MaxGroupID, NextID, DiffsT).

首先,我找到属于GroupID的元素的所有索引,并将其存储在列表(GroupElems)中。

之后,我使用这些索引来获取组中每个成员的GPA。

然后,我只需要获取组中GPA的最大值和最小值并计算差异,将其存储在包含每个组GPA差异的列表中即可。

在那之后,我要做的就是最小化数组值的总和,这样我们得到的结果将包含具有相似GPA的组的最佳结果。

constrain_GPA(GPAs, Groups, MaxGroupID, 1, Diffs),
sum(Diffs, #=, SumDiffs),
labeling([minimize(SumDiffs)], Groups),

但是,它继续使我对标签谓词产生错误。我相信问题是findall谓词。我不知道我是否可以使用它。

逻辑似乎正确,但是编码是问题所在。

这是完整的代码(最后是测试查询)

:- use_module(library(clpfd)).
:- use_module(library(lists)).

groups(Students, GPAs, PreviousUCsInfo, [MinSize, MaxSize], Groups):-
    %create an array representing the groups of each student
    length(Students, NumStudents),
    length(Groups, NumStudents),
    MaxNumGroups is NumStudents div MinSize,
    MinNumGroupsMod is NumStudents mod MaxSize,
    if_then_else(
                    (MinNumGroupsMod = 0),
                        (MinNumGroups is NumStudents div MaxSize),
                        (MinNumGroups is (NumStudents div MaxSize) + 1)
                ),
    domain([MaxGroupID], MinNumGroups, MaxNumGroups),
    domain(Groups, 1, MaxNumGroups),

    %constrain group size
    nvalue(MaxGroupID, Groups),
    constrain_count(Groups, [MinSize, MaxSize], MaxGroupID, 1),

    %contrain GPA
    constrain_GPA(GPAs, Groups, MaxGroupID, 1, Diffs),
    sum(Diffs, #=, SumDiffs),

    append(Groups, [MaxGroupID], LabelVars),
    labeling([minimize(SumDiffs)], LabelVars).

constrain_count(_, _, MaxGroupID, GroupID):- GroupID #> MaxGroupID.
constrain_count(Groups, [MinSize, MaxSize], MaxGroupID, GroupID):-
    count(GroupID, Groups, #=, Times),
    Times #>= MinSize #/\ Times #=< MaxSize,
    NextID is GroupID + 1,
    constrain_count(Groups, [MinSize, MaxSize], MaxGroupID, NextID).

getGPAs(_, [], []).
getGPAs(GPAs, [H|T], [GroupGPAsH | GroupGPAsT]):-
    element(H, GPAs, GPA),   
    GroupGPAsH #= GPA,
    getGPAs(GPAs, T, GroupGPAsT).

constrain_GPA(_, _, MaxGroupID, GroupID, []):- GroupID #> MaxGroupID, !.
constrain_GPA(GPAs, Groups, MaxGroupID, GroupID, [DiffsH | DiffsT]):-
    findall(Index, element(Index,Groups,GroupID),GroupElems),
    getGPAs(GPAs, GroupElems, GroupGPAs),
    minimum(MinGPA, GroupGPAs),
    maximum(MaxGPA, GroupGPAs),
    NextID is GroupID + 1,
    DiffsH #= MaxGPA - MinGPA,
    constrain_GPA(GPAs, Groups, MaxGroupID, NextID, DiffsT).

if_then_else(C, I, _):- C, !, I.
if_then_else(_, _, E):- E.

%Query to test: groups([1,2,3],[4,2,3],_,[1,2],Var).
% The results expected are:
%for groups of 1 student every combination of groups.
%For groups of 2 students the following:
%[1,2,1]
%[1,2,2]
%[2,1,1]
%[2,1,2]

1 个答案:

答案 0 :(得分:1)

您收到实例化错误,因为变量SumDiffs在功能上不依赖于LabelVars。一些评论:

主要问题是目标函数。规范有点模棱两可:

  

GPA相似的学生应该在同一组中。

显然,您将其解释为:

  

找到一个最小化sum(g。1.n)(max(i in group   g)(GPA [i])-min(g组中的i)(GPA [i]))

我想这是您在constrain_GPA/5中遇到麻烦的根源。我猜您正在尝试计算每个组的数量DiffsH,并将其总和最小化。计算该数量有点尴尬。您不能以这种方式使用findall/3,因为element/3是一个约束,并且永远不能在回溯时返回更多值。我会稍微不同地阅读规范:

  

找到一个使sum(i,j在同一组中)最小的解决方案(| GPA [i]-   GPA [j] |)

这更容易实现:计算(n-1)*(n-2)个数量,每个有序对(i,j)取一个,然后求和。

或者您可以通过另一种方式阅读规范:

  

找到一个解决方案,使得如果GPA [i] = GPA [j],则i和j应为   在同一组中。

将整个问题变成可满足性的问题。我想请教授对规范进行澄清。

小点:

  • 代替nvalue(MaxGroupID,Groups)maximum(MaxGroupID,Groups) 对我来说可能更有效率。

  • 使用内置的if_then_else(A,B,C)代替(A -> B ; C)

  • constrain_count/4可以替换为global_cardinality/2,但请记住,您必须允许使用空组。

我希望这可以帮助您完成任务。