Reputation: 3
I want to analyse 2x2 blocks in a bigger matrix and if two of the blocks are the same, delete them. The code I tried to write myself for that looks somewhat like this (C is a big matrix with 2 colums):
n=size(C,1);
for k=1:2:(n-3)
for l=(k+2):2:(n-1)
if C(k:k+1,1:2)==C(l:l+1,1:2);
C([l,l+1],:)=[];
else
end
end
end
The problem I run into is, that matlab does not "notice" that the size of C is shrinking when it deletes rows in it. That means eventually the code runs into a problem because it is asking for the equal check in a dimension too high. How can I avoid it? Is there an entirely better way to do it?
I tried to wrap my head around the "unique" command, too. But that only works for rows. I need 2x2 segments (starting at an odd row).
Complete (original) code here:
A=[1,0;0,1i];
B=1/sqrt(2)*[1,1;1,-1];
C=[0,0;0,0];
C(1:2,1:2)=A(1:2,1:2);
C(3:4,1:2)=B(1:2,1:2);
for m=1:20
d=size(C,1)
C_A=C*A;
C_B=C*B;
s_a=size(C_A);
s_a=s_a(1,1);
s_b=size(C_B);
s_b=s_b(1,1);
C((d+1):(d+s_a),1:2)=C_A(1:s_a,1:2);
C((d+s_a+1):(d+s_a+s_b),1:2)=C_B(1:s_b,1:2);
n=size(C,1);
for k=1:2:(n-3)
for l=(k+2):2:(n-1)
if C(k:k+1,1:2)==C(l:l+1,1:2);
C([l,l+1],:)=[];
else
end
end
end
new_n=size(C,1)
if d==new_n
error('Done')
else
end
end
Upvotes: 0
Views: 103
Reputation: 16791
To make your code work as it is, first reverse the direction of the l
loop:
for l = (size(C,1)-1):-2:(k+2)
That way you're deleting from the end and recalculating the max index each time you increment k
. But you're still going to run into trouble with the k
loop, so just change that to a while
loop. Also, using ==
to compare 2x2 matrices will give you 4 values, probably not what you want. Using isequal
will give you a single boolean if the two matrices have all the same values.
k = 1;
while (k < size(C,1))
for l = (size(C,1)-1):-2:(k+2)
if isequal(C(k:k+1,1:2), C(l:l+1,1:2))
C([l,l+1],:) = [];
end
end
k = k + 2;
end
Alternatively, you can use unique
by rearranging each 2x2 submatrix into a row:
D = reshape(C.', 4, []).';
U = unique(D, 'rows');
C = reshape(U.', 2, []).';
If you're dealing with floating-point values, it's never a good idea to compare them for equality; the limited precision of floating point values will always cause you trouble. To get around this, you can create a function to compare for equality within a tolerance.
isequalTol=@(a,b,tol) all(abs(a-b) < tol);
This function will work for scalars, vectors, or matrices. Now you define a tolerance and replace isequal
with isequalTol
:
tol = eps(1);
k = 1;
while (k < size(C,1))
for l = (size(C,1)-1):-2:(k+2)
if isequalTol(C(k:k+1,1:2), C(l:l+1,1:2), tol) % equal within tolerance
C([l,l+1],:) = [];
end
end
k = k + 2;
end
Upvotes: 1
Reputation: 523
Slightly different approach from beaker which uses masking to prevent while loops. This method would be preferable if you wanted to do anything with the duplicate values later.
CMask = ones(size(C)); %Create mask; anything with a 1 will be part of final answer, zeros will be left out
for ii = 1:2:size(C,1)
for jj = ii+2:2:size(C,1)
CMask(jj:jj+1,:) = CMask(jj,1) && ~isequal(C(ii:ii+1,:),C(jj:jj+1,:))) %If the two sections are equal, make CMask zeros in the lower section. && is required so that we don't overwrite 0s with 1s
end
end
COut = reshape(C(CMask),[],2);
Upvotes: 0