我有两个矩阵,A
和B
。 (B
是连续的,如1:n
)
我需要找到B
中A
的每一行的所有出现次数,并将这些行索引相应地存储在单元格数组C
中。请参阅下面的示例。
A = [3,4,5;1,3,5;1,4,3;4,2,1]
B = [1;2;3;4;5]
因此,
C = {[2,3,4];[4];[1,2,3];[1,3,4];[1,2]}
注意C
不需要在我的应用程序的单元格数组中。我只建议它,因为C
的行向量长度不等。如果你可以建议一个解决办法,这也很好。
我已经尝试为B
的每一行使用循环运行ismember,但是当矩阵A
和B
很大时,这太慢了,大约有一百万个条目。感谢矢量化代码。
(为了给你上下文,这个目的是在网格中识别附加到单个顶点的那些面。注意我不能使用函数edgeattachments因为我的数据不是“TR”形式三角剖分表示。我只有一个面列表和顶点列表。)
答案 0 :(得分:2)
嗯,最好的答案是需要了解A是如何填充的。如果A是稀疏的,也就是说,如果它的列值很少而且B很大,那么我认为保存内存的最佳方法可能是使用稀疏矩阵而不是单元格。
% No fancy stuff, just fast and furious
bMax = numel(B);
nRows = size(A,1);
cLogical = sparse(nRows,bMax);
for curRow = 1:nRows
curIdx = A(curRow,:);
cLogical(curRow,curIdx) = 1;
end
答案:
cLogical =
(2,1) 1
(3,1) 1
(4,1) 1
(4,2) 1
(1,3) 1
(2,3) 1
(3,3) 1
(1,4) 1
(3,4) 1
(4,4) 1
(1,5) 1
(2,5) 1
如何阅读答案。对于每列,行显示列索引在A中显示的索引。1
显示在行[2 3 4]
中,2
显示在行[4]
中,{{1}行3
中的行[1 2 3]
,4
行[1 3 4]
,5
。
然后,您可以根据需要使用[1 2]
而不是单元格作为索引矩阵。
另一种方法是分配C,其中包含索引在C中出现的次数的预期值。
cLogical
答案:
% Fancier solution using some assumed knowledge of A
bMax = numel(B);
nRows = size(A,1);
nColumns = size(A,2);
% Pre-allocating with the expected value, an attempt to reduce re-allocations.
% tic; for rep=1:10000; C = mat2cell(zeros(bMax,nColumns),ones(1,bMax),nColumns); end; toc
% Elapsed time is 1.364558 seconds.
% tic; for rep=1:10000; C = repmat({zeros(1,nColumns)},bMax,1); end; toc
% Elapsed time is 0.606266 seconds.
% So we keep the not fancy repmat solution
C = repmat({zeros(1,nColumns)},bMax,1);
for curRow = 1:nRows
curIdxMsk = A(curRow,:);
for curCol = 1:nColumns
curIdx = curIdxMsk(curCol);
fillIdx = ~C{curIdx};
if any(fillIdx)
fillIdx = find(fillIdx,1);
else
fillIdx = numel(fillIdx)+1;
end
C{curIdx}(fillIdx) = curRow;
end
end
% Squeeze empty indexes:
for curRow = 1:bMax
C{curRow}(~C{curRow}) = [];
end
哪种解决方案效果最好?您在代码中进行性能测试,因为它取决于A,bMax,计算机的内存大小等等。然而,我仍然对其他人可以为此x)做的解决方案感到好奇。我喜欢chappjc的解决方案,尽管它有他指出的缺点。
对于给定的例子(10k次):
>> C{:}
ans =
2 3 4
ans =
4
ans =
1 2 3
ans =
1 3 4
ans =
1 2
答案 1 :(得分:1)
我们可以在不对B
做任何假设的情况下做到这一点。请尝试使用bsxfun
和mat2cell
:
M = squeeze(any(bsxfun(@eq,A,permute(B,[3 2 1])),2)); % 4x3x1 @eq 1x1x5 => 4x3x5
R = sum(M); % 4x5 -> 1x5
[ii,jj] = find(M);
C = mat2cell(ii,R)
上面C
中的单元格将是列向量而不是示例中的行。要使单元格包含行向量,请改用C = mat2cell(ii',1,R)'
。
我唯一担心的是mat2cell
对于数百万R
的值来说可能会很慢,但是如果你想要输出一个单元格,我不确定你能做多少好。 编辑:如果您可以处理类似Werner第一个解决方案的稀疏矩阵,请使用以下内容替换上面的最后一行:
>> Cs = sparse(ii,jj,1)
Cs =
(2,1) 1
(3,1) 1
(4,1) 1
(4,2) 1
(1,3) 1
(2,3) 1
(3,3) 1
(1,4) 1
(3,4) 1
(4,4) 1
(1,5) 1
(2,5) 1
不幸的是,如果 bsxfun
和size(A,1)
都很大,numel(B)
可能会耗尽内存!如果内存成为问题,您可能必须遍历A
或B
的元素。这是通过在B
:
for i=1:numel(B), C{i} = find(any(A==B(i),2)); end
是的,这很容易。在MATLAB中,单元阵列的增长非常快,因为它类似于存储对数据的连续引用的序列容器,而不是保持数据本身是连续的。也许ismember
是你测试的瓶颈。