setdiff row by row without using loops in matlab - matlab

Let's say we have two matrices
A = [1,2,3;
2,4,5;
8,3,5]
B= [2,3;
4,5;
8,5]
How do I perform sediff for each row in A and B respectively without using loops or cellfun, in other words performing setdiff(A(i,:),B(i,:)) for all i. For this example I want to get
[1;
2;
3]
I am trying to do this for two very big matrices for my fluid simulator, thus I can't compromise on performance.
UPDATE:
you can assume that the second dimension (number of columns) of the answer will be fixed e.g. the answer will always be some n by m matrix and not some ragged array of different column sizes.
Another Example:
In my case A and B are m by 3 and m by 2 respectively and the answer should be m by 1. A solution for this case will suffice, but a general solution for matrices of size m by n1, m by n2 with answer of m by n3 will be very interesting. another example is
A = [1,2,3,4,5;
8,4,7,9,6]
B = [2,3;
4,9]
And the answer is
C = [1,4,5;
8,7,6]

Approach #1 Using bsxfun -
mask = all(bsxfun(#ne,A,permute(B,[1 3 2])),3);
At = A.'; %//'
out = reshape(At(mask.'),[],size(A,1)).'
Sample run -
>> A
A =
1 2 3 4 5
8 4 7 9 6
>> B
B =
2 3
4 9
>> mask = all(bsxfun(#ne,A,permute(B,[1 3 2])),3);
>> At = A.'; %//'
>> out = reshape(At(mask.'),[],size(A,1)).'
out =
1 4 5
8 7 6
Approach #2 Using diff and sort -
sAB = sort([A B],2)
dsAB = diff(sAB,[],2)~=0
mask1 = [true(size(A,1),1) dsAB]
mask2 = [dsAB true(size(A,1),1)]
mask = mask1 & mask2
sABt = sAB.'
out = reshape(sABt(mask.'),[],size(A,1)).'

Related

Matlab index matrix by vector specifying column

Let A be of size [n,m], i.e. it has n rows and m columns. Given I of size [n,1] with max(I)<=m, what is the fastest way to return B of size [n,1], such that B(i)=A(i,I(i))?
Example:
A =
8 1 6
3 5 7
4 9 2
and
I =
1
2
2
I want B to look like
B =
8
5
9
There obviously exist several ways to implement this, but in my case n is in the order of 1e6 and m in the order of 1e2, which is why I'm interested in the fastest implementation. I would like to avoid ind2sub or sub2ind since they both appear to be too slow as well. Any idea is greatly appreciated! Thanks!
You can replicate the behavior of sub2ind yourself. This gives me a speedup in my test:
clear
%% small example
A = rand(4,6)
I = [3 2 2 1]
inds = (I-1)*size(A,1) + (1:length(I));
B = A(inds)
%% timing
n = 1e4;
m = 1e2;
A = rand(n, m);
I = ceil(rand(1,n) * m);
% sub2ind
F = #() A(sub2ind(size(A), 1:size(A,1), I));
timeit(F)
% manual
F = #() A((I-1)*size(A,1) + (1:length(I)));
timeit(F)
You can also use something like this:
A(meshgrid(1:size(A,2),1:size(A,1)) == repmat(I,1,size(A,2)))
which will give you the same result, with no loop and no sub2ind.

MATLAB : 3 dimensional matrix with multipication with a vector

I have A matrix which is 16x16x155460. I have a B vector which is 12955x1. I want to multiply each 1:16x1:16x1+12*n:12+12*nwith the elements of B(n). So my goal is to find the weighted sum of the A according to B. My way to do this as follows (I don't want to use for-loop and my method gives wrong answer, I could not obtain the 1:12 vectors which is consecutive) :
B = repmat(B,[1 16 16]);
B = permute(B,[2 3 1]);
B = repmat(B,[1 1 12]);
result = B.*(A);
As a small example n=2 :
A(:,:,1)=[1 2; 3 4]
A(:,:,2)=[1 2; 3 4]
A(:,:,3)=[1 2; 3 4]
A(:,:,4)=[1 2; 3 4]
B = [2,3]
Result would be:
result(:,:,1)=A(:,:,1)*B(1);
result(:,:,2)=A(:,:,2)*B(1);
result(:,:,3)=A(:,:,1)*B(2);
result(:,:,4)=A(:,:,2)*B(2);
If I understood the problem correctly, you can use the powerful trio of bsxfun, permute and reshape to solve it, like so -
[M,N,R] = size(A);
mult_out = bsxfun(#times,reshape(A,M,N,numel(B),[]),permute(B(:),[4 3 1 2]))
out = reshape(mult_out,M,N,[])

Find part of vector in another vector matlab

I would like to know if there is an easy way to find the indices of a vector in another vector in matlab:
a = [1 2 3 5 7 10 2 3 6 8 7 5 2 4 7 2 3]
b = [2 3]
So how to get the indices of a when comparing it with b (index of first element is needed)
In this case:
ans = [2 7 16]
Thanks in advance
find(a(1:end-1) == b(1) & a(2:end) == b(2) == 1)
You can re-purpose strfind by converting the elements of both vectors to byte arrays (uint8) with typecast:
bytesPerEl = numel(typecast(a(1),'uint8'));
byteLocs = strfind(char(typecast(a,'uint8')),char(typecast(b,'uint8')));
locsb = (byteLocs-1)/bytesPerEl + 1
locsb =
2 7 16
Just make sure a and b are of the same type. Also note that this works for 1D vectors, not matrixes or higher dimensional arrays.
General approach with length of b arbitrary (not necessarily 2 as in the example), and avoiding the use of strings:
match1 = bsxfun(#eq, a(:), b(:).'); %'// now we just need to make the diagonals
%// horizontal (in order to apply "all" row-wise). For that we'll use indices
%// ind, ind1, ind2
ind = reshape(1:numel(match1), numel(a), numel(b));
ind1 = nonzeros(tril(ind)); %// source indices
ind2 = sort(nonzeros(tril(flipud(ind)))); %// destination indices
match2 = zeros(size(match1));
match2(ind2) = match1(ind1); %// diagonals have become horizontal
result = find(all(match2.'));

Create Matrix using Matrix Indices

In matlab, how could you create a matrix M using its indices to populate the values? For example, say I want to create a 3x3 matrix M such that
M(i,j) = i+j --> [ 2 3 4; 3 4 5; 4 5 6]
I tried making vectors: x = 1:3', y = 1:3 and then
M = x(:) + y(:)
but it didn't work as expected.
Any thoughts on how this can be done?
Thanks!
UPDATE
The M I actually desire is:
M(i,j) = -2^(-i - j).
One way would be
x = 1:3;
z = ones(1,3);
N = z'*x + x'*z
M = -2 .^ -(z'*x + x'*z)
% Or simply
% M = -2 .^ -N
Output:
N =
2 3 4
3 4 5
4 5 6
M =
-0.250000 -0.125000 -0.062500
-0.125000 -0.062500 -0.031250
-0.062500 -0.031250 -0.015625
You should use bsxfun to find the sum:
M=bsxfun(#plus, (1:3).', 1:3)
and for the second formula:
M=-2.^(-bsxfun(#plus, (1:3).', 1:3))
bsxfun(#(x,y)(-2.^(-x-y)), (1:3).', 1:3)
This uses the Answer of Mohsen Nosratinia with the function you wished.

MATLAB concatenate 2D matrix tiles

I have n2 equally sized (8x8) matrices which I want to tile into a single matrix like in the following diagram:
I know I could concatenate them column by column and then concatenate each row, but I want to know if there's a simpler method to achieve this.
There's a simpler method, you can store all your matrices in a cell array, then reshape and convert back to a matrix:
In the following example, suppose that C is your n2×1 cell array of matrices:
cell2mat(reshape(C, sqrt(numel(C)), []));
The result is a single tiled matrix A as required.
Example
a = ones(2); b = 2 * a; c = 3 * a; d = 4 * a;
C = {a, b, c, d};
A = cell2mat(reshape(C, sqrt(numel(C)), []))
The result is:
A =
1 1 3 3
1 1 3 3
2 2 4 4
2 2 4 4
Note the order of the sub-matrices: they are arranged column-wise. If you want A to be:
A =
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
then you'll have to pass the transposed version of C to reshape:
cell2mat(reshape(C', sqrt(numel(C)), []))
If you already have a for loop where you create the 8-by-8 matrices, you can do something like this:
M = 8; % Rows of each block matrix
N = 8; % Columns of each square block matrix
m = 2; % Number of blocks across
n = 2; % Number of blocks vertically
A(m*n*M,N) = 0; % Preallocate an m*n*M-by-N column of blocks
for i = 1:m*n
a = rand(M,N); % Create your data, just random here
A(1+M*(i-1):M*i,:) = a; % Insert data
end
A = reshape(A,[M*m N*n]); % Reshape to obtain block matrix
This assumes that you have a single for loop iterating over all n^2 (or m*n) cases. Also, it builds up A one column of blocks at a time. Note: if you need to build it with the blocks going across the rows first, then you'll need to change the allocation of A and how the data is inserted by swapping the indices.
Yes there is!
%Assuming your matrices are A1, A2, A3 and A4:
A = zeros(size(A1)*2);
A(1:8,1:8) = A1;
A(9:16, 1:8) = A2;
A(1:8, 9:16) = A3;
A(9:16, 9:16) = A4;