How to vectorize row-wise diagonalization of a mat

2019-02-25 18:42发布

问题:

I have an n-by-m matrix that I want to convert to a mn-by-m matrix, with each m-by-m block of the result containing the diagonal of each row.

For example, if the input is:

[1 2; 3 4; 5 6]

the output should be:

[1 0; 0 2; 3 0; 0 4; 5 0; 0 6]

Of course, I don't want to assemble the matrix step by step myself with a for loop.
Is there a vectorized and simple way to achieve this?

回答1:

For a vectorized way to do this, create the linear indices of the diagonal elements into the resulting matrix, and assign directly.

%# create some input data
inArray = [10 11;12 13;14 15];

%# make the index array
[nr,nc]=size(inArray);

idxArray = reshape(1:nr*nc,nc,nr)';
idxArray = bsxfun(@plus,idxArray,0:nr*nc:nr*nc^2-1);

%# create output
out = zeros(nr*nc,nc);
out(idxArray) = inArray(:);

out =

    10     0
     0    11
    12     0
     0    13
    14     0
     0    15


回答2:

Here's a simple vectorized solution, assuming X is the input matrix:

Y = repmat(eye(size(X, 2)), size(X, 1), 1);
Y(find(Y)) = X;

Another alternative is to use sparse, and this can be written as a neat one-liner:

Y = full(sparse(1:numel(X), repmat(1:size(X, 2), 1, size(X, 1)), X'));


回答3:

The easiest way I see to do this is actually quite simple, using simple index referencing and the reshape function:

I = [1 2; 3 4; 5 6];
J(:,[1,4]) = I;
K = reshape(J',2,6)';

If you examine J, it looks like this:

J =
     1     0     0     2
     3     0     0     4
     5     0     0     6

Matrix K is just what wanted:

K =
     1     0
     0     2
     3     0
     0     4
     5     0
     0     6

As Eitan T has noted in the comments, the above is specific to the example, and doesn't cover the general solution. So below is the general solution, with m and n as described in the question.

J(:,1:(m+1):m^2) = I;
K=reshape(J',m,m*n)';

If you want to test it to see it working, just use

I=reshape(1:(m*n),m,n)';

Note: if J already exists, this can cause problems. In this case, you need to also use

J=zeros(n,m^2);


回答4:

It may not be the most computationally efficient solution, but here's a 1-liner using kron:

A = [1 2; 3 4; 5 6];
B = diag(reshape(A', 6, 1) * kron(ones(3, 1), eye(2))
% B = 
%     1     0
%     0     2
%     3     0
%     0     4
%     5     0
%     0     6

This can be generalized if A is n x m:

diag(reshape(A.', n*m, 1)) * kron(ones(n,1), eye(m))