【问题标题】:Solve riddle in prolog解决prolog中的谜语
【发布时间】:2016-12-03 16:06:52
【问题描述】:

我想在prolog中解开这个谜题:

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

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

正确的解决方案应该是:

  • Lily,美国,读书,二年级
  • 杰克,德国,足球,一年级
  • Daisy,意大利,烹饪,三年级

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

【问题讨论】:

    标签: prolog zebra-puzzle


    【解决方案1】:

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

    这意味着,首先,您需要一些包含可能国家、可能爱好和可能成绩的条款。并非所有可能性都是必要的,因为有些可能性已经被问题排除了。

    下面的解决方案,基于任意将 Lily 人 1、Jack 人 2 和 Daisy 人 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)).
    

    【讨论】:

    • 谢谢,但我不知道说H1=reading还是C3=italy是否正确,因为Prolog应该解决这个难题。还有没有其他可能?
    • 当您查询 prolog 时,它会为您的查询中的变量找到答案。所以,使用下面的queru
    • 当您在 Prolog 中查询时,它会找到变量的答案。因此,在 Prolog 中输入以下查询:
    • 抱歉,stackoverflow 不断添加我似乎无法删除的半成品 cmets。正如我所说,Prolog 找到变量的答案。使用下面的查询who(lily,C,H,G, jack,germany,_,_, daisy,_,cooking,_),它可以找到莉莉的国家、爱好和等级的答案。下划线的意思是“我不关心那个变量”。
    【解决方案2】:

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

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

    _ 表示我们不知道的地方。我还应该说,这不一定是序言,它比任何东西都更常识。

    我们得到短语“Lily 的成绩比来自意大利的那个更好”,这意味着 Daisy 来自德国,Lily 来自美国——因为 Jack 来自德国。

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

    接下来,我们有“杰克的成绩比喜欢读书的人好”,这让我们知道他会成为足球运动员,下一行告诉我们他的成绩最好。然后我们可以及时补足剩余的,我们得到:

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

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

    【讨论】:

    • 您刚刚表明您可以在脑海中完成 Prolog 的工作 :-)。
    【解决方案3】:

    这是我的看法。它本质上是 @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 名以上学生)。
    • 这个问题与其他类似问题的不同之处在于,你有一个混合 影响单个学生的约束(“杰克来自 德国”),影响两名学生(“莉莉的成绩比来自 意大利”),或涉及所有人(“喜欢足球的人最好 成绩”)。
    • 此外,您还有析取约束(“它们是 all 国家,并且有不同的爱好”)。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) 时,它会导致 查找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 谓词提供了额外的好处,即检查没有愚蠢的学生 可以构造,例如“student(lily, 23, asdf, -7.4)”。)
    • 所以:- students(A,B,C). 产生了 3 个学生的所有可能组合,并且 他们的属性,没有使用任何涉及的属性两次。好的。 它还方便地包装了(更难的)student() 结构 使界面更加简洁的单字母变量。
    • 但除了那些不相交的约束,我们还没有实施任何 另一个约束。这些现在遵循(不太优雅)who/3 谓词 (8)。它基本上使用students/3 作为生成器并尝试 通过添加更多约束过滤掉所有不需要的解决方案(因此它有 与students/3基本相同的签名。)
    • 现在另一个有趣的部分开始了,因为我们不仅要能够过滤 单独的student 实例,也可以单独引用它们 (“Daisy”,“Jack”,...)及其各自的属性(“Daisy's hobby” 等等。)。因此,在绑定我的结果变量 A、B 和 C 时,我进行模式匹配 特定的名称。因此,来自 (9) 的文字名称 lily、jack asf。这 让我不必考虑莉莉可能先出现的情况,或者作为 第二,或第三(因为students/3 会产生这样的排列)。所以 所有 3 组不按此顺序来的学生都将被丢弃。
    • 我也可以稍后在像N1 = lily asf 这样的显式约束中这样做。我现在这样做是为了执行杰克来自德国的简单事实 黛西喜欢烹饪 (10)。当这些失败时,Prolog 会回溯到 首次致电students/3,以获取可以尝试的另一组学生。
    • 现在关注关于 Lily 成绩、Jack 成绩和 足球爱好者的成绩 (11)。这是特别难看的代码。
    • 首先,最好有能够返回的辅助谓词 查询“具有属性 X 的学生”的答案。这将采取 当前选择的学生 A、B 和 C、属性名称(“国家”)和 value ('italy') 并返回相应的学生。所以你可以 查询来自意大利的学生,而不是假设它必须是第二个 或者第三个学生(正如问题描述所暗示的那样,莉莉本人 不是来自意大利)。
    • 所以这个假设的辅助谓词,我们称之为student_from_attribute 将需要另一个帮助器来按名称在学生结构中查找值 并返回相应的值。在所有支持某些语言的语言中都很容易 一种对象/命名元组/记录,您可以通过以下方式访问其中的字段 姓名。但香草 Prolog 没有。所以会有一些提升要做, 一些我无法摆脱的东西。
    • who/3 谓词也可以利用这个其他助手,因为您 需要与返回的学生不同的属性 student_from_attribute,例如“等级”,并将其与莉莉的等级进行比较。 这将使所有这些约束变得更好,比如 student_from_attribute([A,B,C], country, italy, S), attrib_by_name(S, grade, G), G1 < G。这可以以相同的方式应用于阅读和足球 约束。这不会更短,而是更简洁、更通用。

    我不确定是否有人会阅读所有这些内容:-)。不管怎样,这些考虑让我觉得这个谜题很有趣。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-22
      • 1970-01-01
      • 2016-08-17
      相关资源
      最近更新 更多