给定网格,我如何确定网格的元素是否都在一个区域中。在下面的情况是正确的,因为矩阵中的每个元素都有一个邻居。
例1:
gridneighbours([[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[4,1],[4,2]]).
true.
然而在我的第二个例子中,例2:
gridneighbours([[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[3,1],[4,1],[4,2]]).
false.
这是错误的,因为[3,1],[4,1],[4,2]与前面的元素不相交。 最初我尝试使用Prolog中的子集通过简单地在X或Y中添加或减去来检查旁边的现有元素,但是这不起作用,因为分割区域的元素将彼此相邻。对角线也不算是彼此相邻。
更新后,添加了代码: 这是我得到的:
%Check right
neighbourcheck([X,Y|_],S) :- X1 is X + 1, subset([[X1,Y]],S).
%Check left
neighbourcheck([X,Y|_],S) :- X1 is X - 1, subset([[X1,Y]],S).
%Check Up
neighbourcheck([X,Y|_],S) :- Y1 is Y + 1, subset([[X,Y1]],S).
%Check Down
neighbourcheck([X,Y|_],S) :- Y1 is Y - 1, subset([[X,Y1]],S).
% Iterate through all sublists and check for neighbours
gridneighbour(S) :- forall(member(X,S), neighbourcheck(X,S)).
这不起作用的原因是因为子集不关心我们是否与另一个脱节的元素匹配。即[3,1]与[4,1]匹配。 运行此代码并使用上面的示例给出:
答案 0 :(得分:3)
一种天真的方法可以概括如下:
Region
,其余为Rest
。一开始,您可以选择任何一个属于Region
的点,剩下的就是Rest
。Rest
中查找与Region
中任意点相邻的点。
Rest
移至Region
并重复Rest
中有分数,则 区域。以下是定义neighbors/2
的简单方法:
neighbors([X1,Y1], [X2,Y2]) :-
abs(X1-X2) + abs(Y1-Y2) =:= 1.
您可以通过简单地尝试每种可能的组合,在一个列表中查找另一个列表中某个点的邻居的点:
% add_to_region(+Region0, +Rest0, -Region, -Rest)
%% Look in Rest0 for a neighbor to Region0;
%% Region is Region0 with the neighbor,
%% Rest is Rest0 without it
add_to_region(Region, Rest0, [B|Region], Rest) :-
member(A, Region),
select(B, Rest0, Rest),
neighbors(A, B).
对member / 2的调用通过回溯来选择Region中的每个点。 select / 3的调用选择Rest0到B中的每个点,其余的点在Rest中。如果这两个点是邻居,则B被添加到Region的前面。
如果Rest
中没有更多邻居到当前区域,则会失败,如果有,则至少成功一次。您可能希望使用once(add_to_region(Region0, Rest0, Region, Rest))
来调用它,这样您就不会有不必要的选择点。使用您的示例:
?- once(
add_to_region(
[[1,1],[1,2],[1,3],[2,1]],
[[2,2],[2,3],[3,1],[4,1],[4,2]],
Region, Rest)).
Region = [[2, 2], [1, 1], [1, 2], [1, 3], [2, 1]],
Rest = [[2, 3], [3, 1], [4, 1], [4, 2]].
查看从[2,2]
中挑选Rest
并添加到Region
的方式。
?- add_to_region(
[[1,1],[1,2],[1,3],[1,4],[1,5],[1,6]],
[[3,1],[4,1],[4,2]],
Region, Rest).
false.
此操作失败,因为Rest
中的所有点都不是Region
中任何一点的邻居。
如上所述肯定是可行的,但稍作修改,我们可以在Prolog中使用更容易实现的算法。它是这样的:
set_region_rest(+Set, -Region, -Rest)
ordset
。要进行拆分,我们将保留一个额外的列表。我们将其称为Open节点列表:我们还没有探索过的节点。在开始时,输入列表的第一个元素是唯一的开放节点。 其余元素按原样传递。 最后两个参数,Region和Rest,是输出参数。
open_set_closed_rest(Open, Set, Closed, Rest)
要在Prolog中执行此操作,我将首先清理坐标表示。
他们有两个列表是有点烦人的:如果我们使用例如一对代替它,那么它的写作要少得多:[X,Y] ---> X-Y
。为此,我添加了这个谓词:
xy(XY) :-
coordinates(C),
maplist([[X,Y], X-Y]>>true, C, XY).
xy([1-1,1-3,1-2]).
xy([1-2,2-1,2-2]).
xy([1-1, 1-2, 1-3, 1-4, 1-5,
2-1, 2-5,
3-1, 3-5,
4-1, 4-5,
5-1, 5-2, 5-3, 5-4, 5-5]).
xy([1-1, 1-2, 1-3, 1-4, 1-5,
2-1, 2-5,
3-1, 3-3, 3-5,
4-1, 4-5,
5-1, 5-2, 5-3, 5-4, 5-5]).
coordinates([[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[4,1],[4,2]]).
coordinates([[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[3,1],[4,1],[4,2]]).
(我还增加了4个测试集!)
因此,我得到:
?- xy(XY).
XY = [1-1, 1-2, 1-3, 2-1, 2-2, 2-3, 3-1, 4-1, ... - ...] [write] % press 'w' here
XY = [1-1, 1-2, 1-3, 2-1, 2-2, 2-3, 3-1, 4-1, 4-2] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 1-6, 3-1, 4-1, 4-2] ;
XY = [1-1, 1-3, 1-2] ;
XY = [1-2, 2-1, 2-2] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 2-1, 2-5, 3-1, 3-5, 4-1, 4-5, 5-1, 5-2, 5-3, 5-4, 5-5] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 2-1, 2-5, 3-1, 3-3, 3-5, 4-1, 4-5, 5-1, 5-2, 5-3, 5-4, 5-5].
以下是人们可以尝试在代码中编写上述算法的方法:
set_region_rest([A|As], Region, Rest) :-
sort([A|As], [B|Bs]),
open_set_closed_rest([B], Bs, Region, Rest).
这只是对输入Set进行了排序,并从中分离出第一个元素。 第一个元素是Open集合中的第一个坐标对,其余是Set,然后是输出参数。
现在,要将Set拆分为Region和Rest,只要我们在Open集中有坐标对,我们就需要继续增长Region。如果Open set为空,则表示我们的Region已完成,剩余的Set为Rest:
:- use_module(library(clpfd)).
open_set_closed_rest([], Rest, [], Rest).
为了找出一个坐标的哪个邻居在Set中,我们使用ord_intersection/4
,它给出了Set中的邻居和Set的其余部分。
NB :列出4个邻居坐标!
open_set_closed_rest([X-Y|As], Set, [X-Y|Closed0], Rest) :-
X0 #= X-1, X1 #= X + 1,
Y0 #= Y-1, Y1 #= Y + 1,
ord_intersection([X0-Y,X-Y0,X-Y1,X1-Y], Set, New, Set0),
append(New, As, Open),
open_set_closed_rest(Open, Set0, Closed0, Rest).
就是这样。有了这个,我得到以下6个解决方案:
?- xy(XY), set_region_rest(XY, Region, Rest).
XY = [1-1, 1-2, 1-3, 2-1, 2-2, 2-3, 3-1, 4-1, 4-2],
Region = [1-1, 1-2, 1-3, 2-3, 2-2, 2-1, 3-1, 4-1, 4-2],
Rest = [] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 1-6, 3-1, 4-1, 4-2],
Region = [1-1, 1-2, 1-3, 1-4, 1-5, 1-6],
Rest = [3-1, 4-1, 4-2] ;
XY = [1-1, 1-3, 1-2],
Region = [1-1, 1-2, 1-3],
Rest = [] ;
XY = [1-2, 2-1, 2-2],
Region = [1-2, 2-2, 2-1],
Rest = [] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 2-1, 2-5, 3-1, 3-5, 4-1, 4-5, 5-1, 5-2, 5-3, 5-4, 5-5],
Region = [1-1, 1-2, 1-3, 1-4, 1-5, 2-5, 3-5, 4-5, 5-5, 5-4, 5-3, 5-2, 5-1, 4-1, 3-1, 2-1],
Rest = [] ;
XY = [1-1, 1-2, 1-3, 1-4, 1-5, 2-1, 2-5, 3-1, 3-3, 3-5, 4-1, 4-5, 5-1, 5-2, 5-3, 5-4, 5-5],
Region = [1-1, 1-2, 1-3, 1-4, 1-5, 2-5, 3-5, 4-5, 5-5, 5-4, 5-3, 5-2, 5-1, 4-1, 3-1, 2-1],
Rest = [3-3].
顺便说一下,使用set_region_rest/3
作为构建块,我们可以轻松地将一组坐标分割成区域:
set_regions([], []).
set_regions([X|Xs], [R|Rs]) :-
set_region_rest([X|Xs], R, Rest),
set_regions(Rest, Rs).
现在:
?- set_regions([1-1, 1-2, 1-3, 1-4, 1-5, 1-7,
2-1, 2-5, 2-7,
3-1, 3-3, 3-5, 3-7,
4-1, 4-5,
5-1, 5-2, 5-3, 5-4, 5-5, 5-7], R).
R = [[1-1, 1-2, 1-3, 1-4, 1-5, 2-5, 3-5, 4-5,
5-5, 5-4, 5-3, 5-2, 5-1, 4-1, 3-1, 2-1],
[1-7, 2-7, 3-7],
[3-3],
[5-7]].
答案 1 :(得分:0)
可以将此问题视为union find algorithms的实例。这种算法通常利用基本上起作用的特殊数据结构 以下两个目的:
1) For a point p, find a representant p*
in the data structure
2) For two representants p1* and p2* extend
the data structure by connecting both
Here是Prolog实现,它使用线程本地事实链接/ 2 作为联盟找到数据结构。这是第二个例子 网格问题:
?- regions(L).
L = [(1, 6), (4, 2)].
技术说明:Prolog统一还有一个内置的联合查找组件, 如果你提到变量X,它将被解除引用,这是步骤1)。如果你统一X = Y并且X和Y已被解除引用,一个变量将链接到另一个变量,这是步骤2)。