解开序言中的谜语



我想在序言中解开这个谜:

学生莉莉、杰克和黛西上同一所大学。他们都来自不同的国家,有不同的爱好。他们都在美国的一所大学上学,其中一人住在那里。莉莉的成绩比来自意大利的那个好。杰克的成绩比喜欢看书的那个好。成绩最好的是喜欢足球的人。杰克来自德国,黛西喜欢做饭。

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

正确的解决方案应该是:

  • 莉莉,美国,阅读书籍,二年级
  • Jack,德国,足球,一年级
  • Daisy,意大利,烹饪,三年级

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

在Prolog中回答这些难题的诀窍是生成(检索)可能的答案,然后根据逻辑约束对其进行测试。因此,如果Lily是P1,那么检索任何P2,并测试该人是否来自意大利。其他规则也是如此。

这意味着,在第一个例子中,你需要一些可能的国家、可能的爱好和可能的成绩的条款。并不是所有的可能性都是必要的,因为有些可能性已经被这个问题排除了。

下面的解决方案,基于任意制作莉莉人1、杰克人2和黛西人3。

加载到Prolog并查询谁(P1、C1、H1、G1、P2、C2、H2、G2、P3、C3、H3、G3)。

country(italy).
country(usa).
hobby(football).
hobby(reading).
grade(c:1).
grade(b:2).
grade(a:3).

who(lily,C1,H1,Grade1, jack,germany,H2,Grade2, daisy,C3,cooking,Grade3):-
country(C1), country(C3), C1 = C3,
hobby(H1), hobby(H2), H1 = H2,
grade(G1:Grade1), grade(G2:Grade2), grade(G3:Grade3),
G1 = G2, G2 = G3, G1 = G3,
(C3=italy, G1@>G3),
(H1=reading, G2@>G1),
((H1=football, G1@>G2, G1@>G3); (H2=football, G2@>G1, G2@>G3)).

首先,通过填写我们从第一条语句中得到的内容,我们得到了以下内容。

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

_状态下,我们不知道什么。我还应该说,这不一定是开场白,它比任何事情都更有常识。

我们得到的短语是"莉莉的成绩比来自意大利的好",这意味着黛西来自德国,莉莉来自美国——因为杰克来自德国。

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

接下来,我们有"杰克的成绩比喜欢读书的人好",这让我们知道他将成为足球运动员,下一行告诉我们他成绩最好。然后我们可以立即填充剩余的,我们得到:

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

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

这是我的看法。这本质上就是@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的解决方案形成了很好的对比)。

  • 正如其他人所指出的,一个方面是如何对在问题描述中给出。你可以非常具体地表达(解决只是这种情况),或者更一般(例如允许将问题扩展到超过3名学生)
  • 这个问题与其他类似问题的不同之处在于影响单个学生("Jack来自德国),影响了两名学生("莉莉的成绩比来自意大利"),或者涉及到他们所有人("喜欢足球的人最好等级")
  • 此外,你有析取约束("它们来自不同的懊悔,并且有不同的爱好")。Prolog非常善于经历一个事实的所有可能的例子,但拥有它更复杂选择一个实例,并将该实例留给谓词的下一次调用。这迫使您找到一种方法,从以下事实中获得一组值成对地不同。(例如,当Prolog满足于Lily的阅读爱好时,不能把读书也当成杰克的爱好)
  • 所以在列出所有已知的事实及其可能的值之后(1)我首先定义了谓词student/4(2),以简单地说明学生具有4个属性。这就产生了学生和他们属性,也允许它们都有相同的名称,来自同一个国家,asf
  • 这是一个很好的例子,如何在Prolog中创建一个结果集大,然后试着把它缩小(就像其他人写的那样)。进一步的谓词可以越来越多地使用这个"生成器"和过滤器结果集中的解决方案。这也更容易测试,在每个阶段可以检查中间输出是否有意义
  • 在下一个谓词students/3(3)中,我尝试了我提到的内容早些时候,创建至少不使用相同的学生实例两次归因(就像两个学生有相同的爱好)。为了实现这一点,我必须浏览我的所有属性事实(名称/1,国家/1,…),获取每个值三个,并确保它们成对不同
  • 不必对其中除了属性,我构造了一个可以传递的辅助谓词permute/4(4)属性名称,它将把该属性作为事实查找三次,并确保绑定值不完全相同
  • 因此,当我在students/3(5)中调用permute(name,N1,N2,N3)时,它将导致查找CCD_ 8(6)的结果与调用CCD_ 9。(我正在收集3个不同的值从总是具有相同属性的3个事实来看,这实际上与进行3个术语相同一个3值集的,因此有了辅助谓词的名称。)
  • 当我到达(7)时,我知道我对每个学生属性都有不同的值,我只是将它们分布在三个学生实例中。(这应该实际上,在没有student/4谓词的情况下也可以正常工作在Prolog中编写这样的结构化术语。拥有student谓词提供了额外的好处,可以检查没有愚蠢的学生可以构造,比如"student(lily,23,asdf,-7.4)"。)
  • 因此:- students(A,B,C).产生了3个学生和它们的属性,而不使用任何涉及的属性两次。美好的它还方便地封装了(更困难的)student()结构单字母变量,使界面更加简洁
  • 但除了这些脱节的限制之外,我们还没有实现任何另一个约束。这些现在跟随在(不那么优雅的)who/3中谓词(8)。它基本上使用students/3作为生成器,并尝试通过添加更多约束过滤掉所有不需要的解决方案(因此基本上与students/3相同的签名。)
  • 现在,另一个有趣的部分开始了,因为我们不仅要能够过滤单个student实例,但也可以单独引用它们("Daisy"、"Jack"…)和他们各自的属性("Daily的爱好"因此,在绑定我的结果变量A、B和C时,我在特定名称。因此,文字名称lilyjack来自(9)。这让我不用考虑莉莉可能先来的情况,或者如students/3将生成这样的排列)。所以所有不按顺序来的3组学生都会被丢弃
  • 我也可以稍后在N1 = lilyasf这样的显式约束中完成此操作。我现在这样做是为了证明杰克来自德国这一简单事实黛西喜欢做饭。当这些失败时,Prolog回溯到最初呼叫students/3,以获得它可以尝试的另一组学生
  • 现在,了解关于Lily的成绩、Jack的成绩以及足球爱好者的成绩(11)。这是一个特别难看的代码
  • 首先,如果有一个能够返回的helper谓词,那就太好了查询"具有属性X的学生"的答案。这需要学生的当前选择,A、B和C,一个属性名称("国家")和一个value('taly'),并返回合适的学生。所以你可以询问来自意大利的学生,而不是假设它一定是第二个或者第三个学生(正如问题描述所示,莉莉本人不是来自意大利)
  • 所以这个假设的辅助谓词,我们称之为student_from_attribute将需要另一个助手来按名称在学生结构中查找值并返回相应的值。在支持某些语言的所有语言中都很容易对象/命名元组/记录的类型,您可以通过以下方式访问其中的字段名称但香草Prolog没有。因此,需要进行一些提升,我无法从头顶上摘下的东西
  • 此外,who/3谓词可以利用这个其他辅助对象,因为您需要与返回的学生不同的属性student_from_attribute,喜欢"等级",并将其与Lily的等级进行比较。这会让所有这些限制变得更好,比如CCD_ 26。这同样适用于阅读和足球限制这不会更短,但更干净、更普遍

我不确定有人会读到这一切:-)。不管怎样,这些考虑因素让这个谜题对我来说很有趣

最新更新