提示了解解决皇后区问题的出色计划

时间:2019-05-20 12:56:37

标签: prolog n-queens

在Sterling&Shapiro的序言艺术中,练习14.1(v)节:

queens(N,Qs) :-
    length(Qs,N),
    place_queens(N,Qs,_,_).

place_queens(0,_Qs,_Ups,_Downs).
place_queens(I,Qs,Ups,[_|Downs]) :-
    I > 0, I1 is I-1,
    place_queens(I1,Qs,[_|Ups] ,Downs),
    place_queen(I,Qs,Ups,Downs).

place_queen(Q,[Q|_],[Q|_],[Q|_]).
place_queen(Q,[_|Qs],[_|Ups],[_|Downs] ):-
    place_queen(Q,Qs,Ups,Downs).

这是一个出色的程序,共有11行,可快速解决在棋盘上放置皇后的问题。这很神奇:只有计数器,递归和列表变得越来越长。我,即使在跟踪的帮助下,也不了解它。有人可以向我解释吗?您如何编写这样的程序?导致从例如另一个(好的标准解决方案)派生该程序的逻辑/心理过程是什么?

queens(N,Qs) :-
    numlist(1,N,Ns), 
    queens(Ns,[ ],Qs).

queens(UnplacedQs,SafeQs,Qs) :-
    select(Q,UnplacedQs,UnplacedQs1),
    \+ attack(Q,SafeQs),
    queens(UnplacedQs1,[Q|SafeQs] ,Qs).
queens([ ],Qs,Qs).

attack(X,Xs) :-
    attack(X,1,Xs).

attack(X,N,[Y|_]) :-
    X is Y+N ; X is Y-N.
attack(X,N,[_|Ys]) :-
    N1 is N+1,
    attack(X,N1,Ys).

5 个答案:

答案 0 :(得分:4)

让我们首先看看顶级谓词。在这里,我们通过调用queens(N,Qs)解决了 N×N 个皇后问题。正文length(Qs, N)中的第一个调用构造了一个长度为N的变量列表。接下来,它用place_queens/4调用place_queens(N, Qs, _, _)。因此,它将两个自由变量传递给place_queens/4。稍后,我们将通过取消构造一个列表。

首先递归调用place_queens/4,直到我们为I达到零为止,例如,如果我们为N = 4“展开”程序,则得到:

place_queens(4, [Q1,Q2,Q3,Q4], UT, [D1,D2,D3,D4|DT]) :-
    place_queens(3, [Q1,Q2,Q3,Q4], [U4|UT], [D2,D3,D4|DT]) :-
        place_queens(2, [Q1,Q2,Q3,Q4], [U3,U4|UT], [D3,D4|DT]) :-
            place_queens(1, [Q1,Q2,Q3,Q4], [U2,U3,U4|UT], [D4|DT]) :-
                place_queens(0, [Q1,Q2,Q3,Q4], [U1,U2,U3,U4|UT], DT),
                %% ---
                place_queen(1, [Q1,Q2,Q3,Q4], [U2,U3,U4|UT], DT),
            place_queen(2, [Q1,Q2,Q3,Q4], [U3,U4|UT], [D4|DT]),
        place_queen(3, [Q1,Q2,Q3,Q4], [U4|UT], [D3,D4|DT]),
    place_queen(4, [Q1,Q2,Q3,Q4], UT, [D2,D3,D4|DT]).

(以上不是Prolog代码,是显示调用结构的说明。)

place_queens会做两件事:

  1. 它“展开” 上升 [U1, U2, U3, U4|_]下降 [D1, D2, D3, D4|_]的列表;和
  2. 它调用具有特定值的place_queen以及起伏列表的某些部分。

place_queen的任务是在列表中的某处填写列I。它始终会获得皇后位置[Q1, Q2, Q3, Q4]的完整列表以及起伏列表的一部分。这些起伏代表对角线在上下方向上移动。

如果我们为给定的皇后位置填写一个值,我们还将在给定的起伏列表中标记该值,从而“声明”该女王的这些对角线。如果我们正确地进行簿记就足够了,因为如果另一个女​​王想在已经声明的对角线上放置一个位置,则它的目的是将该值附加到相应的对角线上,但是由于其值与已经分配的值。

让我们通过一个例子来证明这一点。当我们调用第一个place_queen(1, [Q1, Q2, Q3, Q4], [U2, U3, U4|_], _)时,可以将其分配给第一个位置,这是基本情况,因此导致以下事实:

place_queen(1,[Q1,Q2,Q3,Q4],[U2,U3,U4|_], _D) :-
    Q1 = 1,
    U2 = 1,
    _D = [1|_].

所以这意味着我们的[Q1, Q2, Q3, Q4]看起来像[1, Q2, Q3, Q4],向上的对角线看起来像[U1, U2, U3, U4|_] = [U1, 1, U3, U4|_][D1, D2, D3, D4|_] = [D1, D2, D3, D4, 1|_]

现在,我们旨在分配下一个place_queen(2, [1,Q2,Q3,Q4],[U3,U4|_], [D4, 1|_])。我们知道我们无法将该值分配给Q列表的第一项,因为该值被1占用,因此这意味着两个皇后区具有相同的列并互相攻击,这样就行不通了。

因此,我们执行递归操作,从而弹出 up down 列表,因此:

place_queen(2, [1,Q2,Q3,Q4], [U3,U4|UT], [D4, 1|DT]) :-
    place_queen(2, [Q2,Q3,Q4], [U4|UT], [1|DT]).

因此,现在我们打算将第二行的女王/王后放在棋盘的第二列,但是又有一个问题:女王/王后1再次声明了那个正方形的对角线,我们可以得出形成了[1|_]下来的事实。因此,我们再次必须执行递归,例如:

place_queen(2, [1,Q2,Q3,Q4], [U4|UT], [1|DT]) :-
    place_queen(2, [Q3,Q4], UT, DT).

在这里我们可以安全地放置女王/王后,在这里,所有列表都没有被阻止。因此,当我们这样做时,列表现在看起来像[Q1, Q2, Q3, Q4] = [1, Q2, 2, Q4][U1, U2, U3, U4|_] = [U1, 1, U3, U4, 2|_][D1, D2, D3, D4|_] = [D1, D2, D3, D4, 1, 2|_]。如果看一下我们分配的木板,对角线确实有意义:

 \D5 \D6 \D7 \ D8\
  +---+---+---+---+
 /| Q |   |   |   |
U2+---+---+---+---+
 /|   |   | Q |   |
U3+---+---+---+---+
 /|   |   |   |   |
U4+---+---+---+---+
 /|   |   |   |   |
  +---+---+---+---+
  U5 /U6 /U7 / U8/

因此我们可以看到,第一个皇后宣称D5U2,第二个皇后宣称D6U5

现在我们可以将第三个皇后放在棋盘上,或者至少可以尝试这样做,因此我们用place_queen(3,[1,Q2,2,Q4],[U4,2|_],[D3,D4,1,2|_])进行了通话。

此处我们将无法将其放置在第一列中(因为它被女王1占据),无法将其放置在第二列中(女王2占据了对角线) ,第三列(该列由皇后2占据,下对角线由女王1声明),最后一列(下对角线由女王2声明)。最终,我们用尽了Q列表,因此我们将不得不回溯前任女王的任务。

所以现在我们继续放置第二个皇后,剩下的唯一选择就是将其放在最后一列:

 \D5 \D6 \D7 \ D8\
  +---+---+---+---+
 /| Q |   |   |   |
U2+---+---+---+---+
 /|   |   |   | Q |
U3+---+---+---+---+
 /|   |   |   |   |
U4+---+---+---+---+
 /|   |   |   |   |
  +---+---+---+---+
  U5 /U6 /U7 / U8/

在这种情况下,[Q1, Q2, Q3, Q4] = [1, Q2, Q3, 2][U1, U2, U3, U4|_] = [U1, 1, U3, U4, U5, 2|_][D1, D2, D3, D4|_] = [D1, D2, D3, D4, 1, D6, 2|_]。因此,现在的问题是将下一个皇后放在哪里(女王3):

我们可以再次分配第三个女王,因此我们现在用place_queen(3,[1,Q2,Q3,2],[U4,U5,2|_],[D3,D4,1,D6,2|_])来调用谓词。由于皇后1占据了该列,因此我们无法将该皇后分配给第一个位置,因此我们以place_queen(3,[Q2,Q3,2],[U5,2|_],[D4,1,D6,2|_])递归地调用它。在这里放女王/王后没有问题,因为所有三个列表的头都是自由变量。因此,我们设置了Q2 = U5 = D4 = 3,从而获得了以下面板:

 \D5 \D6 \D7 \ D8\
  +---+---+---+---+
 /| Q |   |   |   |
U2+---+---+---+---+
 /|   |   |   | Q |
U3+---+---+---+---+
 /|   | Q |   |   |
U4+---+---+---+---+
 /|   |   |   |   |
  +---+---+---+---+
  U5 /U6 /U7 / U8/

所以现在我们的列表看起来像[Q1, Q2, Q3, Q4] = [1, 3, Q3, 2][U1, U2, U3, U4|_] = [U1, 1, U3, U4, 3, 2|_][D1, D2, D3, D4|_] = [D1, D2, D3, 3, 1, D6, 2|_]。现在我们可以最终将最后一个皇后分配给董事会,因此我们将place_queen/4place_queen(4,[1,3,Q3,2],[3,2|_],[D2,D3,3,1,D6,2|DT]).一起使用。前两个地方被拒绝(分别被列和对角线占用),因此在两次递归调用之后,我们以place_queen(4,[Q3,2],_,[3,1,D6,2|DT])结尾,但其中一个被女王3占据(对角线下方),实际上,情况看起来像这样:

 \D5 \D6 \D7 \ D8\
  +---+---+---+---+
 /| Q |   |   |   |
U2+---+---+---+---+
 /|   |   |   | Q |
U3+---+---+---+---+
 /|   | Q |   |   |
U4+---+---+---+---+
 /|   |   | Q |   |
  +---+---+---+---+
  U5 /U6 /U7 / U8/

因此,我们再次发现这不会产生糖浆。 Prolog将继续追踪,并最终提出解决方案:

 \D5 \D6 \D7 \ D8\
  +---+---+---+---+
 /|   | Q |   |   |
U2+---+---+---+---+
 /|   |   |   | Q |
U3+---+---+---+---+
 /| Q |   |   |   |
U4+---+---+---+---+
 /|   |   | Q |   |
  +---+---+---+---+
  U5 /U6 /U7 / U8/

然后列表看起来像Qs = [3, 1, 4, 2]U = [1, 3, _, 2, 4|_]D = [_, _, 3, 4_, 1, 2|_]

因此我们可以得出结论,上列表和下列表中的值本身并不相关,它用于防止在这些对角线上分配不同的数字(queen)。

答案 1 :(得分:4)

空格可以帮助大大提高程序的可读性。在这方面,变量命名也非常重要:

queens(N, QS) :-
    length(QS, N),
    place_queens(N,  QS, _, _).

place_queens(0,_,_,_).
place_queens(    I,  QS,    US, [_|DS]) :- I > 0,
    I1 is I-1,
    place_queens(I1, QS, [_|US],   DS),
    place_queen( I,  QS,    US,    DS).

place_queen(     I,  QS,    US,    DS):-       % an equivalent definition!
   nth1(K,QS,I), nth1(K,US,I), nth1(K,DS,I).   % between(1,N,K) holds

威廉(Willem)answer的插图再次进行了空格调整:

place_queens(   4,              [Q1,Q2,Q3,Q4],              UT,  [D1,D2,D3,D4|DT]) :-
    place_queens(   3,          [Q1,Q2,Q3,Q4],          [U4|UT],    [D2,D3,D4|DT]) :-
        place_queens(   2,      [Q1,Q2,Q3,Q4],       [U3,U4|UT],       [D3,D4|DT]) :-
            place_queens(   1,  [Q1,Q2,Q3,Q4],    [U2,U3,U4|UT],          [D4|DT]) :-
                place_queens(0, [Q1,Q2,Q3,Q4], [U1,U2,U3,U4|UT],              DT),
                %% ---
                place_queen(1,  [Q1,Q2,Q3,Q4],    [U2,U3,U4|UT],              DT),
            place_queen(2,      [Q1,Q2,Q3,Q4],       [U3,U4|UT],          [D4|DT]),
        place_queen(3,          [Q1,Q2,Q3,Q4],          [U4|UT],       [D3,D4|DT]),
    place_queen(4,              [Q1,Q2,Q3,Q4],              UT,     [D2,D3,D4|DT]).

因此,递归构建N嵌套的N个嵌套的place_queen长循环,在相同的列表上工作,并且起始位置在某些方案中发生了移位。

它还将使UT = [U5,U6,U7,U8|_](由于place_queen(4))和DT = [D5,D6,D7,D8|_](由于place_queen(1))如此,所以这四个循环等效于

four_queens( [Q1,Q2,Q3,Q4] ) :-
    place_queen(1, [Q1,Q2,Q3,Q4], [U2,U3,U4,U5], [D5,D6,D7,D8]),
    place_queen(2, [Q1,Q2,Q3,Q4], [U3,U4,U5,U6], [D4,D5,D6,D7]),
    place_queen(3, [Q1,Q2,Q3,Q4], [U4,U5,U6,U7], [D3,D4,D5,D6]),
    place_queen(4, [Q1,Q2,Q3,Q4], [U5,U6,U7,U8], [D2,D3,D4,D5]).

实际上,它产生的结果与queens(4, QS)相同。

我们可以看到那里的对角线...。对吗?当第一个皇后放在Q3时,它变成1=Q3=U4=D7

four_queens( [Q1,Q2, 1,Q4] ) :- 
    place_queen(1, [Q1,Q2, ,Q4], [U2,U3, ,U5], [D5,D6, ,D8]),  % 1st row, 3rd pos
    place_queen(2, [Q1,Q2, 1,Q4], [U3, 1,U5,U6], [D4,D5,D6, 1]),
    place_queen(3, [Q1,Q2, 1,Q4], [ 1,U5,U6,U7], [D3,D4,D5,D6]),
    place_queen(4, [Q1,Q2, 1,Q4], [U5,U6,U7,U8], [D2,D3,D4,D5]).

,然后不可能在place_queen(在Q2上被1占用)或US(在1 {上被1占用) Q4)。因此,唯一的其他可能性是DS

2=Q1=U3=D4

因此,由于这些列表在每个迭代步骤中都移动了一个位置,所以我们最终得到了具有共享对角线条目的矩阵,因此对角线中的一个单元格自动地对它进行整体声明!

接下来,four_queens( [ 2,Q2, 1,Q4] ) :- place_queen(1, [ 2,Q2, ,Q4], [U2, 2, 1,U5], [D5,D6, 1,D8]), place_queen(2, [ ,Q2, 1,Q4], [ , 1,U5,U6], [ ,D5,D6, 1]), % 2nd row, 1st pos place_queen(3, [ 2,Q2, 1,Q4], [ 1,U5,U6,U7], [D3, 2,D5,D6]), place_queen(4, [ 2,Q2, 1,Q4], [U5,U6,U7,U8], [D2,D3, 2,D5]). 是不可能的,因为3=Q2已经存在。这样我们得到D4=2

3=Q4=U7=D6

答案就在眼前!

four_queens( [ 2,Q2, 1, 3] ) :-
    place_queen(1, [ 2,Q2, , 3], [U2, 2, 1,U5], [D5, 3, 1,D8]),
    place_queen(2, [ ,Q2, 1, 3], [ 2, 1,U5,U6], [ 2,D5, 3, 1]),
    place_queen(3, [ 2,Q2, 1, ], [ 1,U5,U6, ], [D3, 2,D5, ]),  % 3rd row, 4th pos
    place_queen(4, [ 2,Q2, 1, 3], [U5,U6, 3,U8], [D2,D3, 2,D5]).

所以作者的思考过程可能就是这样。棋盘是一个正方形矩阵。如果在某个特定的单元格上放置一个女王后会自动点亮整个列,那该怎么办呢?还有对角线?

关键见解是,这是同一块板的三个分开视图,然后很容易得出这些矩阵:

four_queens( [ 2, 4, 1, 3] ) :-
    place_queen(1, [ 2, 4, , 3], [U2, 2, 1,U5], [D5, 3, 1,D8]),
    place_queen(2, [ , 4, 1, 3], [ 2, 1,U5, 4], [ 2,D5, 3, 1]),
    place_queen(3, [ 2, 4, 1, ], [ 1,U5, 4, 3], [ 4, 2,D5, 3]),
    place_queen(4, [ 2, , 1, 3], [U5, , 3,U8], [D2, , 2,D5]).  % 4th row, 2nd pos

,然后他们只需要一种自动为任何 [[A, B, C, D], [[E, F, G, H], [[O, N, M, L], [A, B, C, D], [F, G, H, I], [P, O, N, M], [A, B, C, D], [G, H, I, J], [Q, P, O, N], [A, B, C, D]] [H, I, J, K]] [R, Q, P, O]] 进行设置的方法。可以用一些算术运算和两次Nlength调用对它进行编码,但是那样的话,它的神秘性和酷劲性将大大降低,因此它们可以内联并简化所有操作。


关于此代码的另一件有趣的事情是,它如何使用递归以线性方式转到基本情况,同时设置了阶段,以便由内而外地执行以下计算,其中{{1 }}实际上成为非确定性计算的生成和测试模型的强制性嵌套循环解释中的最外层循环。

好像创建了要首先运行的代码(给定值maplist的{​​{1}}嵌套循环),然后运行它。

(例如,某个 Common Lisp 的实现可能与其 macros 有关;但是可以使用递归。或者在功能范例中,我们可以说它使用了隐式延续(在每个谓词定义的第二行,将在第一个递归返回之后输入),以模仿在{{{ 3}}。)

答案 2 :(得分:3)

问题的第一部分中的代码是此处说明的内容。此处将代码重新发布,以确保读者不会错误地看错代码。

queens(N,Qs) :-
    length(Qs,N),
    place_queens(N,Qs,_,_).

place_queens(0,_Qs,_Ups,_Downs).
place_queens(I,Qs,Ups,[_|Downs]) :-
    I > 0, I1 is I-1,
    place_queens(I1,Qs,[_|Ups] ,Downs),
    place_queen(I,Qs,Ups,Downs).

place_queen(Q,[Q|_],[Q|_],[Q|_]).
place_queen(Q,[_|Qs],[_|Ups],[_|Downs] ):-
    place_queen(Q,Qs,Ups,Downs).

此代码与大多数Prolog解决N皇后问题的方法一样,是生成和测试的。该代码生成一个可能的解决方案并对其进行测试。但是,皇后药水不是一次生成一个可能答案的所有位置,而是逐步设置并在部分失败时进行更改,直到找到完整的解决方案。

代码中有一项书面测试,

place_queen(Q,[Q|_],[Q|_],[Q|_]).

要了解这一点,需要了解here

中与该语句相关的参数的含义
  

现在想象一下,国际象棋棋盘分为三层,一层   处理针对列的攻击,针对对角线的攻击分别处理两个。

第一个参数表示由正整数标识并绑定的皇后。

第二个参数表示一列,并且始终是板子尺寸的列表,其中列表中的每个部分代表板子的列之一。 代码使用变量Qs来表示,但对我来说,它更像Rs,即行。因此,如果列表中某个位置有任何绑定值,那么该列将成为女王攻击。

第三个和第四个参数的原理相同,并负责女王的对角线攻击。一种是对角线上升,另一种是对角线下降。由于它们再次是对角线,因此它们被表示为列表,但取决于在检查板上的女王的药水,对角线上升的大小可能与对角线下降的大小不同。

例如,在下面的图像中,白色皇后代表被选中的皇后的位置,而黑色皇后对角向上代表的是对角线列表,另一个皇后则代表向下对角线的列表。

enter image description here

注意:使用Chess Diagram Setup

生成的图像

对角线的长度为2,而对角线的长度为1。

测试表明,如果可以将第一个自变量中的女王/王后与列攻击自变量统一,则向上对角线攻击和向下对角线攻击然后接受该位置的女王/王后以得到部分答案或完整答案回答女王是否在第二个参数中列表的最后位置。

因此进行测试

place_queen(Q,[Q|_],[Q|_],[Q|_]).

与为清晰和文档而写的文字

place_queen(Q,Rs,Ups,Downs) :-
  Rs = [R_1|_],
  Ups = [U_1|_],
  Downs = [D_1|_],
  Q = R_1, Q = U_1, Q = D_1

然后,如果

Q为1
R_1未绑定
U_1未绑定
D_1未绑定

过去的测试和1绑定到变量R_1,U_1和D_1。

以及失败的测试示例

Q是3
R_1是1
U_1未绑定
D_1未绑定

现在是因为列表中没有值而导致测试失败的呼叫。

Q为2
R_1是[]
U_1未绑定
D_1未绑定

其余代码仅会生成用于测试的案例。

可以看到第二个参数是通过运行此代码变体生成的。

queens(N) :-
    length(Qs,N),
    format("N: ~w, Qs: ~w~n",[N,Qs]).

?- queens(4).
N: 4, Qs: [_6476,_6482,_6488,_6494]
true.

通过运行此代码变体可以看到对角参数。

queens(N) :-
    length(Qs,N),
    place_queens(N,Qs,_,_).

place_queens(0,_Qs,_Ups,_Downs).
place_queens(I,Qs,Ups,[_|Downs]) :-
    I > 0,
    I1 is I-1,
    place_queens(I1,Qs,[_|Ups] ,Downs),
    format('I1: ~w, Qs: ~w, Ups: ~w, Downs: ~w~n',[I1,Qs,Ups,Downs]).

?- queens(4).
I1: 0, Qs: [_6474,_6480,_6486,_6492], Ups: [_6528,_6516,_6504|_6506], Downs: _6536
I1: 1, Qs: [_6474,_6480,_6486,_6492], Ups: [_6516,_6504|_6506], Downs: [_6534|_6536]
I1: 2, Qs: [_6474,_6480,_6486,_6492], Ups: [_6504|_6506], Downs: [_6522,_6534|_6536]
I1: 3, Qs: [_6474,_6480,_6486,_6492], Ups: _6506, Downs: [_6510,_6522,_6534|_6536]
true ;
false.

这小部分

place_queen(Q,[_|Rs],[_|Ups],[_|Downs] ):-
    place_queen(Q,Rs,Ups,Downs).

只是说,如果下一女王的位置在该列中的某一行上不起作用,则选择另一行。请注意,上面的示例将第二个参数的变量名称从Qs更改为Rs,以表示它正在被更改。

要使生成和测试更容易看到,请修改代码

queens(N,Qs) :-
    length(Qs,N),
    place_queens(N,Qs,_,_).

place_queens(0,_Qs,_Ups,_Downs).
place_queens(I,Qs,Ups,[_|Downs]) :-
    I > 0,
    I1 is I-1,
    place_queens(I1,Qs,[_|Ups] ,Downs),
    format('Generate 1 - I: ~w, Qs: ~w, Ups: ~w, Downs: ~w~n',[I,Qs,Ups,Downs]),
    place_queen(I,Qs,Ups,Downs),
    format('Result    -> I: ~w, Qs: ~w, Ups: ~w, Downs: ~w~n',[I,Qs,Ups,Downs]).

place_queen(Q,Rs,Ups,Downs) :-
    Rs = [R_1|_],
    Ups = [U_1|_],
    Downs = [D_1|_],
    format('Test        - Q : ~w, R_1: ~w, U_1: ~w, D_1: ~w~n',[Q,R_1,U_1,D_1]),
    (
        Rs = [Q|_],
        Ups = [Q|_],
        Downs = [Q|_]
    ->
        format('Test success~n')
    ;
        format('Test failure~n'),
        fail
    ).

place_queen(Q,[_|Qs],[_|Ups],[_|Downs] ):-
    format('Generate 2 - Q: ~w, Qs: ~w, Ups: ~w, Downs: ~w~n',[Q,Qs,Ups,Downs]),
    place_queen(Q,Qs,Ups,Downs).

第一个解决方案的示例。

?- queens(4,Qs).
Generate 1 - I: 1, Qs: [_6488,_6494,_6500,_6506], Ups: [_6542,_6530,_6518|_6520], Downs: _6550
Test        - Q : 1, Q_1: _6488, U_1: _6542, D_1: _6596
Test success
Result    -> I: 1, Qs: [1,_6494,_6500,_6506], Ups: [1,_6530,_6518|_6520], Downs: [1|_6598]
Generate 1 - I: 2, Qs: [1,_6494,_6500,_6506], Ups: [_6530,_6518|_6520], Downs: [_6548,1|_6598]
Test        - Q : 2, Q_1: 1, U_1: _6530, D_1: _6548
Test failure
Generate 2 - Q: 2, Qs: [_6494,_6500,_6506], Ups: [_6518|_6520], Downs: [1|_6598]
Test        - Q : 2, Q_1: _6494, U_1: _6518, D_1: 1
Test failure
Generate 2 - Q: 2, Qs: [_6500,_6506], Ups: _6520, Downs: _6598
Test        - Q : 2, Q_1: _6500, U_1: _6746, D_1: _6752
Test success
Result    -> I: 2, Qs: [1,_6494,2,_6506], Ups: [_6530,_6518,2|_6748], Downs: [_6548,1,2|_6754]
Generate 1 - I: 3, Qs: [1,_6494,2,_6506], Ups: [_6518,2|_6748], Downs: [_6536,_6548,1,2|_6754]
Test        - Q : 3, Q_1: 1, U_1: _6518, D_1: _6536
Test failure
Generate 2 - Q: 3, Qs: [_6494,2,_6506], Ups: [2|_6748], Downs: [_6548,1,2|_6754]
Test        - Q : 3, Q_1: _6494, U_1: 2, D_1: _6548
Test failure
Generate 2 - Q: 3, Qs: [2,_6506], Ups: _6748, Downs: [1,2|_6754]
Test        - Q : 3, Q_1: 2, U_1: _6902, D_1: 1
Test failure
Generate 2 - Q: 3, Qs: [_6506], Ups: _6898, Downs: [2|_6754]
Test        - Q : 3, Q_1: _6506, U_1: _6932, D_1: 2
Test failure
Generate 2 - Q: 3, Qs: [], Ups: _6928, Downs: _6754
Generate 2 - Q: 2, Qs: [_6506], Ups: _6742, Downs: _6748
Test        - Q : 2, Q_1: _6506, U_1: _6782, D_1: _6788
Test success
Result    -> I: 2, Qs: [1,_6494,_6500,2], Ups: [_6530,_6518,_6740,2|_6784], Downs: [_6548,1,_6746,2|_6790]
Generate 1 - I: 3, Qs: [1,_6494,_6500,2], Ups: [_6518,_6740,2|_6784], Downs: [_6536,_6548,1,_6746,2|_6790]
Test        - Q : 3, Q_1: 1, U_1: _6518, D_1: _6536
Test failure
Generate 2 - Q: 3, Qs: [_6494,_6500,2], Ups: [_6740,2|_6784], Downs: [_6548,1,_6746,2|_6790]
Test        - Q : 3, Q_1: _6494, U_1: _6740, D_1: _6548
Test success
Result    -> I: 3, Qs: [1,3,_6500,2], Ups: [_6518,3,2|_6784], Downs: [_6536,3,1,_6746,2|_6790]
Generate 1 - I: 4, Qs: [1,3,_6500,2], Ups: [3,2|_6784], Downs: [_6524,_6536,3,1,_6746,2|_6790]
Test        - Q : 4, Q_1: 1, U_1: 3, D_1: _6524
Test failure
Generate 2 - Q: 4, Qs: [3,_6500,2], Ups: [2|_6784], Downs: [_6536,3,1,_6746,2|_6790]
Test        - Q : 4, Q_1: 3, U_1: 2, D_1: _6536
Test failure
Generate 2 - Q: 4, Qs: [_6500,2], Ups: _6784, Downs: [3,1,_6746,2|_6790]
Test        - Q : 4, Q_1: _6500, U_1: _7070, D_1: 3
Test failure
Generate 2 - Q: 4, Qs: [2], Ups: _7066, Downs: [1,_6746,2|_6790]
Test        - Q : 4, Q_1: 2, U_1: _7100, D_1: 1
Test failure
Generate 2 - Q: 4, Qs: [], Ups: _7096, Downs: [_6746,2|_6790]
Generate 2 - Q: 3, Qs: [_6500,2], Ups: [2|_6784], Downs: [1,_6746,2|_6790]
Test        - Q : 3, Q_1: _6500, U_1: 2, D_1: 1
Test failure
Generate 2 - Q: 3, Qs: [2], Ups: _6784, Downs: [_6746,2|_6790]
Test        - Q : 3, Q_1: 2, U_1: _6962, D_1: _6746
Test failure
Generate 2 - Q: 3, Qs: [], Ups: _6958, Downs: [2|_6790]
Generate 2 - Q: 2, Qs: [], Ups: _6778, Downs: _6784
Generate 2 - Q: 1, Qs: [_6494,_6500,_6506], Ups: [_6530,_6518|_6520], Downs: _6586
Test        - Q : 1, Q_1: _6494, U_1: _6530, D_1: _6626
Test success
Result    -> I: 1, Qs: [_6488,1,_6500,_6506], Ups: [_6542,1,_6518|_6520], Downs: [_6584,1|_6628]
Generate 1 - I: 2, Qs: [_6488,1,_6500,_6506], Ups: [1,_6518|_6520], Downs: [_6548,_6584,1|_6628]
Test        - Q : 2, Q_1: _6488, U_1: 1, D_1: _6548
Test failure
Generate 2 - Q: 2, Qs: [1,_6500,_6506], Ups: [_6518|_6520], Downs: [_6584,1|_6628]
Test        - Q : 2, Q_1: 1, U_1: _6518, D_1: _6584
Test failure
Generate 2 - Q: 2, Qs: [_6500,_6506], Ups: _6520, Downs: [1|_6628]
Test        - Q : 2, Q_1: _6500, U_1: _6776, D_1: 1
Test failure
Generate 2 - Q: 2, Qs: [_6506], Ups: _6772, Downs: _6628
Test        - Q : 2, Q_1: _6506, U_1: _6806, D_1: _6812
Test success
Result    -> I: 2, Qs: [_6488,1,_6500,2], Ups: [1,_6518,_6770,2|_6808], Downs: [_6548,_6584,1,2|_6814]
Generate 1 - I: 3, Qs: [_6488,1,_6500,2], Ups: [_6518,_6770,2|_6808], Downs: [_6536,_6548,_6584,1,2|_6814]
Test        - Q : 3, Q_1: _6488, U_1: _6518, D_1: _6536
Test success
Result    -> I: 3, Qs: [3,1,_6500,2], Ups: [3,_6770,2|_6808], Downs: [3,_6548,_6584,1,2|_6814]
Generate 1 - I: 4, Qs: [3,1,_6500,2], Ups: [_6770,2|_6808], Downs: [_6524,3,_6548,_6584,1,2|_6814]
Test        - Q : 4, Q_1: 3, U_1: _6770, D_1: _6524
Test failure
Generate 2 - Q: 4, Qs: [1,_6500,2], Ups: [2|_6808], Downs: [3,_6548,_6584,1,2|_6814]
Test        - Q : 4, Q_1: 1, U_1: 2, D_1: 3
Test failure
Generate 2 - Q: 4, Qs: [_6500,2], Ups: _6808, Downs: [_6548,_6584,1,2|_6814]
Test        - Q : 4, Q_1: _6500, U_1: _7070, D_1: _6548
Test success
Result    -> I: 4, Qs: [3,1,4,2], Ups: [_6770,2,4|_7072], Downs: [_6524,3,4,_6584,1,2|_6814]
Qs = [3, 1, 4, 2] .

如果您发现此处输出困难,因为它的宽度很大,并且也很难将其作为顶级输出(swipl.exe)进行查看,那么请查看如何使用protocol/1将输出捕获到文件进行查看和分析。

答案 3 :(得分:3)

作为理解原始程序的中间步骤,您可以考虑以下内容,该内容基于相同的基本思想。有一个变量

  • 每N行
  • 每个2 * N-1个对角线
  • 每个2 * N-1个对角线

这些变量以占据板上相应位置的女王的列号实例化(因为每个女王覆盖一列,一行,对角线和对角线)。

该代码不是原始程序中的巧妙列表操作,而是对行和对角线使用“数组”,并且可能更易于理解:

@Repository
public interface TradeRepository extends CrudRepository<Trade,Long> {
    public List<Trade> findByDateBetween(LocalDate beginDate, LocalDate endDate);
}

答案 4 :(得分:0)

由于以前的良好回答,使程序理解了,所以我尝试给出更具说明性的解释。
该程序的作者是ThomFrühwirth(感谢Jschimpf提供的信息)。
我引用摘录自他发布在comp.lang.prolog上的message

  

在同一行,同一列上不能放置两个皇后   或对角线,每行只放置一个女王。因此,我们可以   通过行号标识女王/王后。现在想象一下棋盘   分为三层,一层处理对列的攻击   和两个分别用于对角线上升和下降。我们指出   女王/王后的编号表示女王/王后对田野的攻击   那里。现在我们通过一次查看一行来解决问题,   在柱子上放置一个皇后,在对角线上放置两个皇后。为了   下一行/女王,我们使用相同的列层,以获取新的   上对角线,我们必须将图层向上移动一个字段,   下对角线,我们将图层向下移动一格。

他的程序:

% -------- Meaning of Variables ------
% N, M  ... Size of the board
% I, J  ... Number of the row current queen is on
% Qs, L ... List of length N used to represent the solution
% Cs ... Column as a list of fields of length N
% Us ... Up-Diagonal as an open list of fields
% Ds ... Down-Diagonal as an open list of fields


queens(N,Qs):- gen_list(N,Qs), place_queens(N,Qs,_,_).

gen_list(0,[]).
gen_list(N,[_|L]):-
        N>0, M is N-1,
        gen_list(M,L).

place_queens(0,_,_,_).
place_queens(I,Cs,Us,[_|Ds]):-
        I>0, J is I-1,
        place_queens(J,Cs,[_|Us],Ds),
        place_queen(I,Cs,Us,Ds).

% place_queen(Queen,Column,Updiagonal,Downdiagonal) places a single queen
place_queen(I,[I|_],[I|_],[I|_]).
place_queen(I,[_|Cs],[_|Us],[_|Ds]):-
                place_queen(I,Cs,Us,Ds).

让我们回到这个问题。 让我们简化问题。让我们只考虑行,列和对角线。

queens(N,Qs) :-
    length(Qs,N),
    place_queens(N,Qs,_).

place_queens(0,_,_).    
place_queens(I,Qs,Ups) :-
    I > 0,
    I1 is I-1,
    place_queens(I1,Qs,[_|Ups]),
    place_queen(I,Qs,Ups).

place_queen(Q,[Q|_],[Q|_]).
place_queen(Q,[_|Qs],[_|Ups]):-
    place_queen(Q,Qs,Ups).

?- queens(3,L).
L = [1, 2, 3];        
L = [3, 1, 2];       % row 3/col 1 -- row 1/col 2 -- row 2/col 3
L = [2, 3, 1];
false

第3边具有对角线的棋盘:

    C1  C2  C3
    |   |   |     Row
  +---+---+---+
U1| / | / | / |-- 1
  +---+---+---+
U2| / | / | / |-- 2
  +---+---+---+
U3| / | / | / |-- 3
  +---+---+---+
   U3  U4  U5

以及与行/皇后,列/皇后列表和上对角/皇后列表相关的谓词:

row_col_ups(1, [ 1,C2,C3], [ 1,U2,U3,U4,U5]). % row 1
row_col_ups(1, [C1, 1,C3], [U1, 1,U3,U4,U5]).
row_col_ups(1, [C1,C2, 1], [U1,U2, 1,U4,U5]).

row_col_ups(2, [ 2,C2,C3], [U1, 2,U3,U4,U5]). % row 2
row_col_ups(2, [C1, 2,C3], [U1,U2, 2,U4,U5]).
row_col_ups(2, [C1,C2, 2], [U1,U2,U3, 2,U5]).

row_col_ups(3, [ 3,C2,C3], [U1,U2, 3,U4,U5]). % row 3
row_col_ups(3, [C1, 3,C3], [U1,U2,U3, 3,U5]).
row_col_ups(3, [C1,C2, 3], [U1,U2,U3,U4, 3]).

考虑 place_queen / 3 谓词:

% place_queen(Q,Cols,Ups)
% Q    -> queen/row
% Cols -> list of colunms/queens
% Ups  -> open list of up-diagonals/queens

place_queen(Q,[Q|_],[Q|_]).
place_queen(Q,[_|Qs],[_|Ups]):-
    place_queen(Q,Qs,Ups).

它与 member / 2 的结构相同:

member(X,[X|_]).
member(X,[_|L]):-
    member(X,L).

?- member(3,[1,2,3]).
true.
?- member(X,[1,2]).
X = 1;
X = 2.

但是它以不寻常的方式使用:

?- L=[1,2,X,4], member(3,L).
L = [1, 2, 3, 4],
X = 3

?- member(3,L).
L = [3|_1388];
L = [_1178, 3|_1186];
L = [_1178, _1184, 3|_1192];

因此, place_queen 寻找一个空的正方形(如果存在),将皇后放在哪里。

?- Col=[C1,C2,C3], place_queen(3,Col,UPS).
Col = [3, C2, C3],
UPS = [3|_]

?- Col=[C1,C2,C3], place_queen(1,Col,UPS), UPS2=[U2|UPS], place_queen(2,Col,UPS2).
Col = [3, C2, 2],
UPS = [3, 2|_],
UPS2 = [U2, 3, 2|_]

?- Col=[C1,C2,C3], place_queen(3,Col,UPS), UPS2=[U2|UPS], place_queen(2,Col,UPS2), UPS3=[U1|UPS2], place_queen(1,Col,UPS3).
Col = [3, 1, 2],
UPS = [3, 2|_],
UPS2 = [1, 3, 2|_],
UPS3 = [U1, 1, 3, 2|_]

对角线(向上和向下)由打开列表表示,也就是说,如果需要,可以在队列中向其中添加元素的列表。 place_queens 处理它们以及行和对角线之间的关系。

place_queens(0,_Qs,_Ups,_Downs). % usually pred(0,[],[],[]) for closed list
                                 % but with open-lists we have the variables.

place_queens(I,Qs,Ups,[_|Downs]) :-
    I > 0, I1 is I-1,
    place_queens(I1,Qs,[_|Ups] ,Downs), %  in next row/queen 
    place_queen(I,Qs,Ups,Downs).        %  for the up-diagonals we move the layer
                                        %  one field up.
                                        %  for the down-diagonals we move the layer
                                        %  one field down.

P.S。在行3的棋盘中关联行/女王/王后,列/女王/王后列表和下对角/女王/王后列表的谓词

row_col_downs(1, [ 1,C2,C3], [D1,D2, 1,D4,D5]).
row_col_downs(1, [C1, 1,C3], [D1,D2,D3, 1,D5]).
row_col_downs(1, [C1,C2, 1], [D1,D2,D3,D4, 1]).

row_col_downs(2, [ 2,C2,C3], [D1, 2,D3,D4,D5]).
row_col_downs(2, [C1, 2,C3], [D1,D2, 2,D4,D5]).
row_col_downs(2, [C1,C2, 3], [D1,D2,D3, 2,D5]).

row_col_downs(3, [ 3,C2,C3], [ 3,D2,D3,D4,D5]).
row_col_downs(3, [C1, 3,C3], [D1, 3,D3,D4,D5]).
row_col_downs(3, [C1,C2, 3], [D1,D2, 3,D4,D5]).

P.P.S。ThomFrühwirth提供了该程序的其他两个版本,其中一个版本在纯Prolog中:

% Pure version with successor function

queensp(N,Qs):- gen_listp(N,Qs), place_queensp(N,Qs,_,_).

gen_listp(0,[]).
gen_listp(s(N),[_|L]):-
        gen_listp(N,L).

place_queensp(0,_,_,_).
place_queensp(s(I),Cs,Us,[_|Ds]):-
        place_queensp(I,Cs,[_|Us],Ds),
        place_queen(s(I),Cs,Us,Ds).

place_queen(I,[I|_],[I|_],[I|_]).
place_queen(I,[_|Cs],[_|Us],[_|Ds]):-
        place_queen(I,Cs,Us,Ds).

?- queensp(Q,L).
L = [],
Q = 0 ;
L = [s(0)],
Q = s(0) ;
L = [s(s(s(0))), s(0), s(s(s(s(0)))), s(s(0))],
Q = s(s(s(s(0))))