【问题标题】:How to find connected components in Matlab?Matlab中如何查找连通分量?
【发布时间】:2013-05-28 19:33:00
【问题描述】:

数组 A =

 2     3
 2     5
 4     8
 5     6
 7     8

我想得到'conidx = [2 3 5 6] and [4 7 8]'的结果。


[2 3]的值之一存在于第2行,

[2 5]的值之一存在于第4行,

所以[2 3]、[2 5]和[5 6]是连通的,

最后我可以得到连接索引为 [2 3 5 6]。

否则,第5行存在[4 8]的值之一,

所以 [4 8] 和 [7 8] 是相连的,最后我可以得到相连的索引为 [4 7 8]。

[3][2][5][6]和[4][8][7]

【问题讨论】:

  • 嗨,Shai,这对我帮助很大。谢谢。

标签: algorithm matlab graph-algorithm connectivity


【解决方案1】:

构建图表并使用graphconncomp

G = sparse( A(:,1), A(:,2), 1, max(A(:)), max(A(:)) );
G = G + G.'; %' make graph undirected
[S C] = graphconncomp( G ); % find connected components

对于那些没有生物信息学工具箱的人

我在 mex 中的 graphconncomp 实现:


graph_conn_comp.m的代码


function [l c] = graph_conn_comp(sA)
% 
%  Computing connected components of an undirected graph - assuming sA is symmetric
% 
%  Usage:
%   [l c] = graph_conn_comp(sA);
% 
%  Inputs:
%   sA - sparse adjacency matrix (for directed graph - does not have to be symmetric)
% 
%  Outputs:
%   l - components labels
%   c - number of connected components
% 
% 
%  Compile using:
%  >> mex -O -largeArrayDims graph_conn_comp_mex.cpp
% 
% 

sA = spfun(@(x) ones(size(x)),sA);
if ~isequal(sA, sA')
    [ii jj] = find(sA);
    sA = sparse([ii jj],[jj ii], ones(1, 2*numel(ii)), size(sA,1), size(sA,2));
end
[l c] = graph_conn_comp_mex(sA);
l = double(l); % make it compatible of the rest of Matlab

graph_conn_comp_mex.cpp 的代码将在 matlab 中使用

编译
>> mex -largeArrayDims -O graph_conn_comp_mex.cpp


#include "mex.h"
#include <string.h> // for memset
/*
 * Computing connected components of an undirected graph - assuming sA is symmetric
 *
 * Usage:
 *  [l c] = graph_conn_comp_mex(sA);
 *
 * Inputs:
 *  sA - sparse adjacency matrix (for directed graph - does not have to be symmetric)
 *
 * Outputs:
 *  l - components labels
 *  c - number of connected components
 *
 *
 * Compile using:
 * >> mex -O -largeArrayDims graph_conn_comp_mex.cpp
 *
 */
#line   __LINE__  "graph_conn_comp_mex"
#define     STR(s)      #s  
#define     ERR_CODE(a,b)   a ":" "line_" STR(b)
// inputs
enum {
    AIN = 0,
    NIN };
// outputs
enum {
    LOUT = 0,
    COUT,
    NOUT };
void
ConnComp(const mxArray* sA, unsigned int* pl, const double* pnc, mwIndex start_node);
mwIndex
FindUnLabeled(unsigned int* pl, mwIndex n);

void mexFunction( 
    int nout,
    mxArray* pout[],
    int nin,
    const mxArray* pin[])  {
    if ( nin != NIN )
         mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"must have %d inputs", NIN);
    if (nout==0)
        return;
    if (nout != NOUT )
         mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"must have exactly %d output", NOUT);

    if ( mxIsComplex(pin[AIN]) || !mxIsSparse(pin[AIN]) )
        mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"sA must be real sparse matrix");
    if ( mxGetM(pin[AIN]) != mxGetN(pin[AIN]) )
        mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"sA must be square matrix");

    mwIndex n = mxGetM(pin[AIN]); // number of variables
    mwIndex ii(0);
    // allocate outputs
    pout[LOUT] = mxCreateNumericMatrix(1, n, mxUINT32_CLASS, mxREAL);
    unsigned int* pl = (unsigned int*)mxGetData(pout[LOUT]);
    memset(pl, 0, n*sizeof(unsigned int)); // set initial labels to zero
    unsigned int cl = 0;
    // number of components
    pout[COUT] = mxCreateDoubleMatrix(1, 1, mxREAL);
    double* pnc = mxGetPr(pout[COUT]); 
    pnc[0] = 0; // number of components
    ii = 0;
    do {
        ConnComp(pin[AIN], pl, pnc, ii); // find conn comp
        pnc[0]++;        
        ii = FindUnLabeled(pl, n);
    } while ( ii < n );
}

mwIndex
FindUnLabeled(unsigned int* pl, mwIndex n) {
    for ( mwIndex ii(0); ii<n ; ii++ ){
        if ( pl[ii]==0 ) {
            return ii;
        }
    }
    return n+1;
}

void
ConnComp(const mxArray* sA, unsigned int* pl, const double* pnc, mwIndex start_node) {
    mwIndex n = mxGetM(sA);
    unsigned int curr_label = (unsigned int)(pnc[0]+1);
    mwIndex *stack = (mwIndex*)mxMalloc( (n)*sizeof(mwIndex) ); 
    memset(stack, 0, (n)*sizeof(mwIndex));
    mwIndex sp(0); // stack pointer
    // put start_node on top of stack after labeling it
    pl[start_node]=curr_label;
    stack[sp] = start_node;
    sp++;    
    mwIndex* pir = mxGetIr(sA);
    mwIndex* pjc = mxGetJc(sA);
    mwIndex  ii(0), neighbor(0);
    while ( sp > 0 ) {       
        // pop start_label from stack
        sp--;
        start_node = stack[sp];
        for ( ii = pjc[start_node] ; ii < pjc[start_node+1] ; ii++ ) {
            neighbor = pir[ii];
            if (pl[neighbor]==0) { // unlabeled
                pl[neighbor] = curr_label; // label it
                // push it into stack
                stack[sp] = neighbor;
                sp++;
            } else {
                if (pl[neighbor]!=curr_label)
                    mexErrMsgIdAndTxt(ERR_CODE(__FILE__, __LINE__),"Got mixed labeling %d <-> %d",pl[neighbor], curr_label);
            }
        }
    }
    mxFree(stack);
}

【讨论】:

  • @EitanT 确实如此。但实施起来并不难。不久前我自己mexed 了。
  • 当然。另外,我建议您使用索引而不是A 的实际值,以提高内存效率...
  • @EitanT 你能详细说明这个内存效率问题吗?
  • @嗯,如果 A 的值是 2000 而不是 2,那么您的邻接矩阵 G 将不必要地膨胀。
  • @Eitan,是的 uniqueing A 作为预处理很重要。我假设A 已经“压缩”了...
【解决方案2】:

对于那些没有graphconncomp 并且不想编译 Shai 的 mex 函数的人。我有posted 一个纯matlab 替代graphconncomp 作为gptoolbox 的一部分

function [S,C] = conncomp(G)
  [p,q,r] = dmperm(G'+speye(size(G)));
  S = numel(r)-1;
  C = cumsum(full(sparse(1,r(1:end-1),1,1,size(G,1))));
  C(p) = C;
end

对于大型稀疏输入,它也比 graphconncomp 快约 10 倍。

【讨论】:

    猜你喜欢
    • 2011-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多