web-dev-qa-db-fra.com

Construire une matrice d'adjacence dans MATLAB

Considérons un ensemble de points disposés sur une grille de taille N-by-M . J'essaie de construire la matrice d'adjacence de telle sorte que Des points voisins soient 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 d'adjacence 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 prime, la solution devrait fonctionner pour les points voisins à 4 et à 8 points connectés, à savoir:

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

C'est le code que j'ai jusqu'ici:

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 boucle?

30
Dave

Si vous remarquez, les matrices de contiguïté que vous créez ont un motif distinct. Plus précisément, ils sont symétriques et bandés . Vous pouvez en tirer parti pour créer facilement vos matrices à l’aide de la fonction diag (ou de la fonction spdiags si vous souhaitez créer une matrice éparse). Voici comment créer la matrice de contiguïté pour chaque cas, en utilisant l'exemple de matrice ci-dessus:

4 voisins connectés:

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 la matrice suivante:

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

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 la matrice suivante:

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
gnovice

Juste pour le plaisir, voici une solution pour construire la matrice d’adjacence en calculant la distance entre toutes les paires de points de la grille (ce n’est évidemment pas le moyen le plus efficace)

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 code pour visualiser la matrice d'adjacence 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
Amro

Je viens de trouver cette question lors de la recherche du même problème. Cependant, aucune des solutions fournies ne fonctionnait pour moi en raison de la taille du problème qui nécessitait l'utilisation de types de matrice clairsemés. Voici ma solution qui fonctionne sur les 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
mnmltype

Je viens de tomber sur cette question. J'ai une fonction m agréable à travailler (lien: sparse_adj_matrix.m ) qui est assez générale.

Il peut gérer une grille à 4 connexions (rayon 1 selon la norme L1), une grille à 8 connexions (rayon 1 selon la norme L_infty).
Il peut également prendre en charge la 3D (et des grilles de densité arbitrairement supérieures).
La fonction peut également connecter des nœuds au-delà de rayon = 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
Shai

Votre code actuel ne semble pas si mauvais. D'une manière ou d'une autre, vous devez parcourir toutes les paires de voisins. Si vous avez vraiment besoin d'optimiser le code, je vous suggère:

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

Notez que pour obtenir toutes les paires de nœuds voisins:

  • vous devez seulement calculer les "bons" voisins (c'est-à-dire les bords horizontaux) pour les nœuds i % M != 0 (puisque Matlab n'est pas basé sur 0 mais sur 1)
  • il suffit de calculer les voisins "au-dessus" (c'est-à-dire les arêtes verticales) pour les nœuds i > M
  • il existe une règle similaire pour les bords diagonaux

Cela conduirait à une seule boucle (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
catchmeifyoutry

Pour chaque nœud du graphique, ajoutez une connexion à droite et une autre vers le bas. Vérifiez que vous n'allez pas trop loin dans votre grille. Considérons la fonction suivante qui construit la matrice d'adjacence.

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

Exemple comme 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
ja72