Prolog中的层次凝聚聚类

Prolog中的层次凝聚聚类,prolog,hierarchical-clustering,Prolog,Hierarchical Clustering,我试图重新熟悉Prolog,我认为这可能是Prolog中优雅解决方案的问题类型 我将遵循以下示例: 我尝试了多种数据格式: 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

我试图重新熟悉Prolog,我认为这可能是Prolog中优雅解决方案的问题类型

我将遵循以下示例:

我尝试了多种数据格式:

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).
现在,这个问题似乎有一些很棒的结构可以利用,但我真的很难掌握它。我想我在这里得到了第一个集群(虽然这可能不是最优雅的方式;)

mindsist(A,B,D):-dist(A,B,D),dist(X,Y,Z),A\=X,A\=Y,B\=X,B\=Y,D
我这里的问题是用集群“替换”涉及A和B的
dist
语句的概念


这很快就成了我的脑筋急转弯,我被卡住了。有没有关于如何表述这一点的想法?或者这可能不是用Prolog优雅地解决的问题吗?

您的表格实际上是完美的!问题是您没有中间数据结构。我猜您会发现下面的代码非常令人惊讶。在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
元谓词为特定目标找到解决方案。调用模式类似于
setof(模板、目标、结果)
,其中
Result
将成为每个
Goal
成功的
Template
列表。这与
bagof/3
类似,不同之处在于
setof/3
提供了独特的结果。它是怎么做到的?通过分类!我的第三个参数是
[Distance |!]
,表示只需给我结果列表中的第一项。由于结果已排序,因此列表中的第一项将是最小的。这是个大骗局

XCity^YCity^
符号表示
setof/3
:我不在乎这些变量实际上是什么。它将它们标记为“存在变量”。这意味着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
,它将包含距离、具有该距离的集群以及要考虑的其余项目<代码>选择/3
非常方便。它的工作原理是从列表中拉出一个项目,然后返回没有该项目的列表。我们在这里使用它两次,从列表中选择两个项目(我不必担心将一个地方与它本身进行比较!)。CC1被标记为自由变量。结果结构将被创建,用于考虑每个可能的集群以及我们给出的项目。同样,
setof/3
将对列表进行排序以使其唯一,因此列表中的第一项恰好是距离最短的项。一次
setof/3
呼叫需要做很多工作,但我喜欢作弊

最后一行说,获取新集群并将其附加到剩余的项目中,然后递归地将其转发给我们自己。调用的结果最终将是基本情况

现在它起作用了吗?让我们做一个quick-n-dirty主程序来测试它:

main :-
    setof(leaf(X), (all_cities(Cities), member(X, Cities)), Basis),
    cluster(Basis, Result),
    write(Result), nl.
第一条线是构建初始条件(所有城市都在它们自己的一个集群中)的一种低俗方法。第二行调用谓词来对事物进行聚类。然后我们把它写出来。我们得到了什么?(输出手动缩进以确保可读性。)

顺序略有不同,但结果是一样的


如果我对使用<代码> SET/OS/<代码>(我将是)感到困惑,那么考虑使用<代码>聚合< /C>库或用简单的递归过程重写这些谓词,用手聚合并找到最小值。

你是否考虑过使用<代码> AsStudio和<代码>缩回< /代码>?
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)))