4
votes

Apologies in advance if this question is a duplicate, or if the solution to this question is very straightforward in Matlab. I have a M x N matrix A, a 1 x M vector ind, and another vector val. For example,

A = zeros(6,5);
ind = [3 4 2 4 2 3];
val = [1 2 3];

I would like to vectorize the following code:

for i = 1 : size(A,1)
    A(i, ind(i)-1 : ind(i)+1) = val;
end

>> A

A =

 0     1     2     3     0
 0     0     1     2     3
 1     2     3     0     0
 0     0     1     2     3
 1     2     3     0     0
 0     1     2     3     0

That is, for row i of A, I want to insert the vector val in a certain location, as specificied by the i'th entry of ind. What's the best way to do this in Matlab without a for loop?

2

2 Answers

3
votes

It can be done using bsxfun's masking capability: build a mask telling where the values will be placed, and then fill those values in. In doing this, it's easier to work with columns instead of rows (because of Matlab's column major order), and transpose at the end.

The code below determines the minimum number of columns in the final A so that all values fit at the specified positions.

Your example applies a displacement of -1 with respect to ind. The code includes a generic displacement, which can be modified.

%// Data
ind = [3 4 2 4 2 3]; %// indices
val = [1 2 3]; %// values
d = -1; %// displacement for indices. -1 in your example

%// Let's go
n = numel(val);
m = numel(ind);
N = max(ind-1) + n + d;  %// number of rows in A (rows before transposition)
mask = bsxfun(@ge, (1:N).', ind+d) & bsxfun(@le, (1:N).', ind+n-1+d); %// build mask
A = zeros(size(mask)); %/// define A with zeros
A(mask) = repmat(val(:), m, 1); %// fill in values as indicated by mask
A = A.'; %// transpose

Result in your example:

A =
     0     1     2     3     0
     0     0     1     2     3
     1     2     3     0     0
     0     0     1     2     3
     1     2     3     0     0
     0     1     2     3     0

Result with d = 0 (no displacement):

A =
     0     0     1     2     3     0
     0     0     0     1     2     3
     0     1     2     3     0     0
     0     0     0     1     2     3
     0     1     2     3     0     0
     0     0     1     2     3     0
1
votes

If you can handle a bit of bsxfun overdose, here's one with bsxfun's adding capability -

N = numel(ind);
A(bsxfun(@plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)

Sample run -

>> ind
ind =
     3     4     2     4     2     3
>> val
val =
     1     2     3
>> A = zeros(6,5);
>> N = numel(ind);
>> A(bsxfun(@plus,N*[-1:1]',(ind-1)*N + [1:N])) = repmat(val(:),1,N)
A =
     0     1     2     3     0
     0     0     1     2     3
     1     2     3     0     0
     0     0     1     2     3
     1     2     3     0     0
     0     1     2     3     0