简体   繁体   中英

Delete same submatrices in a bigger matrix. Matlab

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

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

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM