我用 Java 编写了一个低效但简单的算法,看看我可以多接近对一组点进行一些基本聚类,或多或少如问题中所述。
如果 (x,y) 坐标 ps 指定为 ints,则该算法适用于列表。它还需要三个其他参数:
- radius (
r):给定一个点,扫描附近点的半径是多少
- 最大地址(
maxA):每个集群的最大地址(点)数是多少?
- 最少地址 (
minA):每个集群的最少地址
设置limitA=maxA。
主要迭代:
初始化空列表possibleSolutions。
外部迭代:对于ps 中的每个点p。
初始化空列表pclusters。
定义了点wps=copy(ps) 的工作列表。
工作点wp=p。
内部迭代:而wps 不为空。
删除wps 中的点wp。确定wps 中与wp 距离r 的所有点wpsInRadius。根据与wp 的距离对wpsInRadius 进行升序排序。将第一个min(limitA, sizeOf(wpsInRadius)) 点保留在wpsInRadius 中。这些点形成一个新的集群(点列表)pcluster。将pcluster 添加到pclusters。从wps 中删除pcluster 中的点。如果wps 不为空,wp=wps[0] 并继续内部迭代。
结束内部迭代。
获得集群pclusters 的列表。将此添加到possibleSolutions。
结束外部迭代。
对于ps 中的每个p,我们都有一个pclusters 和possibleSolutions 中的集群列表。然后对每个pclusters 进行加权。如果avgPC 是possibleSolutions(全局)中每个簇的平均点数,avgCSize 是每个pclusters(全局)中的平均簇数,那么这个函数使用这两个变量来确定重量:
private static WeightedPClusters weigh(List<Cluster> pclusters, double avgPC, double avgCSize)
{
double weight = 0;
for (Cluster cluster : pclusters)
{
int ps = cluster.getPoints().size();
double psAvgPC = ps - avgPC;
weight += psAvgPC * psAvgPC / avgCSize;
weight += cluster.getSurface() / ps;
}
return new WeightedPClusters(pclusters, weight);
}
现在最好的解决方案是重量最轻的pclusters。只要我们能找到比limitA=max(minA,(int)avgPC) 之前最好的解决方案更好的解决方案(重量更轻),我们就会重复主迭代。 结束主要迭代。
请注意,对于相同的输入数据,此算法将始终产生相同的结果。列表用于保持顺序,并且不涉及随机。
要查看此算法的行为方式,这是 32 点测试模式上的结果图像。如果maxA=minA=16,那么我们会找到 2 个 16 个地址的集群。
(来源:paperboyalgorithm at sites.google.com)
接下来,如果我们通过设置minA=12 来减少每个集群的最小地址数,我们会找到 12/12/8 个点的 3 个集群。
(来源:paperboyalgorithm at sites.google.com)
为了证明该算法远非完美,这里是maxA=7 的输出,但我们得到了 6 个集群,其中一些集群很小。所以在确定参数的时候还是要猜测太多。注意r这里只有5个。
(来源:paperboyalgorithm at sites.google.com)
出于好奇,我在一组更大的随机选择的点上尝试了该算法。我在下面添加了图片。
结论?这花了我半天的时间,效率低下,代码看起来很难看,而且速度比较慢。但这表明在短时间内产生一些的结果是可能的。当然,这只是为了好玩;把它变成真正有用的东西是困难的部分。
(来源:paperboyalgorithm at sites.google.com)
(来源:paperboyalgorithm at sites.google.com)