Prolog中的层次凝聚聚类

时间:2014-07-12 22:48:12

标签: prolog hierarchical-clustering

我试图重新熟悉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优雅解决的那种问题?

1 个答案:

答案 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库重写这些谓词,或使用简单的递归过程来汇总并找到最小值手。