我的目标是在Prolog中实现一个简单的聚类算法。
假设您有一定数量的位置。每个位置都有一个ID,它们看起来像'位置(X,Y,ID)'。
我有以下想法:基本上你拿第一个ID及其位置,然后将这个ID添加到列表中。然后,您将此ID的位置与所有其他可能的位置进行比较。然后,每个具有与原始位置在一定距离内的位置的ID也被添加到列表中。下一步是对已添加到列表中的所有新ID执行完全相同的操作。然后,您对上一步中添加的ID执行相同的操作,等等。
在没有更多可能性之后,您将拥有一个形成群集的ID列表。例如1,2,3,6和8。
对于您在ID 1开始的第一个群集。通常,您将从ID 2开始并检查其中的群集。但它已经在第一个集群中,没有理由再次看这个集群。就像我们不必查看值3,6和8一样。因为我们只知道查看这些ID会给出完全相同的集群。
所以我怎么想象有一个谓词返回一个列表列表,基本上都是所有簇。该谓词使用另一个实际生成单个集群的谓词。
我已经尝试了一些东西,但它确实没有用。
可能的地点列表:
position(1,1,1)
position(3,2,2)
position(2,4,3)
position(4,6,4)
position(5,4,5)
position(10,3,6)
position(12,2,7)
position(13,6,8)
position(16,2,9)
假设距离为3,则应该有四个簇。 1,2,3,4和5; 6和7; 8; 9。
首先是一个简单的谓词,它检查两个ID是否在彼此的特定距离内:
checkDistance(ID1,ID2,Distance) :-
position(X1,Y1,ID1),
position(X2,Y2,ID2),
abs(X2 - X1) =< Distance,
abs(Y2 - Y1) =< Distance.
以下代码创建一个接近初始ID的ID列表:
compareAllPos(ID,Val,[],Distance) :-
\+ checkDistance(ID,Val,Distance).
compareAllPos(ID,Val,[Val|LS],Distance) :-
checkDistance(ID,Val,Distance),
Val2 is Val+1,
compareAllPos(ID,Val2,LS,Distance).
它提供以下输出:
?- compareAllPos(1,1,List,3).
List = [1, 2, 3] ;
这只解决了第一部分。现在我必须浏览添加到列表中的每个值。我通过添加另一个compareAllPos来实现这一点:
compareAllPos(ID,Val,[Val|LS],Distance) :-
checkDistance(ID,Val,Distance),
Val2 is Val+1,
append(LS1,LS2,LS),
compareAllPos(Val,1,LS1,Distance),
compareAllPos(ID,Val2,LS2,Distance).
下一个问题是程序现在陷入了无限循环,因此需要进行检查以确保尚未看到该ID。
我有以下想法:
compareAllPos2(ID,Val,_,[],Distance) :-
\+ checkDistance(ID,Val,Distance).
compareAllPos2(ID,_,Visited,[],_) :-
member(ID,Visited).
compareAllPos2(ID,Val,Visited,[Val|LS],Distance) :-
checkDistance(ID,Val,Distance),
Val2 is Val+1,
append(LS1,LS2,LS),
append(ID,Visited,Visited1),
compareAllPos2(Val,1,Visited1,LS1,Distance),
append([],Visited1,Visited2),
compareAllPos2(ID,Val2,Visited2,LS2,Distance).
我会使用以下行来调用它:
compareAllPos2(1,1,[],List,3).
这就是我遇到的问题。上面的代码无法正常工作,我还没有想出解决这个问题的正确方法。如果该代码有效,则应根据ID返回单个群集。最后一步是查看其他可能的集群。
答案 0 :(得分:1)
添加最后一个compareAllPos / 4后,您将获得
?- compareAllPos(1,1,List,3).
List = [1, 2, 3] ;
List = [1, 2, 3, 1, 2, 3, 4, 5] ;
List = [1, 2, 3, 1, 2, 3, 4, 5] ;
List = [1, 2, 3, 1, 2, 3, 4, 5] ;
List = [1, 2, 3, 1, 2, 3, 4, 5] ;
List = [1, 2, 3, 1, 2, 1, 2, 3, 3|...] ;
List = [1, 2, 3, 1, 2, 1, 2, 3, 3|...] ;
...
我认为可能是循环的原因,你应该保持简单。我会写
checkDistance(ID1,ID2,Distance) :-
position(X1,Y1,ID1),
position(X2,Y2,ID2),
ID1 \= ID2,
abs(X2 - X1) =< Distance,
abs(Y2 - Y1) =< Distance.
clusters(Distance, Cs) :-
once(position(_,_,First)),
clusters(Distance, First, [], Cs).
clusters(Distance, Pivot, SoFar, Cs) :-
findall(Tentative,
( pos_not_clustered(SoFar, Tentative),
checkDistance(Pivot, Tentative, Distance)
),
Cluster),
Updated = [[Pivot|Cluster]|SoFar],
( pos_not_clustered(Updated, NextPivot)
-> clusters(Distance, NextPivot, Updated, Cs)
; Cs = Updated
).
pos_not_clustered(Clusters, ID) :-
position(_,_,ID),
maplist(not_member(ID), Clusters).
not_member(E, L) :- \+ memberchk(E, L).
因为它产生了
?- clusters(3,C).
C = [[9], [6, 7, 8], [4, 5], [1, 2, 3]].
编辑抱歉有错误的代码,我已经改为
了clusters(Distance, Cs) :-
once(position(_,_,First)),
clusters(Distance, [[First]], Cs).
clusters(Distance, SoFar, Cs) :-
( pos_not_clustered(SoFar, Tentative),
add_to_cluster(SoFar, Tentative, Distance, Updated)
-> clusters(Distance, Updated, Cs)
; Cs = SoFar
).
pos_not_clustered(Clusters, ID) :-
position(_,_,ID),
maplist(not_member(ID), Clusters).
not_member(E, L) :- \+ memberchk(E, L).
add_to_cluster([C|Cs], Tentative, Distance, Updated) :-
member(Id, C),
checkDistance(Id, Tentative, Distance), !,
Updated = [[Tentative|C]|Cs].
add_to_cluster([C|Cs], Tentative, Distance, [C|Rs]) :-
add_to_cluster(Cs, Tentative, Distance, Rs).
add_to_cluster([], Tentative, _Distance, [[Tentative]]).
现在我得到了
?- clusters(3,C).
C = [[5, 4, 3, 2, 1], [8, 7, 6], [9]].