Понимание ограничений в Прологе

Я пытаюсь ознакомиться с ограничениями в Прологе, и у меня возникают проблемы с пониманием программы ниже:

sudoku(Rows) :-
        length(Rows, 9), maplist(same_length(Rows), Rows),
        append(Rows, Vs), Vs ins 1..9,
        maplist(all_distinct, Rows),
        transpose(Rows, Columns),
        maplist(all_distinct, Columns),
        Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],
        blocks(As, Bs, Cs),
        blocks(Ds, Es, Fs),
        blocks(Gs, Hs, Is).

blocks([], [], []).
blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2], [N7,N8,N9|Ns3]) :-
        all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
        blocks(Ns1, Ns2, Ns3).

problem(1, [[_,_,_,_,_,_,_,_,_],
            [_,_,_,_,_,3,_,8,5],
            [_,_,1,_,2,_,_,_,_],
            [_,_,_,5,_,7,_,_,_],
            [_,_,4,_,_,_,1,_,_],
            [_,9,_,_,_,_,_,_,_],
            [5,_,_,_,_,_,_,7,3],
            [_,_,2,_,1,_,_,_,_],
            [_,_,_,_,4,_,_,_,9]]).

Если кто-нибудь может помочь мне разбить этот код, чтобы я лучше понял, как он решается с использованием ограничений, я был бы очень признателен.


person 0x1984    schedule 06.05.2021    source источник
comment
Откуда этот код, если я могу спросить?   -  person David Tonhofer    schedule 07.05.2021
comment
Пример @DavidTonhofer 9.7 из: swish.swi-prolog.org/pldoc/man? section=clpfd   -  person 0x1984    schedule 07.05.2021
comment
Да, также нашел его в разделе transpose/2.   -  person David Tonhofer    schedule 07.05.2021
comment
В этой программе много хороших трюков.   -  person David Tonhofer    schedule 07.05.2021


Ответы (1)


Что происходит:

  • sudoku/1 imposes constraints on a 9x9 matrix of elements
    • with the help of blocks/2
  • problem/2 перечисляет конкретную проблему

И мы можем затем собрать оба вместе:

?- problem(1,Matrix), 
   % Matrix is now a 9x9 matrix of partially bound elements.
   % Impose constraints; these suffice to find a unique solution
   sudoku(Matrix).    

Matrix = [[9,8,7,6,5,4,3,2,1],
          [2,4,6,1,7,3,9,8,5],
          [3,5,1,9,2,8,7,4,6],
          [1,2,8,5,3,7,6,9,4],
          [6,3,4,8,9,2,1,5,7],
          [7,9,5,4,6,1,8,3,2],
          [5,1,9,2,8,6,4,7,3],
          [4,7,2,3,1,9,5,6,8],
          [8,6,3,7,4,5,2,1,9]].

Решатель ограничений начинает работать немедленно (очевидно, он обнаруживает, что информации достаточно для продолжения; всегда ли так?). Нет необходимости вызывать label/1.

Давайте посмотрим, как устанавливаются ограничения между элементами матрицы:

sudoku(Rows) :-

   % If "Rows" is unbound, bind it to a list of 9 unbound variables.
   % If "Rows" is bound (as in our call above), this just verifies that there
   % are indeed 9 elements in Rows. Same as:
   %
   % Rows = [R1,
   %         R2,
   %         R3,
   %         R4,
   %         R5,
   %         R6,
   %         R7,
   %         R8,
   %         R9].

   length(Rows, 9),

   % For each element in "Rows", bind the element to a list of the same
   % length as "Rows" (i.e. 9); we now have a 9 x 9 matrix of possibly
   % unbound variables. A bit too clever maybe.
   %
   % Same as:
   %
   % Rows = [[E11,E12,E13,E14,E15,E16,E17,E18,E19],
   %         [E21,E22,E23,E24,E25,E26,E27,E28,E29],
   %         [E31,E32,E33,E34,E35,E36,E37,E38,E39],
   %         [E41,E42,E43,E44,E45,E46,E47,E48,E49],
   %         [E51,E52,E53,E54,E55,E56,E57,E58,E59],
   %         [E61,E62,E63,E64,E65,E66,E67,E68,E69],
   %         [E71,E72,E73,E74,E75,E76,E77,E78,E79],
   %         [E81,E82,E83,E84,E85,E86,E87,E88,E89],
   %         [E91,E92,E93,E94,E95,E96,E97,E98,E99]].

   maplist(same_length(Rows), Rows),     

   % Concatenate all the variables in all the rows into a new list Vs (that
   % predicate is badly named, it concatenates lists form a list into a list)

   append(Rows, Vs),

   % A first constraint: every element Exx in Vs must be in domain [1..9]
   % If we passed in a matrix Rows with concrete values for Exx that are not
   % in domain [1..9], this will fail.

   Vs ins 1..9,

   % Another constraint: For every row (list of 9 elements) in Rows: 
   % all the elements of the row must be distinct

   maplist(all_distinct, Rows),

   % We now want to impose the constraint that for every column, 
   % all the elements of the column must be distinct

   % Transpose the 9x9 matrix Rows into the 9x9 matrix Columns. 
   % (the new rows of Columns are the columns of Rows).
   %
   % Columns = [[E11,E21,E31,E41,E51,E61,E71,E81,E91],
   %            [E12,E22,E32,E42,E52,E62,E72,E82,E92]
   %            [E13,E23,E33,E43,E53,E63,E73,E83,E93]
   %            [E14,E24,E34,E44,E54,E64,E74,E84,E94]
   %            [E15,E25,E35,E45,E55,E65,E75,E85,E95]
   %            [E16,E26,E36,E46,E56,E66,E76,E86,E96]
   %            [E17,E27,E37,E47,E57,E67,E77,E87,E97]
   %            [E18,E28,E38,E48,E58,E68,E78,E88,E98]
   %            [E19,E29,E39,E49,E59,E69,E79,E89,E99]].

   transpose(Rows, Columns),  

   % Another constraint: For every "column of Rows"
   % all the elements of the column must be distinct

   maplist(all_distinct, Columns), 

   % Now we need to impose the "all-distinct" constraint
   % on the 3x3 sub-matrices

   % Give the rows distinct names

   Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],  

   % Impose constraint on "3-element-wide" groups of the
   % "3-row-high" group of As, Bs, Cs: namely, "all distinct" 

   blocks(As, Bs, Cs),
                   
   % Same of the next "3-high" group
   
   blocks(Ds, Es, Fs),

   % Same of the next "3-high" group

   blocks(Gs, Hs, Is).                   
person David Tonhofer    schedule 06.05.2021