How to vectorize for loop with custom index - matlab

I'm new to Matlab, so I'm not sure if this is possible. I have a simple for-loop:
for i=1:n
B.x(indexB(i)) += A.x(i);
end
Where A.x and B.x are two vectors of length n, and indexB is a vector of length n that contains the appropriate mapping from elements in A.x to B.x.
Is it possible to vectorize this loop?

I think so, following this example:
a = [1 2 3 4 5];
b = a;
idx = [5 4 3 2 1];
a(idx) = a(idx) + b(1:5);
Which should give:
a =
6 6 6 6 6
So in your case, if indexB has size n you can write:
B.x(indexB) = B.x(indexB) + A.x(1:n);
And otherwise:
B.x(indexB(1:n)) = B.x(indexB(1:n)) + A.x(1:n);

Related

How to save indices and values from Matrix in Matlab?

I have a 3x3 Matrix and want to save the indices and values into a new 9x3 matrix. For example A = [1 2 3 ; 4 5 6 ; 7 8 9] so that I will get a matrix x = [1 1 1; 1 2 2; 1 3 3; 2 1 4; 2 2 5; ...] With my code I only be able to store the last values x = [3 3 9].
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x=[];
for i = 1:size(A)
for j = 1:size(A)
x =[i j A(i,j)]
end
end
Thanks for your help
Vectorized approach
Here's one way to do it that avoids loops:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
[ii, jj] = ndgrid(1:size(A,1), 1:size(A,2)); % row and column indices
vv = A.'; % values. Transpose because column changes first in the result, then row
x = [jj(:) ii(:) vv(:)]; % result
Using your code
You're only missing concatenation with previous x:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = [];
for i = 1:size(A)
for j = 1:size(A)
x = [x; i j A(i,j)]; % concatenate new row to previous x
end
end
Two additional suggestions:
Don't use i and j as variable names, because that shadows the imaginary unit.
Preallocate x instead of having it grow in each iteration, to increase speed.
The modified code is:
A = [1 2 3 ; 4 5 6 ; 7 8 9];
x = NaN(numel(A),3); % preallocate
n = 0;
for ii = 1:size(A)
for jj = 1:size(A)
n = n + 1; % update row counter
x(n,:) = [ii jj A(ii,jj)]; % fill row n
end
end
I developed a solution that works much faster. Here is the code:
% Generate subscripts from linear index
[i, j] = ind2sub(size(A),1:numel(A));
% Just concatenate subscripts and values
x = [i' j' A(:)];
Try it out and let me know ;)

MATLAB multidimensional array

I have been trying to use the multidimensional array function to store NxN vectors of length n in each (i,j) entry of a 3d matrix of NxNxn dimensions.
My code looks like:
a=zeros(N,N,n);
a(i,j,:)=v_ij; %here v is a vector of length n, which differs for each i,j
However when I try to extract each vector by typing e.g. a(1,1,:) I don't get a nice vector. Rather I get something like:
ans(:,:,1) = ..
ans(:,:,2) = ..
...
I don't understand why it says ans(:,:)...
That's because each vector v_ij is stored along the 3rd dimension of your matrix, so when you access a(1,1,:), you are looking for a multidimensional array consisting of every value at the location (1,1) along the 3rd dimension of a.
Let's consider a simple example:
N = 2;
n = 3;
a = zeros(N,N,n);
for k = 1:N
for l = 1:N
v_kl = randi([0 10],1,n);
a(k,l,:) = v_kl;
end
end
The randomly-generated matrix a is a 2x2x3 matrix that looks like this:
a(:,:,1) =
4 1
4 10
a(:,:,2) =
0 2
0 5
a(:,:,3) =
2 2
9 5
Therefore, using a(1,1,:) is equivalent to getting the element located at (1,1) for all 3 dimensions, here it would be 4,0 and 2.
Indeed, calling a(1,1,:) yields:
ans(:,:,1) =
4
ans(:,:,2) =
0
ans(:,:,3) =
2
Benoit_11's answer plus squeeze should work, but I would like to propose a different solution.
Rather than creating a NxNxn array, why not make it nxNxN?
N = 2;
n = 3;
a = zeros(n,N,N);
for p = 1:N
for q = 1:N
v_pq = randi([0 10],1,n);
a(:,p,q) = v_pq;
if (p == 1) && (q == 1)
v_pq %// display vector at a(:,1,1)
end
end
end
a
v_pq =
3 4 8
a =
ans(:,:,1) =
3 3
4 9
8 7
ans(:,:,2) =
5 6
10 1
9 5
Now the vectors are stored along the first dimension, so [3 4 8] shows up as the first column of ans(:,:,1). To access it you would use a(:,p,q) rather than a(p,q,:):
a(:,1,1)
ans =
3
4
8

Get elements of vector by non-given indexes in Matlab

I have the following:
a = [1:10 1:10];
idx = [3 5 7];
b = a(idx);
b = [3 5 7];
c = a(~idx); %this syntax is not correct!
c = [1 2 4 6 8 9 10 1 2 3 4 5 6 7 8 9 10];
is there a straight forward way to get c like this? In other words I have an vector and I want to exclude the elements at the given indexes, how can I do that?
Explicit way: generate a negated logical index:
logical_idx = true(1,numel(a));
logical_idx(idx) = false;
c = a(logical_idx);
More compact code using setdiff or ismember:
c = a(setdiff(1:numel(a), idx));
or
c = a(~ismember(1:numel(a), idx));
Directly remove elements indexed by idx:
c = a;
c(idx) = [];

Find the minimum positive difference between elements in vector

A = [1 3 5 8]
B = [1 2 3 4 5 6 7 8]
I would like to create a vector C which returns the rownumber of the element in vector A with the smallest non-negative difference to each element in vector B.
So, given the example above, it should return:
C = [1 2 2 3 3 4 4 4]
I'm sure there are many ways to do this. Here's one:
A = [1 3 5 8]
B = [1 2 3 4 5 6 7 8]
%create matrices of the values to subtract
[a,b] = meshgrid(A,B);
%subtract
aLessB = a-b;
%make sure we don't use the negative values
aLessB(aLessB < 0) = Inf;
%sort the subtracted matrix
[dum, idx] = sort(aLessB, 2, 'ascend');
idx(:,1) is the solution you are looking for.
An alternative solution:
D = bsxfun(#minus, A', B);
D(D < 0) = Inf;
[~, C] = min(D, [], 1);

Matlab reshape two vectors in a particular way

I have two vectors and I want the numbers to follow one each other. By this I mean:
a = [5 6 4 2 1];
b = [4 2 1 3];
Vector b can be smaller than a by one or can be the same length
I want to get
c = [5 4 6 2 4 1 2 3 1];
I tried to use reshape but gave up. So I just implemented the loop.
But is there a better way to solve this problem?
You can use sliced assignment:
% prepare c
c = zeros(1, length(a) + length(b));
% assign a
c(1:2:length(a)*2) = a;
% assign b
c((1:2:length(b)*2)+1) = b;
Note: This solution does not verify if either a or b are too short. Too long a or b will give an error though.
AFAIK reshape is only usable to change the dimensions of a single array/matrix.
Why not use simple concatenation and reordering?
>> a = [5 6 4 2 1];
>> b = [4 2 1 3];
>> c = [a b]; % initialize by concatenation
>> c([1:2:end 2:2:end]) = c % reorder by sliced re-assignment
c =
5 4 6 2 4 1 2 3 1