【问题标题】:Construct adjacency matrix in MATLAB在MATLAB中构造邻接矩阵
【发布时间】:2011-03-17 16:37:47
【问题描述】:

考虑排列在大小为 N×M 的网格上的一组点。 我正在尝试构建邻接矩阵,使得 相邻点相连。

例如,在带有图表的 3x3 网格中:

1-2-3
| | |
4-5-6
| | |
7-8-9

我们应该有对应的邻接矩阵:

+---+------------------------------------------------------+
|   |   1     2     3     4     5     6     7     8     9  |
+---+------------------------------------------------------+
| 1 |   0     1     0     1     0     0     0     0     0  |
| 2 |   1     0     1     0     1     0     0     0     0  |
| 3 |   0     1     0     0     0     1     0     0     0  |
| 4 |   1     0     0     0     1     0     1     0     0  |
| 5 |   0     1     0     1     0     1     0     1     0  |
| 6 |   0     0     1     0     1     0     0     0     1  |
| 7 |   0     0     0     1     0     0     0     1     0  |
| 8 |   0     0     0     0     1     0     1     0     1  |
| 9 |   0     0     0     0     0     1     0     1     0  |
+---+------------------------------------------------------+

作为奖励,该解决方案应该适用于 4 和 8 连接的相邻点,即:

   o             o  o  o
o  X  o   vs.    o  X  o
   o             o  o  o

这是我到目前为止的代码:

N = 3; M = 3;
adj = zeros(N*M);

for i=1:N
    for j=1:M
        k = sub2ind([N M],i,j);
        if i>1
            ii=i-1; jj=j;
            adj(k,sub2ind([N M],ii,jj)) = 1; 
        end
        if i<N
            ii=i+1; jj=j;
            adj(k,sub2ind([N M],ii,jj)) = 1; 
        end
        if j>1
            ii=i; jj=j-1;
            adj(k,sub2ind([N M],ii,jj)) = 1; 
        end
        if j<M
            ii=i; jj=j+1;
            adj(k,sub2ind([N M],ii,jj)) = 1; 
        end
    end
end

如何改进以避免所有循环?

【问题讨论】:

  • 不,这不是功课。我的最终目标是绘制这些点并在连接点之间画线作为图形。有趣的是,这些点不必停留在网格上..

标签: matlab matrix graph-theory


【解决方案1】:

如果您注意到,您正在创建的邻接矩阵有一个独特的模式。具体来说,它们是对称的和banded。您可以利用这一事实,使用diag 函数(如果您想创建稀疏矩阵,则使用spdiags 函数)轻松创建矩阵。以下是为每种情况创建邻接矩阵的方法,以上面的示例矩阵为例:

4 个连接的邻居:

mat = [1 2 3; 4 5 6; 7 8 9];                 % Sample matrix
[r, c] = size(mat);                          % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1);  % Make the first diagonal vector
                                             %   (for horizontal connections)
diagVec1 = diagVec1(1:end-1);                % Remove the last value
diagVec2 = ones(c*(r-1), 1);                 % Make the second diagonal vector
                                             %   (for vertical connections)
adj = diag(diagVec1, 1)+diag(diagVec2, c);   % Add the diagonals to a zero matrix
adj = adj+adj.';                             % Add the matrix to a transposed copy of
                                             %   itself to make it symmetric

你会得到以下矩阵:

adj =

     0  1  0  1  0  0  0  0  0
     1  0  1  0  1  0  0  0  0
     0  1  0  0  0  1  0  0  0
     1  0  0  0  1  0  1  0  0
     0  1  0  1  0  1  0  1  0
     0  0  1  0  1  0  0  0  1
     0  0  0  1  0  0  0  1  0
     0  0  0  0  1  0  1  0  1
     0  0  0  0  0  1  0  1  0


8 个连接的邻居:

mat = [1 2 3; 4 5 6; 7 8 9];                 % Sample matrix
[r, c] = size(mat);                          % Get the matrix size
diagVec1 = repmat([ones(c-1, 1); 0], r, 1);  % Make the first diagonal vector
                                             %   (for horizontal connections)
diagVec1 = diagVec1(1:end-1);                % Remove the last value
diagVec2 = [0; diagVec1(1:(c*(r-1)))];       % Make the second diagonal vector
                                             %   (for anti-diagonal connections)
diagVec3 = ones(c*(r-1), 1);                 % Make the third diagonal vector
                                             %   (for vertical connections)
diagVec4 = diagVec2(2:end-1);                % Make the fourth diagonal vector
                                             %   (for diagonal connections)
adj = diag(diagVec1, 1)+...                  % Add the diagonals to a zero matrix
      diag(diagVec2, c-1)+...
      diag(diagVec3, c)+...
      diag(diagVec4, c+1);
adj = adj+adj.';                             % Add the matrix to a transposed copy of
                                             %   itself to make it symmetric

你会得到以下矩阵:

adj =

     0  1  0  1  1  0  0  0  0
     1  0  1  1  1  1  0  0  0
     0  1  0  0  1  1  0  0  0
     1  1  0  0  1  0  1  1  0
     1  1  1  1  0  1  1  1  1
     0  1  1  0  1  0  0  1  1
     0  0  0  1  1  0  0  1  0
     0  0  0  1  1  1  1  0  1
     0  0  0  0  1  1  0  1  0

【讨论】:

    【解决方案2】:

    只是为了好玩,这是一个通过计算网格上所有点对之间的距离来构造邻接矩阵的解决方案(显然不是最有效的方法)

    N = 3; M = 3;                  %# grid size
    CONNECTED = 8;                 %# 4-/8- connected points
    
    %# which distance function
    if CONNECTED == 4,     distFunc = 'cityblock';
    elseif CONNECTED == 8, distFunc = 'chebychev'; end
    
    %# compute adjacency matrix
    [X Y] = meshgrid(1:N,1:M);
    X = X(:); Y = Y(:);
    adj = squareform( pdist([X Y], distFunc) == 1 );
    

    下面是一些可视化邻接矩阵和连接点图的代码:

    %# plot adjacency matrix
    subplot(121), spy(adj)
    
    %# plot connected points on grid
    [xx yy] = gplot(adj, [X Y]);
    subplot(122), plot(xx, yy, 'ks-', 'MarkerFaceColor','r')
    axis([0 N+1 0 M+1])
    %# add labels
    [X Y] = meshgrid(1:N,1:M);
    X = reshape(X',[],1) + 0.1; Y = reshape(Y',[],1) + 0.1;
    text(X, Y(end:-1:1), cellstr(num2str((1:N*M)')) )
    

    【讨论】:

      【解决方案3】:

      我刚刚在搜索相同问题时发现了这个问题。但是,由于需要使用稀疏矩阵类型的问题大小,所提供的解决方案都不适合我。这是我适用于大规模实例的解决方案:

      function W = getAdjacencyMatrix(I)
      
      [m, n] = size(I);
      
      I_size = m*n;
      
      % 1-off diagonal elements
      V = repmat([ones(m-1,1); 0],n, 1);
      V = V(1:end-1); % remove last zero
      
      % n-off diagonal elements
      U = ones(m*(n-1), 1);
      
      % get the upper triangular part of the matrix
      W = sparse(1:(I_size-1),    2:I_size, V, I_size, I_size)...
        + sparse(1:(I_size-m),(m+1):I_size, U, I_size, I_size);
      
      % finally make W symmetric
      W = W + W';
      

      【讨论】:

        【解决方案4】:

        刚刚遇到这个问题。我有一个很好用的 m 函数(链接:sparse_adj_matrix.m),它非常通用。

        它可以处理4-connect grid(半径1根据L1 norm),8-connect grid(radius 1根据L_infty norm)。
        它还可以支持 3D(以及任意更高的维度网格)。
        该函数还可以连接比radius = 1更远的节点。

        这是函数的签名:

        
        % Construct sparse adjacency matrix (provides ii and jj indices into the
        % matrix)
        %
        % Usage:
        %   [ii jj] = sparse_adj_matrix(sz, r, p)
        %
        % inputs:
        %   sz - grid size (determine the number of variables n=prod(sz), and the
        %        geometry/dimensionality)
        %   r  - the radius around each point for which edges are formed
        %   p  - in what p-norm to measure the r-ball, can be 1,2 or 'inf'
        %
        % outputs
        %   ii, jj - linear indices into adjacency matrix (for each pair (m,n)
        %   there is also the pair (n,m))
        %
        % How to construct the adjacency matrix?
        % >> A = sparse(ii, jj, ones(1,numel(ii)), prod(sz), prod(sz));
        %
        %
        % Example:
        % >> [ii jj] = sparse_adj_matrix([10 20], 1, inf);
        % construct indices for 200x200 adjacency matrix for 8-connect graph over a
        % grid of 10x20 nodes.
        % To visualize the graph:
        % >> [r c]=ndgrid(1:10,1:20);
        % >> A = sparse(ii, jj, 1, 200, 200);;
        % >> gplot(A, [r(:) c(:)]);
        

        【讨论】:

        • Shai,假设我有一个二值图像 (100x200),我想只为白色像素 (foreground) 获得有效的 8 邻接,即在某些情况下(i,j) 的 8 个邻居无效,因为它们可能属于背景。我只想获得前台的那些。如何使用您的 sparse_adj_matrix 脚本构建这样的邻接矩阵?
        • 以下链接中提供了二进制图像/掩码图像的示例:imgur.com/Wb432Sc
        • @Tin 一旦有了ii 和jj 对的索引,就可以只选择(使用逻辑索引)属于白色像素的索引。
        【解决方案5】:

        您当前的代码似乎还不错。您需要以一种或另一种方式迭代所有邻居对。如果你真的需要优化代码,我建议:

        • 循环遍历节点索引 i,其中1 &lt;= i &lt;= (N*M)
        • 不要使用 sub2ind() 来提高效率,节点 i 的邻居是简单的 [i-M, i+1, i+M, i-1] 顺时针顺序

        注意获取所有邻居节点对:

        • 您只需计算节点 i % M != 0 的“正确”邻居(即水平边)(因为 Matlab 不是基于 0 而是基于 1)
        • 您只需计算节点i &gt; M 的“上方”邻居(即垂直边)
        • 对角线也有类似的规则

        这将导致单个循环(但 N*M 迭代次数相同),不调用 sub2ind(),并且循环中只有两个 if 语句。

        【讨论】:

        • 没问题。我看到 jalexiou 更新了他的帖子,他发布的代码是恕我直言,是所有当前发布的解决方案中最易读和最直观的代码(尽管它仍然可以进一步优化)。例如,Gnovice 的解决方案举例说明了为什么我 讨厌 Matlab :p。哦,好吧,在 Matlab 中它可能更有效。祝你好运!
        【解决方案6】:

        为图表中的每个节点添加一个向右和向下的连接。检查你没有超出你的网格。考虑以下构建邻接矩阵的函数。

        function  adj = AdjMatrixLattice4( N, M )
            % Size of adjacency matrix
            MN = M*N;
            adj = zeros(MN,MN);
        
            % number nodes as such
            %  [1]---[2]-- .. --[M]
            %   |     |          |
            % [M+1]-[M+2]- .. -[2*M]
            %   :     :          :
            %   []    []   ..  [M*N]     
        
            for i=1:N
                for j=1:N
                    A = M*(i-1)+j;          %Node # for (i,j) node
                    if(j<N)                
                        B = M*(i-1)+j+1;    %Node # for node to the right
                        adj(A,B) = 1;
                        adj(B,A) = 1;
                    end
                    if(i<M)
                        B = M*i+j;          %Node # for node below
                        adj(A,B) = 1;       
                        adj(B,A) = 1;
                    end            
                end
            end    
        end
        

        如上例AdjMatrixLattice4(3,3)=

         0     1     0     1     0     0     0     0     0
         1     0     1     0     1     0     0     0     0
         0     1     0     0     0     1     0     0     0
         1     0     0     0     1     0     1     0     0
         0     1     0     1     0     1     0     1     0
         0     0     1     0     1     0     0     0     1
         0     0     0     1     0     0     0     1     0
         0     0     0     0     1     0     1     0     1
         0     0     0     0     0     1     0     1     0
        

        【讨论】:

        • 您没有抓住重点,我正在寻找一种适用于任何 N 和 M 值(网格大小)的解决方案。我仅以 3x3 为例。
        • 我想我错过了你想要一个完全连接的图。所以现在需要一个基于网格大小生成连接向量的函数。实际上有两个功能,一个用于4连接方案,一个用于8连接方案。有关 4 个连接的答案,请参见上文。
        • 8 节点场景更复杂,因为您必须决定连接是否可以相互交叉。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多