我试图重新熟悉Prolog,我认为这可能是Prolog中优雅解决方案的问题类型。
我跟着这个例子:
http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/hierarchical.html
我尝试了各种数据格式:
dist('BA','FI',662).
dist(0,'BA','FI',662).
dist(['BA'],['FI'],662).
但我还没找到任何一个最适合的人。
这里是第一种格式的所有数据:
%% Graph distances
dist('BA','FI',662).
dist('BA','MI',877).
dist('BA','NA',255).
dist('BA','RM',412).
dist('BA','TO',996).
dist('FI','MI',295).
dist('FI','NA',468).
dist('FI','RM',268).
dist('FI','TO',400).
dist('MI','NA',754).
dist('MI','RM',564).
dist('MI','TO',138).
dist('NA','RM',219).
dist('NA','TO',869).
dist('RM','TO',669).
现在,似乎有一些很棒的结构可以利用这个问题,但我真的很难掌握它。我想我在这里得到了第一个集群(认为它可能不是最优雅的方式;)
minDist(A,B,D) :- dist(A,B,D), dist(X,Y,Z), A \= X, A \= Y, B \= X, B \= Y, D < Z.
min(A,B,B) :- B < A
min(A,B,A) :- A < B
dist([A,B],C, D) :- minDist(A,B,D), dist(A,C,Q), dist(B,C,W), min(Q,W,D)
我在这里遇到的问题是&#34;替换&#34;涉及A和B的dist
语句与集群。
这很快成为了我的脑筋急转弯,而且我被卡住了。关于如何制定这个的任何想法?或者这可能不是Prolog优雅解决的那种问题?
答案 0 :(得分:2)
你的桌子实际上是完美的!问题是您没有中间数据结构。我猜你会发现以下代码非常令人惊讶。在Prolog中,您可以简单地使用您想要的任何结构,它实际上可以工作。首先让我们得到计算距离所需的初步,而不考虑参数顺序:
distance(X, Y, Dist) :- dist(X, Y, Dist) ; dist(Y, X, Dist).
如果第一次尝试没有距离,这只是交换订单。
我们需要的另一个实用程序:城市列表:
all_cities(['BA','FI','MI','NA','RM','TO']).
这只是有用的;我们可以计算它,但它看起来很乏味和奇怪。
好的,所以链接文章的结尾清楚地表明实际创建的是树结构。文章根本没有显示树,直到你结束,所以在合并中发生的事情并不明显。在Prolog中,我们可以简单地使用我们想要的结构,它就是这样,并且它将起作用。为了演示,让我们使用类似member/2
的列表列出树中的项目:
% Our clustering forms a tree. So we need to be able to do some basic
% operations on the tree, like get all of the cities in the tree. This
% predicate shows how that is done, and shows what the structure of
% the cluster is going to look like.
cluster_member(X, leaf(X)).
cluster_member(X, cluster(Left, Right)) :-
cluster_member(X, Left) ; cluster_member(X, Right).
因此,您可以看到我们将使用leaf('FI')
来使用树,以表示叶节点,N = 1的群集和cluster(X,Y)
来表示具有两个分支的集群树。上面的代码允许您枚举群集中的所有城市,我们需要计算它们之间的最小距离。
% To calculate the minimum distance between two cluster positions we
% need to basically pair up each city from each side of the cluster
% and find the minimum.
cluster_distance(X, Y, Distance) :-
setof(D,
XCity^YCity^(
cluster_member(XCity, X),
cluster_member(YCity, Y),
distance(XCity, YCity, D)),
[Distance|_]).
这可能看起来很奇怪。我在这里作弊。 setof/3
metapredicate为特定目标找到解决方案。调用模式类似于setof(Template, Goal, Result)
,其中Result
将成为每个Template
成功的Goal
列表。这与bagof/3
类似,只是setof/3
为您提供唯一结果。它是如何做到的?通过排序!我的第三个参数是[Distance|_]
,说只是给我结果列表中的第一个项目。由于结果已排序,因此列表中的第一项将是最小的。这是一个很大的骗局!
XCity^YCity^
符号表示setof/3
:我并不关心这些变量 的含义。它将它们标记为&#34;存在变量。&#34;这意味着Prolog不会为每个城市组合提供多种解决方案;他们将被抛在一起并整理一次。
这就是我们执行群集所需的全部内容!
从文章中,基本情况是剩下两个集群:只需将它们组合起来:
% OK, the base case for clustering is that we have two items left, so
% we cluster them together.
cluster([Left,Right], cluster(Left,Right)).
归纳案例获取结果列表并找到最接近的两个并将它们组合起来。等等!
% The inductive case is: pair up each cluster and find the minimum distance.
cluster(CityClusters, FinalCityClusters) :-
CityClusters = [_,_,_|_], % ensure we have >2 clusters
setof(result(D, cluster(N1,N2), CC2),
CC1^(select(N1, CityClusters, CC1),
select(N2, CC1, CC2),
cluster_distance(N1, N2, D)),
[result(_, NewCluster, Remainder)|_]),
cluster([NewCluster|Remainder], FinalCityClusters).
Prolog的内置排序是对第一个参数的结构进行排序。我们通过创建一个新结构result/3
在这里再次欺骗 ,它将包含距离,具有该距离的群集以及要考虑的其余项目。 select/3
非常非常方便。它的工作原理是将一个项目拉出列表,然后返回没有该项目的列表。我们在这里使用它两次从列表中选择两个项目(我不必担心将一个地方与自己进行比较!)。 CC1被标记为自由变量。将创建结果结构,以便考虑每个可能的集群以及我们给出的项目。同样,setof/3
将对列表进行排序以使其唯一,因此列表中的第一项恰好是距离最短的项目。一个setof/3
电话有很多工作要做,但我喜欢作弊!
最后一行说,取新集群并将其附加到其余项目,然后递归转发给自己。该调用的结果最终将成为基本情况。
现在有效吗?让我们做一个快速肮脏的主要程序来测试它:
main :-
setof(leaf(X), (all_cities(Cities), member(X, Cities)), Basis),
cluster(Basis, Result),
write(Result), nl.
第一行是构建初始条件的一种俗气方式(所有城市都在自己的一个群集中)。第二行调用我们的谓词来聚类。然后我们写出来。我们得到了什么? (为了便于阅读,手动缩进输出。)
cluster(
cluster(
leaf(FI),
cluster(
leaf(BA),
cluster(
leaf(NA),
leaf(RM)))),
cluster(
leaf(MI),
leaf(TO)))
订单略有不同,但结果是一样的!
如果您因使用setof/3
而感到困惑(我会!),请考虑使用aggregate
库重写这些谓词,或使用简单的递归过程来汇总并找到最小值手。