如何在MATLAB中对此循环进行矢量化

时间:2015-11-10 03:12:24

标签: matlab matrix vectorization

我有一个遍历矩阵的循环,并将所有行和列设置为只有一个非零元素为全零。

例如,它将转换此矩阵:

A = [ 1 0 1 1
      0 0 1 0
      1 1 1 1
      1 0 1 1 ]

到矩阵:

A' = [ 1 0 1 1
       0 0 0 0
       1 0 1 1
       1 0 1 1 ]

A的行/列2中只有1个非零元素,因此行/列2中的每个元素都在A'

中设置为0

(假设矩阵总是对角对称)

这是我的非矢量化代码:

for ii = 1:length(A)
    if nnz(A(ii,:)) == 1
        A(ii,:) = 0;
        A(:,ii) = 0;
    end
end

有没有更有效的方法在MATLAB中编写这段代码?

修改

我在评论中被要求做一些澄清,所以我有责任。

此代码的目的是从图形中删除导致顶点为1的边。

如果A是表示无向图G的邻接矩阵,那么该矩阵的只有一个非零元素的行或列表示该行/列表示一度的顶点因为它只有一个边缘事件。

我的目标是从图中删除这些边,因为在我试图解决的问题的解决方案中永远不会访问这些顶点,并且减少图也会减小我的搜索算法输入的大小。

@TimeString,据我所知,在您给出的示例中,递归地将算法应用于矩阵将导致零矩阵,但是我应用它来表示大型连通图的矩阵,因此永远不会有这样的情况。回答你的问题,为什么我只检查一行中有多少元素,但清除了列和行;这是因为矩阵总是对角线对称,所以我知道如果一行是真的,那么它将是相应的列..

所以,只是为了澄清另一个例子:

我想转换此图G

graph G

由矩阵表示:

A = [ 0 1 1 0
      1 0 1 0
      1 1 0 1
      0 0 1 0 ]

到此图G'

graph G'

由此矩阵表示:

A' = [ 0 1 1 0
       1 0 1 0
       1 1 0 0
       0 0 0 0 ]

(我知道这个矩阵实际上应该是一个3x3矩阵,因为D点已被删除,但我已经知道如何在这个例子中缩小矩阵,我的问题是关于有效设置只有1非零的列/行元素全部为0)

我希望这是一个很好的澄清......

3 个答案:

答案 0 :(得分:3)

不确定它是否真的更快(取决于Matlab的JIT),但您可以尝试以下方法:

要找出哪些列(相当于行,因为矩阵是对称的)使用多个非零元素:

sum(A ~= 0) > 1 

在您的情况下可能不需要~= 0,因为矩阵仅包含1/0元素(如果我理解正确,则为图形边缘)。

将上述内容转换为对角矩阵,以消除不需要的列:

D = diag(sum(A~=0) > 1)

将A从左到右乘以及从右到右乘以:

res = D * A * D

答案 1 :(得分:0)

感谢nimrodm建议使用sum(A~ = 0)而不是nnz,我设法找到比原来更好的解决方案

使用我使用的一个元素清除行:

A(sum(A ~= 0) == 1,:) = 0;

然后用一个元素清除列:

A(:,sum(A ~= 0) == 1) = 0;

对于那些有兴趣的人,我做了一个“toc-toc'比较1000 x 1000矩阵:

% establish matrix
A = magic(1000);
rem_rows = [200,555,950];
A(rem_rows,:) = 0;
A(:,rem_rows) = 0;

% insert single element into empty rows/columns
A(rem_rows,500) = 5;
A(500,rem_rows) = 5;

% testing original version
A_temp = A;
for test = 1
    tic
    for ii = 1:length(A_temp)
        if nnz(A_temp(ii,:)) == 1
            A_temp(ii,:) = 0;
            A_temp(:,ii) = 0;
        end
    end
    toc
end

Elapsed time is 0.041104 seconds.

% testing new version
A_temp = A;
for test = 1
    tic
    A_temp(sum(A_temp ~= 0) == 1,:) = 0;
    A_temp(:,sum(A_temp ~= 0) == 1) = 0;
    toc
end

Elapsed time is 0.010378 seconds

% testing matrix operations based solution suggested by nimrodm
A_temp = A;
for test = 1
tic
B = diag(sum(A_temp ~= 0) > 1);
res = B * A_temp * B;
toc
end

Elapsed time is 0.258799 seconds

所以看来我提出的单线版本受到了尼姆罗姆的建议的启发,是最快的

感谢您的帮助!

答案 2 :(得分:0)

Bsxfuning它 -

A(bsxfun(@or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0

示例运行 -

>> A
A =
     1     0     1     1
     0     0     1     0
     1     1     1     1
     1     0     1     1
>> A(bsxfun(@or,(sum(A~=0,2)==1),(sum(A~=0,1)==1))) = 0
A =
     1     0     1     1
     0     0     0     0
     1     0     1     1
     1     0     1     1