在prolog中解决谜语

时间:2016-12-03 16:06:52

标签: prolog zebra-puzzle

我想在prolog中解决这个谜语:

  

学生Lily,Jack和Daisy去了同一所大学。他们都来自不同的国家,有不同的爱好。他们都去了美国的一所大学,其中一所大学就住在那里。 Lily比来自意大利的成绩更好。杰克比那些喜欢看书的人有更好的成绩。最好的成绩是喜欢足球的人。杰克来自德国,戴西喜欢做饭。

     

谁是谁(姓名,国家,爱好,成绩)?

正确的解决方案应该是:

  • Lily,USA,Reading Books,Grade 2
  • 杰克,德国,足球,一年级
  • Daisy,Italy,Cooking,Grade 3

我现在面临的问题是,我不知道如何解决这个谜语。我该如何定义事实以及解决这个谜题的最佳方法是什么?

2 个答案:

答案 0 :(得分:0)

首先,从填写第一个陈述中得到的内容,我们有以下内容。

(Lily, _, _, _)
(Jack,Germany, _, _)
(Daisy, _, Cooking, _)

我们不了解_州的情况。我还应该说,这不一定是序言,它比任何事情更常见。

我们得到的短语"莉莉比来自意大利的人有更好的成绩",这意味着黛西来自德国,莉莉来自美国 - 自杰克来自德国。 / p>

(Lily, USA, _, Grade>Daisy)
(Jack,Germany, _, _)
(Daisy, Italy, Cooking, Grade<Lily)

接下来,我们有了#41;杰克的成绩比那些喜欢读书的人更好,这让我们知道他会成为足球运动员,下一行告诉我们他的成绩最好。然后我们可以迅速填写剩余的,我们得到:

(Lily, USA, Reading, Grade2)
(Jack,Germany, Football, Grade1)
(Daisy, Italy, Cooking, Grade3)

可能有一个用prolog编写的程序可以以非常迂回的方式解决这个难题,但这个难题比一般情况更具体。

答案 1 :(得分:0)

这是我的看法。它本质上是@RdR所具有的,只是我将逻辑分解为更多的谓词,也是为了减少重载who()主谓词。

name(lily).  % (1)
name(jack).
name(daisy).
country(italy).
country(usa).
country(germany).
hobby(football).
hobby(cooking).
hobby(reading).
grade(1).
grade(2).
grade(3).

student(N,C,H,G):-  % (2)
    name(N), country(C), hobby(H), grade(G).

permute(P,X,Y,Z):- (4)
    call(P,X), call(P,Y), call(P,Z)  % (6)
    , X\=Y, Y\=Z, X\=Z.

students(A,B,C):- (3)
    permute(name,N1,N2,N3)   % (5)
    , permute(country,C1,C2,C3)
    , permute(hobby,H1,H2,H3)
    , permute(grade,G1,G2,G3)
    , A = student(N1,C1,H1,G1)  % (7)
    , B = student(N2,C2,H2,G2)
    , C = student(N3,C3,H3,G3)
    .

who(A,B,C):-  % (8)
    students(A,B,C)
    , A = student(lily,C1,H1,G1)  % (9)
    , B = student(jack,C2,H2,G2)
    , C = student(daisy,C3,H3,G3)
    , C2 = germany                % (10)
    , H3 = cooking
    , (( C2=italy -> G1 < G2)     % (11)
      ;( C3=italy -> G1 < G3))
    , (( H1=reading -> G2 < G1)
      ;( H3=reading -> G2 < G3))
    , (( H1=football -> G1 < G2, G1 < G3)
      ;( H2=football -> G2 < G1, G2 < G3)
      ;( H3=football -> G3 < G1, G3 < G2))
    .

% Running it:
% ?- who(A,B,C).
% A = student(lily, usa, reading, 2),
% B = student(jack, germany, football, 1),
% C = student(daisy, italy, cooking, 3) ;
% false.

讨论

所以这里有很多内容(这引起了我的兴趣),并且可以做出不同的选择(因此它可能与@ RdR&#39的解决方案形成鲜明对比)。

  • 正如其他人指出的一个方面是如何编码信息 在问题描述中给出。你可以非常具体地表达它(解决 只是这种情况),或更一般(例如允许将问题扩展到 超过3名学生)。
  • 这个问题与其他类似问题的不同之处在于你有一个混合 影响任何一个学生的约束(&#34;杰克来自 德国&#34;)影响两个学生(&#34;莉莉的成绩优于来自 意大利&#34;),或涉及他们所有人(&#34;一个喜欢的足球是最好的 等级&#34;。)
  • 此外,你有分离约束(&#34;他们所有来自不同 contries,并有不同的爱好&#34;)。 Prolog很擅长经历 事实的所有可能实例,但拥有它更复杂 选择一个实例并将其留下以供下一次谓词调用。 这迫使您找到一种从事实中获取一组值的方法 成对不同。 (例如当Prolog为莉莉的业余爱好而阅读时, 它也不能把阅读作为杰克的爱好。
  • 因此,在列出所有已知事实及其可能的值(1)后,我首先 定义谓词student/4(2)来简单地说明学生有这些 4个属性。这产生了所有可能的学生和他们的组合 属性,也允许它们都具有相同的名称,来自 同一个国家,asf。
  • 如何在Prolog中创建结果集也是一个很好的例子 大,然后尝试进一步缩小(就像别人写的那样)。 进一步的谓词可以利用这个&#34;生成器&#34;并过滤越来越多 结果集中的解决方案。在每个舞台上,这也更容易测试 可以检查中间输出是否有意义。
  • 在下一个谓词students/3(3)中,我完全按照我提到的内容进行尝试 之前,创建至少不使用相同内容的学生实例 属性两次(就像两个学生有相同的爱好)。要实现这一点 必须运行我的所有属性事实(名称/ 1,国家/ 1,...),得到 每个都有三个值,并确保它们是成对不同的。
  • 不必为每个属性明确地执行此操作 除了名称之外,实现总是相同的 属性,我构造了一个我可以传递的辅助谓词permute/4(4) 属性名称,它会将属性作为事实查找三次, 并确保绑定值都不相同。
  • 因此,当我在permute(name,N1,N2,N3)(5)中致电students/3时,会导致 查找call(P,X), call(P,Y), call(P,Z)(6),结果与...相同 调用name(X), name(Y), name(Z)。 (因为我正在收集3个不同的值 从总共3个具有相同属性的事实来看,这实际上与进行3个排列相同 一个3值集,因此是辅助谓词的名称。)
  • 当我到达(7)时,我知道每个学生属性都有不同的值, 我只是在三个学生实例中分发它们。 (这应该 实际上在没有student/4谓词的情况下工作相同 在Prolog中即时编制这样的结构化术语。拥有student 谓词提供额外的好处,检查没有愚蠢的学生 可以构建,如学生(百合,23,asdf,-7.4)&#34;。)
  • 所以:- students(A,B,C).产生了3名学生的所有可能组合 他们的属性,没有使用任何涉及的属性两次。尼斯。 它还将(更难的)student()结构包装得很方便 单字母变量,使界面更清晰。
  • 但除了那些不相交的限制,我们还没有实施任何一项 另一个约束。这些现在跟随(不那么优雅)who/3 谓词(8)。它基本上使用students/3作为生成器并尝试 通过添加更多约束来过滤掉所有不需要的解决方案(因此它有 与students/3基本相同的签名。)
  • 现在另一个有趣的部分开始了,因为我们不仅要过滤 单个student实例,但也可以单独引用它们 (&#34; Daisy&#34;,&#34; Jack&#34;,...)和他们各自的属性(&#34; Daisy的爱好&#34; 等等。)。因此,在绑定我的结果变量A,B和C时,我进行了模式匹配 特别的名字。因此,(9)中的文字名lilyjack asf。这个 让我免于考虑Lily可能先进入的情况,或者作为 第二个,或第三个(因为students/3会产生这样的排列)。所以 所有不能按顺序进入的3组学生都将被丢弃。
  • 我可以稍后在像N1 = lily asf这样的显式约束中完成此操作。我这样做现在执行杰克来自德国的简单事实 和雏菊喜欢烹饪(10)。当那些失败的Prolog回溯到了 初次调用students/3,以获得可以尝试的另一组学生。
  • 现在按照关于莉莉等级,杰克等级的其他已知事实进行操作 足球爱好者的成绩(11)。这是特别难看的代码。
  • 首先,拥有能够返回的辅助谓词会很好 查询的答案&#34;具有属性X&#34;的学生。这需要 当前选择的学生,A,B和C,属性名称(&#39; country&#39;)和a 价值(&#39;意大利&#39;)并将返回适当的学生。所以你可以 查询来自意大利的学生,而不是假设它必须是第二个 或者第三个学生(因为问题描述表明莉莉本人 不是来自意大利)。
  • 所以这个假设的助手谓词,让我们称之为student_from_attribute 需要另一个帮助器,通过名称在学生结构中找到一个值 并返回相应的值。易于支持某些语言的所有语言 一种对象/命名元组/记录,您可以在其中访问其中的字段 名称。但香草Prolog没有。所以有一些提升要做, 我无法脱离头顶的东西。
  • 同样,who/3谓词可以像你一样利用这个其他助手 需要从学生返回的不同属性 student_from_attribute,就像&#39;等级&#39;,并将其与Lily的成绩进行比较。 这会使所有这些限制更好,比如 student_from_attribute([A,B,C], country, italy, S), attrib_by_name(S, grade, G), G1 < G。这可以以相同的方式应用于阅读和足球 约束。这不会更短,但更清洁,更通用。

我不确定有人会读到这一切:-)。无论如何,这些考虑让我感到很有趣。