Construire une matrice de contiguïté dans MATLAB

tenir compte d'un ensemble de points disposés sur une grille de taille N-par-M. J'essaie de construire la matrice de voisinage de telle sorte que les points voisins sont connectés.

Par exemple, dans une grille 3x3 avec un graphique:

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

nous devrions avoir la matrice de contiguïté correspondante:

+---+------------------------------------------------------+
|   |   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  |
+---+------------------------------------------------------+

en bonus, la solution devrait fonctionner pour les points voisins connectés 4 et 8, c'est-à-dire:

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

le code que j'ai donc loin:

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

comment cela peut-il être amélioré pour éviter toute la boucle?

29
demandé sur Peter Mortensen 2010-07-19 02:36:25

6 réponses

si vous remarquez, il y a un motif distinct dans les matrices de contiguïté que vous créez. Plus précisément, ils sont symétriques et bandes. Vous pouvez profiter de ce fait pour créer facilement vos matrices en utilisant le diag fonction (ou spdiags fonction de si vous voulez faire une matrice creuse). Voici comment vous pouvez créer la matrice de contiguïté pour chaque cas, en utilisant votre matrice d'échantillon ci-dessus comme exemple:

4-connectés voisins:

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

Et vous obtiendrez le tableau suivant:

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-connecté voisins:

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

Et vous obtiendrez le tableau suivant:

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
24
répondu gnovice 2018-07-16 16:58:37

juste pour le plaisir, voici une solution pour construire la matrice de contiguïté en calculant la distance entre toutes les paires de points sur la grille (pas la manière la plus efficace évidemment)

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 );

et voici un peu de code pour visualiser la matrice de contiguïté et le graphe des points connectés:

%# 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)')) )

8_connected4_connected

14
répondu Amro 2014-08-28 15:04:51

je viens de trouver cette question en cherchant le même problème. Cependant, aucune des solutions proposées n'a fonctionné pour moi en raison de la taille du problème qui nécessitait l'utilisation de types de matrice épars. Voici ma solution qui fonctionne sur des instances à grande échelle:

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
répondu mnmltype 2013-10-10 11:49:43

Votre code actuel ne semble pas si mal. D'une façon ou d'une autre, vous devez itérer sur tous les paires de voisins. Si vous avez vraiment besoin d'optimiser le code, je dirais:

  • boucle sur le nœud indices i, où 1 <= i <= (N*M)
  • n'utilisez pas sub2ind () pour l'efficacité, les voisins de node i sont simpy [i-M, i+1, i+M, i-1] dans le sens des aiguilles d'une montre

notez que pour obtenir toutes les paires de noeuds de voisinage:

  • il suffit de calculer les "bons" voisins (i.e. bords horizontaux) pour les noeuds i % M != 0 (puisque Matlab n'est pas basé sur 0 mais sur 1)
  • vous n'avez qu'à calculer "au-dessus" des voisins (c.-à-d. les bords verticaux) pour les noeuds i > M
  • il existe une règle similaire pour les bords diagonaux

ceci donnerait une boucle simple (mais le même nombre D'itérations N*M), n'appelle pas sub2ind (), et n'a que deux instructions if dans la boucle.

2
répondu catchmeifyoutry 2010-07-19 03:24:35

Viens de tomber sur cette question. J'ai un beau travail m-fonction (lien: sparse_adj_matrix.m) qui est assez général.


Il peut également prendre en charge la 3D (et arbitrairement des grilles de domension plus élevées).

La fonction peut aussi connecter des noeuds plus loin que radius = 1.

Voici la signature de la fonction:


% 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(:)]);
2
répondu Shai 2015-05-14 05:22:54

pour chaque noeud dans le graphe ajouter une connexion à droite et une vers le bas. Vérifiez que vous n'exagérez pas votre grille. Considérons la fonction suivante qui construit la matrice de contiguïté.

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

l'Exemple ci-dessus 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
0
répondu ja72 2010-07-19 16:33:37