了解 clpfd 中 label/5 的实现

Understanding the implementation of label/5 in clpfd

我正在尝试了解 clpfd 库中 label/5 谓词的 实现 (我了解其用法):

(copied from here)


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 年)。

特别是标记的部分(显然是重要的部分)让我很困惑:

我显然遗漏了可能需要理解的证明机制的一些组成部分,所以我很好奇关于这个行为的一些更高层次的解释(可能有一些资源可供阅读)实施。

免责声明:以下答案只是我在浏览代码并在 SWISH 中玩了一下后的有根据的猜测。

首先,整个标记部分及其下面的两行(即第 1836-1837 行)似乎与 labeling/2 的未记录选项有关,我将其称为 upto_in (在仿函数的名称之后)。我将尝试解释我认为此选项的作用。

通常,当调用 labeling/2 时,所有的 FD 变量(在其第二个参数中)都是基于成功的。这确实是标签所做的:一个接一个地分配变量,直到它们都被分配。例如,在下面的代码片段中,labeling/2XY 地面上成功了 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

当约束未决时,这意味着变量值的每个组合(在本例中为 XY)可能是也可能不是解决方案。相反,如果没有待定约束,那么我们知道值的每个组合都是一个解决方案。在某些应用程序中,当我们确定所有值组合都是解决方案时,人们可能会考虑停止标记。简而言之,这就是选项 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 = 1Y 可以取从 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/5returns另外域的上下界
  • all_dead/1 似乎在检查与变量关联的所有传播子是否都“死了”,这意味着在这种情况下,出现在其中的变量域中的所有值组合都是解决方案。 (我说“似乎”是因为这部分内容确实触及了 clpfd 库的内部结构,我不想深入挖掘。)
  • 它并没有真正列举Var。为了解释标记代码在句子中的作用,我会这样写:“如果使用了 upto_in 选项并且没有约束可以进一步减少 Var 的域,则跳过枚举 Var(并将某个累加器乘以其域的大小)。"

希望这有助于您的理解。如果更有知识的人在我的解释中发现错误或漏洞,请随时修复它们。