[英]How can I vectorise this loop in MATLAB
我有一個循環,可以在矩陣上進行迭代,並將只有一個非零元素的所有行和列都設置為全零。
因此,例如,它將轉換此矩陣:
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個非零元素,因此A'
行/列2中的每個元素都設置為0
(假設矩陣將始終是對角對稱的)
這是我的非矢量化代碼:
for ii = 1:length(A)
if nnz(A(ii,:)) == 1
A(ii,:) = 0;
A(:,ii) = 0;
end
end
有沒有在MATLAB中編寫此代碼的更有效方法?
編輯:
有人在評論中要求我進行澄清,所以我將負責。
該代碼的目的是從圖形中刪除導致階數為1的頂點的邊。
如果A
是表示無向圖G
的鄰接矩陣,則該矩陣的僅包含一個非零元素的行或列表示該行/列表示一個度為1的頂點,因為它只有一個入射的邊。
我的目標是從圖中除去這些邊緣,因為在我要解決的問題的解決方案中將永遠不會訪問這些頂點,並且縮小圖也將減小搜索算法的輸入大小。
@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)
我希望這是足夠的澄清。
不知道它是否真的更快(取決於Matlab的JIT),但是您可以嘗試以下方法:
要找出哪些列(由於矩陣是對稱的,所以等同於行)具有多個非零元素,請使用:
sum(A ~= 0) > 1
在這種情況下,可能不需要~= 0
,因為矩陣僅由1/0元素組成(如果我正確理解,則為圖形邊緣)。
將以上內容轉換為對角矩陣,以消除不需要的列:
D = diag(sum(A~=0) > 1)
並從左到零行和從右到零列乘以A:
res = D * A * D
由於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建議的啟發,我想出的單行版本是最快的
感謝你的幫助!
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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.