了解 clpfd 中 label/5 的实现
Understanding the implementation of label/5 in clpfd
我正在尝试了解 clpfd
库中 label/5 谓词的 实现 (我了解其用法):
1824label([], _, _, _, 一致性) :- !,
1825 ( 一致性 = upto_in(I0,I) -> I0 = I
1826 年;真的
1827 年)。
1828label(变量、选择、顺序、选择、一致性):-
第1829章
1830 年; select_var(选择,Vars,Var,RVars),
第 1831 章
1832 ( 一致性 = upto_in(I0,I), fd_get(Var, _, Ps), all_dead(Ps) - >
1833 fd_size(变量,大小),
1834 I1 是 I0*大小,
1835 标签(RVars,选择,顺序,选择,upto_in(I1,I))
1836 年;一致性 = upto_in, fd_get(Var, _, Ps), all_dead(Ps) ->
1837 标签(RVars、选择、顺序、选择、一致性)
1838 年; choice_order_variable(选择、顺序、Var、RVars、Vars、选择、一致性)
1839)
1840 年;标签(RVars、选择、顺序、选择、一致性)
1841)
1842 年)。
特别是标记的部分(显然是重要的部分)让我很困惑:
- 我不太确定
fd_get
(/3
或 /5
)的作用
all_dead
使用某种 propagator
(我还没有研究过)
- 这个“枚举”如何
Var
?
我显然遗漏了可能需要理解的证明机制的一些组成部分,所以我很好奇关于这个行为的一些更高层次的解释(可能有一些资源可供阅读)实施。
免责声明:以下答案只是我在浏览代码并在 SWISH 中玩了一下后的有根据的猜测。
首先,整个标记部分及其下面的两行(即第 1836-1837 行)似乎与 labeling/2
的未记录选项有关,我将其称为 upto_in
(在仿函数的名称之后)。我将尝试解释我认为此选项的作用。
通常,当调用 labeling/2
时,所有的 FD 变量(在其第二个参数中)都是基于成功的。这确实是标签所做的:一个接一个地分配变量,直到它们都被分配。例如,在下面的代码片段中,labeling/2
在 X
和 Y
地面上成功了 6 次:
?- [X,Y] ins 1..4, X #< Y, labeling([],[X,Y]).
X = 1, Y = 2 ;
X = 1, Y = 3 ;
X = 1, Y = 4 ;
X = 2, Y = 3 ;
X = 2, Y = 4 ;
X = 3, Y = 4
作为比较,当不使用 labeling/2
时,我们可以看到两个变量的(减少的)域以及约束仍然悬而未决的事实。
?- [X,Y] ins 1..4, X #< Y.
X in 1..3, X #=< Y + -1, Y in 2..4
当约束未决时,这意味着变量值的每个组合(在本例中为 X
和 Y
)可能是也可能不是解决方案。相反,如果没有待定约束,那么我们知道值的每个组合都是一个解决方案。在某些应用程序中,当我们确定所有值组合都是解决方案时,人们可能会考虑停止标记。简而言之,这就是选项 upto_in
的作用:它告诉您在没有约束未决时避免标记。回到我们的 运行 示例,这显示为:
?- [X,Y] ins 1..4, X #< Y, labeling([upto_in],[X,Y]).
X = 1, Y in 2..4 ;
X = 2, Y in 3..4 ;
X = 3, Y = 4
所以第一个解决方案意味着对于 X = 1
,Y
可以取从 2 到 4 的所有值。
请注意,upto_in
有两种形式,第一种如上所示,第二种带有参数,表示生成的答案中包含的地面解决方案的数量。所以:
?- [X,Y] ins 1..4, X #< Y, labeling([upto_in(K)],[X,Y]).
X = 1, Y in 2..4, K = 3 ;
X = 2, Y in 3..4, K = 2 ;
X = 3, Y = 4, K = 1
作为另一个例子,如果删除唯一的约束,我们会看到(基础)解决方案的数量是域的笛卡尔积(并且没有实际标记发生)。
?- [X,Y] ins 1..4, labeling([upto_in(K)],[X,Y]).
K = 16, X in 1..4, Y in 1..4
第二个选项可能很有用,例如当一个人想要计算一个问题的解决方案的数量时。然后 upto_in/1
允许人们快捷地标记“不相关”变量以获得更好的性能,同时跟踪实际解决方案的数量。
现在,回答更具体的问题:
fd_get(V,D,Ps)
变量 V
的“returns”:它的当前域 D
和结构 Ps
与(3 个列表)传播者相关联给它。传播器基本上是发布约束的内部实现,其作用是从域中删除不可能的值。在上面的示例中,Ps
将包含(表示?)不等式约束的传播者,并且该传播者将在所有情况下从 Y
和 [ 的域中删除 1
=36=] 来自 X
的域。 fd_get/5
returns另外域的上下界
all_dead/1
似乎在检查与变量关联的所有传播子是否都“死了”,这意味着在这种情况下,出现在其中的变量域中的所有值组合都是解决方案。 (我说“似乎”是因为这部分内容确实触及了 clpfd
库的内部结构,我不想深入挖掘。)
- 它并没有真正列举
Var
。为了解释标记代码在句子中的作用,我会这样写:“如果使用了 upto_in
选项并且没有约束可以进一步减少 Var
的域,则跳过枚举 Var
(并将某个累加器乘以其域的大小)。"
希望这有助于您的理解。如果更有知识的人在我的解释中发现错误或漏洞,请随时修复它们。
我正在尝试了解 clpfd
库中 label/5 谓词的 实现 (我了解其用法):
1824label([], _, _, _, 一致性) :- !, 1825 ( 一致性 = upto_in(I0,I) -> I0 = I 1826 年;真的 1827 年)。 1828label(变量、选择、顺序、选择、一致性):- 第1829章 1830 年; select_var(选择,Vars,Var,RVars), 第 1831 章 1832 ( 一致性 = upto_in(I0,I), fd_get(Var, _, Ps), all_dead(Ps) - > 1833 fd_size(变量,大小), 1834 I1 是 I0*大小, 1835 标签(RVars,选择,顺序,选择,upto_in(I1,I)) 1836 年;一致性 = upto_in, fd_get(Var, _, Ps), all_dead(Ps) -> 1837 标签(RVars、选择、顺序、选择、一致性) 1838 年; choice_order_variable(选择、顺序、Var、RVars、Vars、选择、一致性) 1839) 1840 年;标签(RVars、选择、顺序、选择、一致性) 1841) 1842 年)。
特别是标记的部分(显然是重要的部分)让我很困惑:
- 我不太确定
fd_get
(/3
或/5
)的作用 all_dead
使用某种propagator
(我还没有研究过)- 这个“枚举”如何
Var
?
我显然遗漏了可能需要理解的证明机制的一些组成部分,所以我很好奇关于这个行为的一些更高层次的解释(可能有一些资源可供阅读)实施。
免责声明:以下答案只是我在浏览代码并在 SWISH 中玩了一下后的有根据的猜测。
首先,整个标记部分及其下面的两行(即第 1836-1837 行)似乎与 labeling/2
的未记录选项有关,我将其称为 upto_in
(在仿函数的名称之后)。我将尝试解释我认为此选项的作用。
通常,当调用 labeling/2
时,所有的 FD 变量(在其第二个参数中)都是基于成功的。这确实是标签所做的:一个接一个地分配变量,直到它们都被分配。例如,在下面的代码片段中,labeling/2
在 X
和 Y
地面上成功了 6 次:
?- [X,Y] ins 1..4, X #< Y, labeling([],[X,Y]).
X = 1, Y = 2 ;
X = 1, Y = 3 ;
X = 1, Y = 4 ;
X = 2, Y = 3 ;
X = 2, Y = 4 ;
X = 3, Y = 4
作为比较,当不使用 labeling/2
时,我们可以看到两个变量的(减少的)域以及约束仍然悬而未决的事实。
?- [X,Y] ins 1..4, X #< Y.
X in 1..3, X #=< Y + -1, Y in 2..4
当约束未决时,这意味着变量值的每个组合(在本例中为 X
和 Y
)可能是也可能不是解决方案。相反,如果没有待定约束,那么我们知道值的每个组合都是一个解决方案。在某些应用程序中,当我们确定所有值组合都是解决方案时,人们可能会考虑停止标记。简而言之,这就是选项 upto_in
的作用:它告诉您在没有约束未决时避免标记。回到我们的 运行 示例,这显示为:
?- [X,Y] ins 1..4, X #< Y, labeling([upto_in],[X,Y]).
X = 1, Y in 2..4 ;
X = 2, Y in 3..4 ;
X = 3, Y = 4
所以第一个解决方案意味着对于 X = 1
,Y
可以取从 2 到 4 的所有值。
请注意,upto_in
有两种形式,第一种如上所示,第二种带有参数,表示生成的答案中包含的地面解决方案的数量。所以:
?- [X,Y] ins 1..4, X #< Y, labeling([upto_in(K)],[X,Y]).
X = 1, Y in 2..4, K = 3 ;
X = 2, Y in 3..4, K = 2 ;
X = 3, Y = 4, K = 1
作为另一个例子,如果删除唯一的约束,我们会看到(基础)解决方案的数量是域的笛卡尔积(并且没有实际标记发生)。
?- [X,Y] ins 1..4, labeling([upto_in(K)],[X,Y]).
K = 16, X in 1..4, Y in 1..4
第二个选项可能很有用,例如当一个人想要计算一个问题的解决方案的数量时。然后 upto_in/1
允许人们快捷地标记“不相关”变量以获得更好的性能,同时跟踪实际解决方案的数量。
现在,回答更具体的问题:
fd_get(V,D,Ps)
变量V
的“returns”:它的当前域D
和结构Ps
与(3 个列表)传播者相关联给它。传播器基本上是发布约束的内部实现,其作用是从域中删除不可能的值。在上面的示例中,Ps
将包含(表示?)不等式约束的传播者,并且该传播者将在所有情况下从Y
和 [ 的域中删除1
=36=] 来自X
的域。fd_get/5
returns另外域的上下界all_dead/1
似乎在检查与变量关联的所有传播子是否都“死了”,这意味着在这种情况下,出现在其中的变量域中的所有值组合都是解决方案。 (我说“似乎”是因为这部分内容确实触及了clpfd
库的内部结构,我不想深入挖掘。)- 它并没有真正列举
Var
。为了解释标记代码在句子中的作用,我会这样写:“如果使用了upto_in
选项并且没有约束可以进一步减少Var
的域,则跳过枚举Var
(并将某个累加器乘以其域的大小)。"
希望这有助于您的理解。如果更有知识的人在我的解释中发现错误或漏洞,请随时修复它们。