以下是我想要使用的矩阵子集的示例:
1 3 5
2 3 6
1 1 1
3 5 4
5 5 5
8 8 0
这个矩阵实际上是3000 x 3。
对于前3行,我希望用这三行的第一行减去每一行。
对于后三行,我希望用这三行中的第一行减去每一行,依此类推。
因此,输出矩阵将如下所示:
0 0 0
1 0 1
0 -2 -4
0 0 0
2 0 1
5 3 -4
MATLAB中的哪些代码会为我做这个?
答案 0 :(得分:12)
您也可以使用mat2cell
,cellfun
,然后使用cell2mat
完全向量化。假设我们的矩阵存储在A
中,请尝试:
numBlocks = size(A,1) / 3;
B = mat2cell(A, 3*ones(1,numBlocks), 3);
C = cellfun(@(x) x - x([1 1 1], :), B, 'UniformOutput', false);
D = cell2mat(C); %//Output
第一行显示我们需要多少3 x 3块。这假设行数是3的倍数。第二行使用mat2cell
来分解每个3 x 3块并将它们放入单个单元格中。然后第三行使用cellfun
,以便对于我们的单元格数组中的每个单元格(3 x 3矩阵),它采用3 x 3矩阵的每一行并用第一行减去自身。这非常像@David所做的,除了我没有使用repmat
来减少开销。然后第四行接受每个矩阵并将它们堆叠起来,以便我们最终获得最终矩阵。
示例(这是使用您帖子中定义的矩阵):
A = [1 3 5; 2 3 6; 1 1 1; 3 5 4; 5 5 5; 8 8 0];
numBlocks = size(A,1) / 3;
B = mat2cell(A, 3*ones(1, numBlocks), 3);
C = cellfun(@(x) x - x([1 1 1], :), B, 'UniformOutput', false);
D = cell2mat(C);
输出:
D =
0 0 0
1 0 1
0 -2 -4
0 0 0
2 0 1
5 3 -4
事后看来,我认为@David在性能提升方面是正确的。除非这段代码重复多次,否则我认为for
循环会更有效率。无论哪种方式,我想提供另一种选择。很酷的运动!
由于我们之前的讨论,我决定进行时间和尺寸测试。这些测试是在具有16 GB RAM的Intel i7-4770 @ 3.40 GHz CPU上进行的,使用Windows 7 Ultimate上的MATLAB R2014a。基本上,我做了以下事情:
测试#1 - 将随机种子生成器设置为1以获得再现性。我写了一个循环,循环10000次。对于循环中的每次迭代,我生成了一个随机整数3000 x 3矩阵,然后执行此处描述的每个方法。我注意到10000次循环后每种方法完成所需的时间。时间结果如下:
0.092129 seconds
1.9828 seconds
0.20097 seconds
bsxfun
方法:0.10972 seconds
bsxfun
方法:0.0689 seconds
因此,Divakar的方法是最快的,其次是David的for
循环方法,紧接着是natan的bsxfun
方法,接着是natan的原始kron
方法,然后是树懒(又名我的。
stem
图,显示每个矩阵大小的时间。我对绘图进行了子集化,使其显示从200000 x 3到300000 x 3的时序。请注意,水平轴在每次迭代时记录行数。第一个主干为3000行,下一个为6000行,依此类推。列(当然)列保持不变。
我无法解释整个图表中的随机峰值....可能归因于RAM中发生的事情。但是,我非常确定我在每次迭代时清除变量以确保没有偏差。无论如何,迪瓦卡和大卫都是紧密联系在一起的。接下来是natan的bsxfun
方法,然后是natan的kron
方法,最后是我的方法。有趣的是看看Divakar的bsxfun
方法和大卫的for
方法在时间上是如何并排的。
我在水平轴上绘制了半对数刻度,而垂直轴仍然是线性刻度。同样,横轴表示矩阵中的行数。
我更改了垂直限制,因此我们可以看到大多数方法。我的方法表现很差,以至于它会将其他时间压缩到图表的下端。因此,我改变了观看限制,使我的方法脱离了图片。基本上在测试#2中看到的内容在此验证。
答案 1 :(得分:9)
以下是使用bsxfun
实现此目的的另一种方式,与natan's bsxfun implementation略有不同 -
t1 = reshape(a,3,[]); %// a is the input matrix
out = reshape(bsxfun(@minus,t1,t1(1,:)),[],3); %// Desired output
答案 2 :(得分:6)
稍微短一点的矢量化方式(如果a
是你的矩阵):
b=a-kron(a(1:3:end,:),ones(3,1));
让我们测试一下:
a=[1 3 5
2 3 6
1 1 1
3 5 4
5 5 5
8 8 0]
a-kron(a(1:3:end,:),ones(3,1))
ans =
0 0 0
1 0 1
0 -2 -4
0 0 0
2 0 1
5 3 -4
这是一个bsxfun解决方案(不太优雅,但希望更快):
a-reshape(bsxfun(@times,ones(1,3),permute(a(1:3:end,:),[2 3 1])),3,[])'
ans =
0 0 0
1 0 1
0 -2 -4
0 0 0
2 0 1
5 3 -4
好吧,这让我感到好奇,因为我知道bsxfun对于更大的阵列尺寸开始效率低下。因此,我尝试使用timeit
我的两个解决方案进行检查(因为它们是一个简单的衬里)。这是:
range=3*round(logspace(1,6,200));
for n=1:numel(range)
a=rand(range(n),3);
f=@()a-kron(a(1:3:end,:),ones(3,1));
g=@() a-reshape(bsxfun(@times,ones(1,3),permute(a(1:3:end,:),[2 3 1])),3,[])';
t1(n)=timeit(f);
t2(n)=timeit(g);
end
semilogx(range,t1./t2);
所以我没有测试for循环和Divkar的bsxfun,但是你可以看到,对于小于3e4 kron的数组优于bsxfun,并且这在更大的数组中变化(比率为< 1意味着克朗在给定阵列大小的情况下花费的时间更少。这是在Matlab 2012a win7(i5机器)
完成的答案 3 :(得分:4)
简单for
循环。这将分别执行每个3x3块。
A=randi(5,9,3)
B=A(1:3:end,:)
for i=1:length(A(:,1))/3
D(3*i-2:3*i,:)=A(3*i-2:3*i,:)-repmat(B(i,:),3,1)
end
D
虽然可以对此进行矢量化,但我不认为性能提升是值得的,除非你多次这样做。对于3000x3矩阵,它根本不需要很长时间。
编辑:事实上这似乎很快。我认为这是因为Matlab的JIT编译可以很好地加速简单的for
循环。
答案 4 :(得分:3)
您只需使用索引即可完成此操作:
a(:) = a(:) - a(3*floor((0:numel(a)-1)/3)+1).';
当然,上面的3
可以替换为任何其他数字。即使该数字不划分行数,它仍然有效。