将列表从最短到最长排序

时间:2016-06-07 23:11:55

标签: prolog combinatorics prolog-findall

我最近选择了prolog,我正在尝试制作一个程序来寻找着名拼图Knight's Tour的解决方案 [找到这里]

使用Warnsdorff算法我试图找到可以从国际象棋棋盘上的特定位置进行的所有可能的移动,然后在移动后做出移动最少的移动然后重复这个过程,但是我找不到这个动作。

到目前为止,这是我的代码

possibleKnightMove(I, J, I1, J1) :- I1 is I+1, J1 is J+2.
possibleKnightMove(I, J, I1, J1) :- I1 is I+2, J1 is J+1.
possibleKnightMove(I, J, I1, J1) :- I1 is I+2, J1 is J-1.
possibleKnightMove(I, J, I1, J1) :- I1 is I+1, J1 is J-2.
possibleKnightMove(I, J, I1, J1) :- I1 is I-1, J1 is J-2.
possibleKnightMove(I, J, I1, J1) :- I1 is I-2, J1 is J+1.
possibleKnightMove(I, J, I1, J1) :- I1 is I-2, J1 is J-1.
possibleKnightMove(I, J, I1, J1) :- I1 is I-1, J1 is J+2.

possible_knight_moves(Rows, Columns, X, Y, Visited, NewX, NewY) :-
    possibleKnightMove(X, Y, NewX, NewY),
    NewX > 0, NewX =< Rows,
    NewY > 0, NewY =< Columns,
    \+ member([NewX,NewY], Visited).

possible_moves_count(Rows, Columns, X, Y, Visited, Count) :-
    findall(_, possible_knight_moves(Rows, Columns, X, Y, Visited, _NewX, _NewY), Moves),
    length(Moves, Count).

warnsdorff(Rows, Columns, X, Y, Visited, NewX, NewY, Score) :-
    possible_knight_moves(Rows, Columns, X, Y, Visited, NewX, NewY),
    possible_moves_count(Rows, Columns, NewX, NewY, [[NewX, NewY] | Visited], Score).

由于可能的移动次数仅在找到它们之后才计算,因此我的列表不会按照我的需要进行排序。

例如使用此输入

warnsdorff(8,8,3,5,[[1,1],[2,3],[3,5]], NewX, NewY, Score).

结果应该是

NewX = 4,
NewY = 7,
Score = 5

但是我得到了

NewX = 1,
NewY = 4,
Score = 3

如果有人能帮助我获得NewXNewY的最低分数

1 个答案:

答案 0 :(得分:1)

一个好的解决方案是将所有可能的移动表示为 N-Spot其中(例如):

  • NSpot
  • 仍然可以移动的次数
  • Spot是我们可以搬到的地方。

在这样的列表中,您可以使用keysort/2对列表进行排序,使得对中的第一个元素处于非递减顺序。例如,此列表的第一个元素将是进一步移动最受限制的点。

您已经非常接近解决方案了。我建议你首先关注评估一个可能的移动,就像spot_freedom(S, F)这样的谓词来说,计算一个点S自由度F(计算移动的数量)仍然可以从S)。

然后,您可以这样做:

findall(F-S, spot_freedom(S, F), SFs0),
keysort(SFs0, SFs)

并在SFs中有候选点的按键列表。然后,您可以选择(例如)此列表的第一个元素,或使用:

member(_-Target, SFs)

以回溯的自由度增加的顺序尝试可用的点。

请注意,您可能需要spot_freedom的其他参数,例如已经移动的历史记录,以检测哪些斑点已被占用。我把这个想象成一个练习。