【问题标题】:How can I vectorise this loop in MATLAB如何在 MATLAB 中对这个循环进行矢量化
【发布时间】:2015-11-10 03:12:23
【问题描述】:

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

例如,它将转换这个矩阵:

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 行/第 2 列只有 1 个非零元素,因此第 2 行/第 2 列的每个元素在A' 中都设置为 0

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

这是我的非矢量化代码:

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

有没有更高效的方法在 MATLAB 中编写此代码?

编辑:

我在 cmets 中被要求澄清一下,所以我会答应的。

此代码的目的是从图中删除通向度数为 1 的顶点的边。

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

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

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

所以,用另一个例子来澄清一下:

我要转这个图G

用矩阵表示:

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

到这张图G':

由这个矩阵表示:

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

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

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

【问题讨论】:

  • 我觉得您的问题可能自相矛盾,而且您的解决方案并不能真正解决您提出的问题。 1) A = [1 1 0; 的预期答案是什么? 0 1 1; 0 0 1]?我认为递归我应该得到一个零矩阵。 2)在您的解决方案代码中,为什么您只检查一行中有多少个1,还要清除整个列?澄清一下?
  • 知道了,这就是为什么你说 A 是对称的,我之前没有捕捉到

标签: matlab matrix vectorization


【解决方案1】:

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

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

sum(A ~= 0) > 1 

~= 0 在您的情况下可能不需要,因为矩阵仅由 1/0 元素组成(如果我理解正确,请绘制边缘)。

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

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

并与 A 从左到零行和从右到零列相乘:

res = D * A * D

【讨论】:

  • 我使用的示例只有 0 和 1,但是在实践中,我使用的矩阵是表示边缘权重的正整数值,这种方法是否仍然有效?抱歉,我应该更明确地说明这一点
  • 根据您的建议,我想出了另一个解决方案,删除我所做的行:A(sum(A ~= 0) == 1, :) = 0;,然后交换 sum(...) 和 : 列,我将测试多快它与我原来的解决方案进行了比较..
  • 我的计算方式是原来的两倍,感谢您使用 sum(A~=0) 的建议
  • 因为它是对称的,你只需要计算一次x = sum(A~=0)==1。考虑到矩阵运算的速度有多快,D = diag(x); answer = D * A * D 有可能实际上更快,尽管矩阵乘法是 O(2.8)
【解决方案2】:

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

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

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

然后用一个元素清除列:

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

对于那些感兴趣的人,我对 1000 x 1000 矩阵进行了“tic-toc”比较:

% 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

看来我在 nimrodm 建议的启发下提出的单行版本是最快的

感谢您的帮助!

【讨论】:

    【解决方案3】:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-25
      • 1970-01-01
      • 1970-01-01
      • 2015-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多