Prolog 适用于从记录中提取一对多关系的约束编程

Prolog 适用于从记录中提取一对多关系的约束编程,prolog,constraints,constraint-programming,Prolog,Constraints,Constraint Programming,也许有人能帮我解决Prolog或任何约束编程语言的问题。想象一个项目表(学生与母亲一起做一些事情的学校项目)。每个项目都有一个或多个孩子参与。对于每个孩子,我们存储其姓名和母亲的姓名。但对于每个项目,只有一个单元格包含所有母亲,一个单元格包含所有孩子。两个单元格的排序方式不一定相同 例如: +-----------+-----------+------------+ | | | | | Project | Parents |

也许有人能帮我解决Prolog或任何约束编程语言的问题。想象一个项目表(学生与母亲一起做一些事情的学校项目)。每个项目都有一个或多个孩子参与。对于每个孩子,我们存储其姓名和母亲的姓名。但对于每个项目,只有一个单元格包含所有母亲,一个单元格包含所有孩子。两个单元格的排序方式不一定相同

例如:

+-----------+-----------+------------+
|           |           |            |
|   Project |   Parents |   Children |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   1       |   Jane;   |   Brian;   |
|           |   Claire  |   Stephen  |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   2       |   Claire; |   Emma;    |
|           |   Jane    |   William  |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   3       |   Jane;   |   William; |
|           |   Claire  |   James    |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   4       |   Jane;   |   Brian;   |
|           |   Sophia; |   James;   |
|           |   Claire  |   Isabella |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   4       |   Claire  |   Brian    |
|           |           |            |
+-----------+-----------+------------+
|           |           |            |
|   5       |   Jane    |   Emma     |
|           |           |            |
+-----------+-----------+------------+
我希望这个例子能将问题形象化。正如我所说,两个单元格只包含由分隔符分隔的名称,但不一定以类似的方式排序。因此,对于技术应用,您可以将数据转换为:

+-------------+-----------+----------+
|   Project   |   Name    |   Role   |
+-------------+-----------+----------+
|   1         |   Jane    |   Mother |
+-------------+-----------+----------+
|   1         |   Claire  |   Mother |
+-------------+-----------+----------+
|   1         |   Brian   |   Child  |
+-------------+-----------+----------+
|   1         |   Stephen |   Child  |
+-------------+-----------+----------+
|   2         |   Jane    |   Mother |
+-------------+-----------+----------+
|   2         |   Claire  |   Mother |
+-------------+-----------+----------+
|   2         |   Emma    |   Child  |
+-------------+-----------+----------+
|   2         |   William |   Child  |
+-------------+-----------+----------+
|             |           |          |
|                                    |
|              And so on             |
每个项目的家长和子女人数相同。因此,对于每一笔交易,我们有n个母亲和n个孩子,每个母亲只属于一个孩子。有了这些约束条件,就可以从只涉及一个孩子的项目(即4和5)开始,通过逻辑推理将每位母亲分配给她的所有孩子

结果:

简有爱玛、斯蒂芬和詹姆斯

克莱尔有布赖恩和威廉

索菲亚有伊莎贝拉


我想知道如何使用约束编程来解决这个问题。此外,数据集可能未充分确定,我想知道是否有可能隔离那些在手动解决时(即当母子作业手动完成时)会打破未充分确定的记录。

我不确定是否理解问题的所有要求,但这里有一个minizing()中的约束编程模型。完整模型如下所示:

稍后注意:项目约束的第一个版本不正确。我删除了不正确的代码。有关原始答案,请参见编辑历史记录

enum mothers = {jane,claire,sophia};
enum children = {brian,stephen,emma,william,james,isabella};      

% decision variables

% who is the mother of this child?
array[children] of var mothers: x;


solve satisfy;

constraint
  % All mothers has at least one child
  forall(m in mothers) (
    exists(c in children) (
      x[c] = m
    )
  )
;

constraint
% NOTE: This is a more correct version of the project constraints.
% project 1
(
  ( x[brian] = jane /\ x[stephen] = claire) \/
  ( x[stephen] = jane /\ x[brian] = claire)
) 
/\
% project 2
(
  ( x[emma] = claire /\ x[william] = jane) \/
  ( x[william] = claire /\ x[emma] = jane) 
)
/\
% project 3
(
  ( x[william] = claire /\ x[james] = jane) \/
  ( x[james] = claire /\ x[william] = jane) 
)
/\
% project 4
( 
  ( x[brian] = jane /\ x[james] = sophia /\ x[isabella] = claire) \/
  ( x[james] = jane /\ x[brian] = sophia /\ x[isabella] = claire) \/
  ( x[james] = jane /\ x[isabella] = sophia /\ x[brian] = claire) \/
  ( x[brian] = jane /\ x[isabella] = sophia /\ x[james] = claire) \/
  ( x[isabella] = jane /\ x[brian] = sophia /\ x[james] = claire) \/
  ( x[isabella] = jane /\ x[james] = sophia /\ x[brian] = claire) 
)
/\

% project 4(sic!)
( x[brian] = claire) /\

% project 5
( x[emma] = jane)
;


output [
  "\(c): \(x[c])\n"
  | c in children
];
唯一的解决方案是

brian: claire
stephen: jane
emma: jane
william: claire
james: jane
isabella: sophia
编辑2:这里有一个更一般的解决方案。有关完整的模型,请参见

include "globals.mzn"; 

enum mothers = {jane,claire,sophia};
enum children = {brian,stephen,emma,william,james,isabella};      

% decision variables
% who is the mother of this child?
array[children] of var mothers: x;

% combine all the combinations of mothers and children in a project
predicate check(array[int] of mothers: mm, array[int] of children: cc) =
  let {
    int: n = length(mm);
    array[1..n] of var 1..n: y;
  } in
  all_different(y) /\
  forall(i in 1..n) (
     x[cc[i]] = mm[y[i]]
  )
;    

solve satisfy;

constraint
% All mothers has at least one child.
forall(m in mothers) (
  exists(c in children) (
    x[c] = m
  )
)
;


constraint
% project 1    
check([jane,claire], [brian,stephen]) /\
% project 2
check([claire,jane],[emma,william]) /\
% project 3
check([claire,jane],[william,james]) /\
% project 4
check([claire,sophia,jane],[brian,james,isabella]) /\
% project 4(sic!)
check([claire],[brian]) /\
% project 5
check([jane],[emma])
;

output [
 "\(c): \(x[c])\n"
 | c in children
];
该模型使用以下谓词确保考虑母亲与儿童的所有组合:

predicate check(array[int] of mothers: mm, array[int] of children: cc) =
   let {
     int: n = length(mm);
     array[1..n] of var 1..n: y;
  } in
  all_different(y) /\
  forall(i in 1..n) (
    x[cc[i]] = mm[y[i]]
  )
;    

它使用全局约束
all_different(y)
来确保
mm[y[i]]
mm
中的母亲之一,然后将“i”个孩子分配给特定的母亲。

这是我的第一个CHR计划,因此我希望有人能来给我一些建议,告诉我如何改进它

我的想法是,你需要把所有的清单扩展成事实。从那里,如果您知道一个项目只有一个父项目和一个子项目,您可以从中建立父项目关系。此外,一旦建立了父子关系,就可以从其他项目中的其他事实中删除该集合,并将问题的基数减少1。最终你会找到你能找到的一切。完全确定的数据集和不完全确定的数据集之间的唯一区别在于缩减的程度。如果它还没有达到目的,它会留下一些事实,这样你就可以看到哪些项目/家长/孩子仍在制造歧义

:- use_module(library(chr)).

:- chr_constraint project/3, project_parent/2, project_child/2, 
   project_parents/2, project_children/2, project_size/2, parent/2.

%% turn a project into a fact about its size plus 
%% facts for each parent and child in this project
project(N, Parents, Children) <=>
    length(Parents, Len),
    project_size(N, Len),
    project_parents(N, Parents),
    project_children(N, Children).

%% expand the list of parents for this project into a fact per parent per project
project_parents(_, []) <=> true.
project_parents(N, [Parent|Parents]) <=>
    project_parent(N, Parent),
    project_parents(N, Parents).

%% same for the children
project_children(_, []) <=> true.
project_children(N, [Child|Children]) <=>
    project_child(N, Child),
    project_children(N, Children).

%% a single parent-child combo on a project is exactly what we need
one_parent @ project_size(Project, 1), 
             project_parent(Project, Parent), 
             project_child(Project, Child) <=>
    parent(Parent, Child).

%% if I have a parent relationship for project of size N,
%% remove this parent and child from the project and decrease
%% the number of parents and children by one
parent_det @ parent(Parent, Child) \ project_size(Project, N), 
                                     project_parent(Project, Parent), 
                                     project_child(Project, Child) <=>
    succ(N0, N),
    project_size(Project, N0).
这将产生:

parent(sophia, isabella),
parent(jane, james),
parent(claire, william),
parent(jane, emma),
parent(jane, stephen),
parent(claire, brian).
为了证明不完全的决心,我添加了第七个项目:

project(7, [sally,sandy], [grace,miriam]).
然后程序输出以下内容:

project_parent(7, sandy),
project_parent(7, sally),
project_child(7, miriam),
project_child(7, grace),
project_size(7, 2),
parent(sophia, isabella),
parent(jane, james),
parent(claire, william),
parent(jane, emma),
parent(jane, stephen),
parent(claire, brian).
如您所见,任何剩余的
project_size/2
都会告诉您需要解决的问题的基数(project seven有两个尚待确定的父/子关系),并且您可以准确地返回仍需处理的父/子关系,以及所有可以确定的
parent/2
关系

我对这个结果很满意,但希望其他人能来改进我的代码

编辑:我的代码有一个在邮件列表中发现的缺点,即即使可以计算解决方案,某些输入也无法收敛,例如:

project(1,[jane,claire],[brian, stephan]),
project(2,[jane,emma],[stephan, jones]).

有关详细信息,请参见,它使用设置交点来确定映射。

有点偏离主题,但因为:

普通序言可以看作CLP(H),其中H代表赫伯兰 条款。在这个域中,=/2和dif/2是最重要的 分别表示平等性和质量的约束 条件

我觉得有权建议一个Prolog解决方案,比您建议的算法更通用(基于单对单关系逐步减少关系):

solve2(项目、家长):-
foldl([\uPS-Cs,L,L1]>>尝试链接(Ps,Cs,L,L1),项目,[],儿童家长),
转置对(ChildrenParent,ParentsChildrenFlat),
按密钥分组(ParentsChildrenFlat,ParentsChildren)。
尝试链接([]、[]、链接、链接)。
尝试链接(Ps、Cs、Linked、Linked2):-
选择(P、Ps、Ps1),
选择(C、Cs、Cs1),
链接(C、P、链接、链接1),
尝试链接(Ps1、Cs1、Linked1、Linked2)。
链接(C、P、已分配、已分配1):-
(成员CHK(C-Q,已分配)
->P==Q,
已分配1=已分配
;赋值1=[C-P |赋值]
).
它接受自然格式的数据,如

数据(1,
[1-[jane,claire]-[brian,stephen]
,2-[claire,jane]-[emma,william]
,3-[jane,claire]-[william,james]
,4-[简,索菲亚,克莱尔][布莱恩,詹姆斯,伊莎贝拉]
,5-[claire]-[brian]
,6-[jane]-[emma]
]).
数据(2,
[1-[jane,claire]-[brian,stephen]
,2-[claire,jane]-[emma,william]
,3-[jane,claire]-[william,james]
,4-[简,索菲亚,克莱尔][布莱恩,詹姆斯,伊莎贝拉]
,5-[claire]-[brian]
,6-[jane]-[emma]
,7-[sally,sandy]-[grace,miriam]
]).
-数据(2,Ps),数据2(Ps,S)。
Ps=[1-[jane,claire]-[brian,stephen]、2-[claire,jane]-[emma,william]、3-[jane,claire]-[william,james]、4-[jane,sophia,claire]-[brian,james,isabella]、5-[claire]-[brian]、6-[jane]-[emma]、7-[grace |]-[,
S=[claire-[william,brian],jane-[james,emma,stephen],sally-[grace],sandy-[miriam],sophia-[isabella]]。

我必须承认我无法理解您的数据。可以
project(1,[jane,claire],[brian, stephan]),
project(2,[jane,emma],[stephan, jones]).