想象一下,我们有很多点,其中一些连接在一起,我们想要聚类它们。
请看下图。
如果我们有" 连接矩阵"我们如何将它们聚集在两组(连接点组)中?
ConnectivityMatrix=
[1 2
1 3
2 4
2 3
2 1
3 1
3 2
3 4
4 3
4 2
5 8
5 7
5 6
6 5
6 7
7 6
7 5
7 8
8 7
8 5]
最终结果应该是第一组(群集)中1,2,3,4
的节点和第二组(群集)中5,6,7,8
的节点。
答案 0 :(得分:3)
以下是一些可以帮助您入门的代码。我基本上实现了深度优先搜索...无论如何都是非常粗略的实现。
Depth First Search是一种用于遍历树的算法。图表本质上是树的一种特殊情况,其中有叶节点连接回根。深度优先搜索的基本算法如下:
如果我们有上面的断开图表,我们基本上会多次运行深度优先搜索。每次都是针对一个群集。在一个Depth First Search结果之后,我们将发现属于一个集群的节点。我们再次使用我们尚未触及的任何节点重新启动深度优先搜索,这将是来自我们尚未访问的另一个群集的节点。由于您的图表结构中显然有两个聚类,我们必须运行两次深度优先搜索。这通常被称为在整体图表中查找所有连接的组件。
要查找已连接的组件,请执行以下步骤:
不用多说,这就是代码。请记住,这不是战斗测试。如果您的图形结构会产生错误,那么您将自行修复:)
ConnectivityMatrix = [1 2
1 3
2 4
2 3
2 1
3 1
3 2
3 4
4 3
4 2
5 8
5 7
5 6
6 5
6 7
7 6
7 5
7 8
8 7
8 5];
%// Find all possible node IDs
nodeIDs = unique(ConnectivityMatrix(:));
%// Flag that tells us if there are any nodes we should visit
nodeIDList = false(1,numel(nodeIDs));
%// Stores our list of clusters
clusterList = {};
%// Keeps track of how many clusters we have
counter = 1;
%// Stack - initialize to empty
stackNodes = [];
%// While there is at least one node we need to visit
while (~all(nodeIDList))
% Find any node
stackNodes = find(nodeIDList == false, 1);
% Initialize our stack to contain this node
nodesCluster = stackNodes;
%// While our stack is not empty
while (~isempty(stackNodes))
% Grab the node off the stack and pop off
node = stackNodes(end);
stackNodes(end) = [];
%// If we have marked this node as visited, skip
if (nodeIDList(node))
continue;
end
%// Mark as visited
nodeIDList(node) = true;
%// Retrieve all nodes connected to this node
connectedNodes = ConnectivityMatrix(ConnectivityMatrix(:,1) == node, :);
nodesToVisit = unique(connectedNodes(:,2).');
%// Remove those already visited
visitedNodes = ~nodeIDList(nodesToVisit);
finalNodesToVisit = nodesToVisit(visitedNodes);
%// Add to cluster
nodesCluster = unique([nodesCluster finalNodesToVisit]);
%// Add to stack
stackNodes = unique([stackNodes finalNodesToVisit]);
end
%// Add connected components to its own cluster
clusterList{counter} = nodesCluster;
counter = counter + 1;
end
运行此代码后,我们可以像这样显示我们的集群:
celldisp(clusterList)
clusterList{1} =
1 2 3 4
clusterList{2} =
5 6 7 8
因此,集群#1包含节点1,2,3,4,而集群#2包含节点5,6,7,8。
请记住,只有在您按照图表中的顺序标记节点时,此代码才有效。你不能跳过任何标签号码(即你不能做1,2,4,6,9等等。这应该是1,2,3,4,5)。
祝你好运!答案 1 :(得分:1)
您可以使用“现成的”Matlab命令来解决此问题。例如,您可以使用graphconncomp
。
答案 2 :(得分:1)
The answer from rayryeng is pretty good。但是,我想指出一些细节:
numel(nodeIDs)
"如果您的节点标签不是顺序的(即,您有10个节点且最大节点标签为20),则会导致可能的错误。你可以切换" numel(nodeIDs)
"到" max(nodeIDs)
或更大的值。" 我还认为以下代码在使用此功能时会带来一些问题(即某些节点丢失并成为隔离节点):
connectedNodes = ConnectivityMatrix(ConnectivityMatrix(:,1) == node, :);
nodesToVisit = unique(connectedNodes(:,2).');
我用以下凌乱的代码修改了简单的两行:
connectedNodes1 = ConnectivityMatrix (ConnectivityMatrix (:,1) == node, :);
connectedNodes2 = ConnectivityMatrix (ConnectivityMatrix (:,2) == node, :);
AC=connectedNodes1(:,2).';
AD=connectedNodes2(:,1).';
ACA=reshape(AC,[],1);
ADA=reshape(AD,[],1);
AE= [ACA; ADA] ;
AEA=reshape(AE,[],1);
AEA=AEA';
nodesToVisit = unique(AEA);
修改这两点后,rayryeng的初始代码没有问题。