如何解决';6本书';用Prolog解谜?

如何解决';6本书';用Prolog解谜?,prolog,Prolog,我刚刚在math.SE()上看到了这个问题: 六本大小相同的书(A、B、C、D、E、F)如图所示堆叠在一起 图为: 我们知道以下事实: A和D不接触 E位于两本书之间,两本书都是垂直的或水平的 C正好接触到两本书 A和F触摸 E和F沿其盖接触(长侧) 有多少本书知道它们的位置 我想我可以用Prolog解决这个问题: % Those are the books: book(a). book(b). book(c). book(d). book(e). book(f). % This is ho

我刚刚在math.SE()上看到了这个问题:

六本大小相同的书(A、B、C、D、E、F)如图所示堆叠在一起 图为:

我们知道以下事实:

  • A和D不接触
  • E位于两本书之间,两本书都是垂直的或水平的
  • C正好接触到两本书
  • A和F触摸
  • E和F沿其盖接触(长侧)
  • 有多少本书知道它们的位置

    我想我可以用Prolog解决这个问题:

    % Those are the books:
    book(a).
    book(b).
    book(c).
    book(d).
    book(e).
    book(f).
    
    % This is how 'touching' works:
    touching(X,Y):- touching(Y,X). % touching is symmetric
    touching(p1,p2).
    touching(p2,p3).
    touching(p3,p4).
    touching(p3,p5).
    touching(p3,p6).
    touching(p4,p5).
    touching(p5,p6).
    
    % List all possible positions:
    position(a):- p1,p2,p3,p4,p5,p6.
    position(b):- p1,p2,p3,p4,p5,p6.
    position(c):- p1,p2,p3,p4,p5,p6.
    position(d):- p1,p2,p3,p4,p5,p6.
    position(e):- p1,p2,p3,p4,p5,p6.
    position(f):- p1,p2,p3,p4,p5,p6.
    
    % Every position has one book
    getBook(p1) :- a,b,c,d,e,f.
    getBook(p2) :- a,b,c,d,e,f.
    getBook(p3) :- a,b,c,d,e,f.
    getBook(p4) :- a,b,c,d,e,f.
    getBook(p5) :- a,b,c,d,e,f.
    getBook(p6) :- a,b,c,d,e,f.
    
    % Add your facts:
    not(touching(position(a),position(d))).
    position(e):- p5,p2.
    % C touches exactly two books: eventually something like aggregate_all(count, touching(e,X), Count):-2.
    position(c):- p2, p4,p6.
    touching(position(a),position(f)).
    touching(position(e),position(f)).
    
    但当我尝试
    位置(a)
    时,我得到:

    ?- consult(books).
    Warning: /home/moose/Downloads/LaTeX-examples/documents/Programmierparadigmen/scripts/prolog/books.pl:37:
        Clauses of position/1 are not together in the source-file
    Warning: /home/moose/Downloads/LaTeX-examples/documents/Programmierparadigmen/scripts/prolog/books.pl:40:
        Clauses of touching/2 are not together in the source-file
    % books compiled 0.00 sec, 32 clauses
    true.
    
    ?- position(a).
    ERROR: position/1: Undefined procedure: p1/0
       Exception: (7) p1 ? 
    
    • 问题1:为什么会出现异常,如何修复
    • 问题2:有没有一种更接近文本的方法来描述序言中的事实3
    • 问题3:如何打印所有组合

      • 关键在于此。您需要使用满足特定约束的逻辑变量查找
        [1,2,3,4,5,6]
        的排列,标记为
        [A,B,C,D,E,F]
        。约束是书本接触和书本水平或垂直对齐。我们掌握的事实是

        vert(1).
        vert(2).
        vert(3).
        
        horz(4).
        horz(5).
        horz(6).
        
        以及书籍之间的一些关系,即

        touch(3, 4).
        touch(3, 5).
        touch(3, 6).
        
        touch_long(1, 2).
        touch_long(2, 3).
        touch_long(4, 5).
        touch_long(5, 6).
        
        touching(X, Y) :-
            touch(X, Y) ; touch(Y, X); touching_long(X, Y).
        
        touching_long(X, Y) :-
            touch_long(X, Y) ; touch_long(Y, X).
        
        蛮力方法(对于这样一个小问题来说足够了)就是生成置换并检查约束。这在Prolog编程中称为生成和测试方法

        % books(A, B, C, D, E, F) unifies its variables with the
        % integers 1 through 6 to meet the constraints.
        books(A, B, C, D, E, F) :-
            permutation([1, 2, 3, 4, 5, 6], [A, B, C, D, E, F]),
        
            % 1. A and D are not touching.
            \+ touching(A, D),
        
            % 2. E is between two books which are both vertical or both horizontal.
            % We can take a shortcut by exploiting the asymmetry in touch_long.
            touch_long(_, E),
            touch_long(E, _),
        
            % 3. C touches exactly two books. That means that the set of all books
            % touching C has cardinality 2.
            setof(X, touching(X, C), TouchingC),
            length(TouchingC, 2),
        
            % 4. A and F touch.
            touching(A, F),
        
            % 5. E and F touch along their cover (long side).
            touching_long(E, F).
        
        您现在可以运行
        书籍(A、B、C、D、E、F)
        生成有效排列:

        ?- books(A,B,C,D,E,F).
        A = 3,
        B = 2,
        C = 4,
        D = 1,
        E = 5,
        F = 6 ;
        A = 3,
        B = 2,
        C = 6,
        D = 1,
        E = 5,
        F = 4 
        

        等。可以通过观察输出来解决原始问题;为原始程序编写一个全自动的解决方案只是一个练习(这有点乏味)。

        这里是关键。您需要使用满足特定约束的逻辑变量查找
        [1,2,3,4,5,6]
        的排列,标记为
        [A,B,C,D,E,F]
        。约束是书本接触和书本水平或垂直对齐。我们掌握的事实是

        vert(1).
        vert(2).
        vert(3).
        
        horz(4).
        horz(5).
        horz(6).
        
        以及书籍之间的一些关系,即

        touch(3, 4).
        touch(3, 5).
        touch(3, 6).
        
        touch_long(1, 2).
        touch_long(2, 3).
        touch_long(4, 5).
        touch_long(5, 6).
        
        touching(X, Y) :-
            touch(X, Y) ; touch(Y, X); touching_long(X, Y).
        
        touching_long(X, Y) :-
            touch_long(X, Y) ; touch_long(Y, X).
        
        蛮力方法(对于这样一个小问题来说足够了)就是生成置换并检查约束。这在Prolog编程中称为生成和测试方法

        % books(A, B, C, D, E, F) unifies its variables with the
        % integers 1 through 6 to meet the constraints.
        books(A, B, C, D, E, F) :-
            permutation([1, 2, 3, 4, 5, 6], [A, B, C, D, E, F]),
        
            % 1. A and D are not touching.
            \+ touching(A, D),
        
            % 2. E is between two books which are both vertical or both horizontal.
            % We can take a shortcut by exploiting the asymmetry in touch_long.
            touch_long(_, E),
            touch_long(E, _),
        
            % 3. C touches exactly two books. That means that the set of all books
            % touching C has cardinality 2.
            setof(X, touching(X, C), TouchingC),
            length(TouchingC, 2),
        
            % 4. A and F touch.
            touching(A, F),
        
            % 5. E and F touch along their cover (long side).
            touching_long(E, F).
        
        您现在可以运行
        书籍(A、B、C、D、E、F)
        生成有效排列:

        ?- books(A,B,C,D,E,F).
        A = 3,
        B = 2,
        C = 4,
        D = 1,
        E = 5,
        F = 6 ;
        A = 3,
        B = 2,
        C = 6,
        D = 1,
        E = 5,
        F = 4 
        

        等。可以通过观察输出来解决原始问题;为原始程序编写一个全自动的解决方案只是一个练习(有点乏味)。

        编辑:修复了一个错误(关于“E和F沿封面(长边)触摸”的规则)使用了D而不是F

        使用ECLiPSe CLP Prolog的约束编程解决方案:

        :- lib(gfd).
        
        model(Books) :-
            [A, B, C, D, E, F] = Books,
        
            Books :: 1..6,
            alldifferent(Books),
        
            % A and D are not touching.
            abs(A - D) #\= 1, (A #= 3) => (D #< 4), (D #= 3) => (A #< 4),
        
            % E is between two books which are both vertical or both horizontal.
            E :: [2, 5],
        
            % C touches exactly two books.
            C :: [2, 4, 6],
        
            % A and F touch.
            (abs(A - F) #= 1) or (A #= 3 and F #> 4) or (F #= 3 and A #> 4),
        
            % E and F touch along their cover (long side)
            abs(E - F) #= 1, (E #> 3) => (F #> 3), (E #< 4) => (F #< 4).
        
        find(Books) :-
            findall(Books, (model(Books), labeling(Books)), Sols),
            table(Books, Sols).
        

        因此,只有书D知道位置-1。

        编辑:修复了一个错误(关于“E和F沿封面(长边)接触”的规则)使用了D而不是F

        使用ECLiPSe CLP Prolog的约束编程解决方案:

        :- lib(gfd).
        
        model(Books) :-
            [A, B, C, D, E, F] = Books,
        
            Books :: 1..6,
            alldifferent(Books),
        
            % A and D are not touching.
            abs(A - D) #\= 1, (A #= 3) => (D #< 4), (D #= 3) => (A #< 4),
        
            % E is between two books which are both vertical or both horizontal.
            E :: [2, 5],
        
            % C touches exactly two books.
            C :: [2, 4, 6],
        
            % A and F touch.
            (abs(A - F) #= 1) or (A #= 3 and F #> 4) or (F #= 3 and A #> 4),
        
            % E and F touch along their cover (long side)
            abs(E - F) #= 1, (E #> 3) => (F #> 3), (E #< 4) => (F #< 4).
        
        find(Books) :-
            findall(Books, (model(Books), labeling(Books)), Sols),
            table(Books, Sols).
        


        所以,只有D本书知道位置-1。

        您还没有定义
        p1
        事实。要解决这个问题,您需要以完全不同的方式设置程序。您能解释一下如何解决这个问题吗?我是Prolog新手,目前不知道解决此问题的其他方法。例外情况是,您没有名为
        p1
        且没有参数的事实或谓词。您将在
        p2
        p6
        中遇到相同的异常<代码>位置(a):-p1、p2、p3、p4、p5、p6。尝试以谓词或事实的形式查询
        p1
        等,但它们本身并不存在。您尚未定义
        p1
        事实。要解决这个问题,您需要以完全不同的方式设置程序。您能解释一下如何解决这个问题吗?我是Prolog新手,目前不知道解决此问题的其他方法。例外情况是,您没有名为
        p1
        且没有参数的事实或谓词。您将在
        p2
        p6
        中遇到相同的异常<代码>位置(a):-p1、p2、p3、p4、p5、p6。尝试以谓词或事实的形式查询
        p1
        等,但它们本身并不存在。
        \+
        中的
        \+
        是什么?从上下文来看,我会说它有点像
        not
        ,但写“not”是一种奇怪的方式。@moose,它是“not”,看起来确实有点奇怪,但它是prolog语法。有些序言(如SWI序言)允许
        not/1
        @moose It's negation。我想它应该代表一个⊢ (“provable”)带有斜杠。在
        \+触摸(a,D),
        中什么是
        \+
        ?从上下文来看,我会说它有点像
        not
        ,但写“not”是一种奇怪的方式。@moose,它是“not”,看起来确实有点奇怪,但它是prolog语法。有些序言(如SWI序言)允许
        not/1
        @moose It's negation。我想它应该代表一个⊢ (“provable”)带有斜杠。它约束双方的值相等@SergeyDymchenko如何打印出所有可能的配置?@Maesumi
        findall(书籍,(模型(书籍),标签(书籍),writeln(书籍)),Sols)。
        @SergeyDymchenko推荐的免费windows程序是什么,可以处理上述文件?@Maesumi ECLiPSe CLP在windows上工作。您可以从it下载它来约束两边的值相等@SergeyDymchenko如何打印出所有可能的配置?@Maesumi
        findall(书籍,(模型(书籍),标签(书籍),writeln(书籍)),Sols)。
        @SergeyDymchenko推荐的免费windows程序是什么,可以处理上述文件?@Maesumi ECLiPSe CLP在windows上工作。你可以从