【问题标题】:How to create arithmetic and disequality constraints in Prolog如何在 Prolog 中创建算术和不等式约束
【发布时间】:2016-06-28 15:50:03
【问题描述】:

我是 Prolog 的新手,我有兴趣将以下单词问题转换为 (SWI) Prolog:

有 4 个孩子:Abe、Dan、Mary 和 Sue。他们的年龄,排名不分先后,分别是 3、5、6 和 8 岁。Abe 比 Dan 大。苏比玛丽年轻。苏的年龄是丹的年龄加上 3 岁。 Mary 比 Abe 大。

到目前为止,我想出了

child(X) :-
    member(X, [3,5,6,8]).

solution(Abe, Dan, Mary, Sue) :-
    child(Abe),
    child(Dan),
    child(Mary),
    child(Sue),
    Abe > Dan,
    Sue < Mary,
    Sue == Dan+3,
    Mary > Abe,
    Abe \== Dan,
    Abe \== Mary,
    Abe \== Sue,
    Dan \== Mary,
    Dan \== Sue,
    Mary \== Sue.

但是运行查询

?- solution(Abe, Dan, Mary, Sue)

我刚收到false。作为一个附带问题,Prolog 会执行蛮力搜索来寻找解决方案,还是有一些机器可以比 O(n!) 更好地解决这个(某种)问题?

我想要的结果是Abe = 5, Dan = 3, Mary = 9, Sue = 6

【问题讨论】:

  • 你必须写 Sue =:= Dan+3 而不是 Sue == Dan+3
  • Sue == Dan+3 不会执行算术运算。为此,您需要Sue =:= Dan+3
  • 好的,那么问题实例在哪里呢?我很想见到他们!

标签: prolog clpfd


【解决方案1】:

整数的算术约束,例如这个谜题中的约束,最好用 Prolog 系统的 CLP(FD) 约束来表达。

例如,在 SICStus Prolog、YAP 或 SWI 中:

:- 使用模块(库(clpfd))。 年龄(作为):- As = [Abe,Dan,Mary,Sue], % 有 4 个孩子:Abe, Dan, Mary, Sue 如 ins 3\/5\/6\/8, % 他们的年龄分别是 3、5、6 和 8 all_different(As), Abe #> Dan, % Abe 比 Dan 年长 Sue #安倍。 % Mary 比 Abe 大

示例查询及其结果:

?- 年龄([Abe,Dan,Mary,Sue])。 安倍 = 5, 丹 = 3, 玛丽 = 8, 苏 = 6。

我们从这个答案中看到,这个谜题有一个独特的解决方案。

请注意,无需任何搜索才能获得此答案!约束求解器通过称为约束传播的强大隐式机制推导出唯一解,这是 CLP 系统优于蛮力搜索的关键优势。在此示例中成功使用约束传播来修剪除搜索树的一个剩余分支之外的所有分支。

【讨论】:

  • 在查找 clpfd 并随后发现 swi-prolog.org/man/clpqr.html 之后,我有几个小问题也许你可以回答。 1) CLP(Q,R) 是 CLP(FD) 的超集 - CLP(Q,R) 也可以有效地解决这个问题,还是严格使用整数是一个有用的约束? 2) 我发现 CLP(FD) 用于处理整数,但字母 FD 实际代表什么?
  • 虽然使用 CLP(Q,R) 来解决这个(更准确,相似但更大)的问题会带来性能损失吗?
  • @Brent。 fd = "有限域"。是也不是,clpqr 可以处理整数和非整数(有理数和浮点数),但主要适用于可通过高斯消元法和单纯形法求解的线性方程组和线性不等式系统“线性规划”方法。也提供了 MIP(混合整数线性规划),但全整数组合问题的首选武器是 clpfd,而不是 clpqr
  • @Brent。好问题!提供一些问题实例,我们可以试一试。我认为 clpfd 通常会(以很大的优势)解决此类问题。
  • @Brent:我同意重复所说的一切。此外,我认为您的每个问题都值得单独提出。你会得到几个很好的答案。
【解决方案2】:

answer by @WillemVanOnsem——使用低级算术生成和测试——是老派

相比之下, @mat's code 在通用性/多功能性/稳健性、声明性、简洁性、效率等方面胜出!怎么会?运气?天才?神干预?可能每个都有一点,但主要原因是:@mat 使用高级工具。 @mat 使用

好消息! 普遍可用。使用它并获得好处:

  • 注意@mat 的 Prolog 代码和原始规范有多接近!

  • 代码保留。这会产生重要的后果:

    • 可以使用高级调试方法(利用纯 Prolog 代码的有用代数属性)!

    • 可以进行低级调试, 但要先探索高级方法!

【讨论】:

  • 我真的很喜欢置换函数的使用,因为它非常类似于我的问题的内部模型。您能简要解释一下 maplist 调用中发生了什么吗? #&lt; 是 maplist/3 的第一个参数吗?这是什么?
  • @Brent。稍后我将编辑我的答案,但让我从这里开始评论:maplist/3 是一个广泛可用的meta-predicate,它是一个将另一个谓词作为参数的谓词。 (#&lt;)/2 正好错过了 2 个参数。 maplist/3#&lt; 作为第一个参数,[A1,A2,A3] 作为第二个参数,[B1,B2,B3] 作为第三个参数与A1#&lt;B1, A2#&lt;B2, A3#&lt;B3 的含义相同。 (它的工作原理是两个具有相同长度的列表。在我的 SO-answer 中的查询中,我们将它与两个列表一起使用,每个列表包含 5 个项目。)
  • 这是有道理的。感谢您的澄清。
  • 在我看来,约束传播是完全避免 permutation/2O(n!) 调用的关键优势。这甚至在原始问题中被明确要求。现在它似乎不太受关注,也许将蛮力方法(使用permutation/2)与约束传播进行对比具有一定的教育价值,但总体而言,我会更加强调后者而不是前者。
  • @mat。感谢您帮助我回到自己真正要走的路!
【解决方案3】:

由于值在child 调用后接地,您可以使用is 运算符:

child(X) :-
    member(X, [3,5,6,8]).
solution(Abe, Dan, Mary, Sue) :-
    child(Abe),
    child(Dan),
    child(Mary),
    child(Sue),
    Abe > Dan,
    Sue < Mary,
    Sue is Dan+3,
    Mary > Abe,
    Abe =\= Dan,
    Abe =\= Mary,
    Abe =\= Sue,
    Dan =\= Mary,
    Dan =\= Sue,
    Mary =\= Sue.

您可以通过交错生成和测试来进一步提高性能,而不是先生成然后测试:

child(X) :-
    member(X, [3,5,6,8]).
solution(Abe, Dan, Mary, Sue) :-
    child(Abe),
    child(Dan),
    Abe > Dan,
    Abe =\= Dan,
    child(Mary),
    Mary > Abe,
    Abe =\= Mary,
    Dan =\= Mary,
    Sue is Dan+3,
    Sue < Mary,
    child(Sue),
    Abe =\= Sue,
    Dan =\= Sue,
    Mary =\= Sue.

那么你也可以消除一些不相等的谓词 (=\=),因为这些是由小于 (&lt;) 或大于 (&gt;) 谓词;或is 谓词:

child(X) :-
    member(X, [3,5,6,8]).
solution(Abe, Dan, Mary, Sue) :-
    child(Abe),
    child(Dan),
    Abe > Dan,
    child(Mary),
    Mary > Abe,
    Dan =\= Mary,
    Sue is Dan+3,
    Sue < Mary,
    child(Sue),
    Abe =\= Sue.

尽管如此,使用约束逻辑编程 (CLP) 工具可能是解决此问题的最佳方法。

【讨论】:

  • 为什么不在child(Sue) 之前放置Sue is Dan+3?然后,也许,将两者都移到顶部;我知道......它永远不会结束:)
猜你喜欢
  • 2015-12-30
  • 1970-01-01
  • 1970-01-01
  • 2020-03-29
  • 2019-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多