我有一个3维Y(i,j,w)的矩阵。 我想得到一个行列式向量d(w),其中每个数字都是矩阵的行列式 Y(:,:,W)。
是否有优雅的语法,或者我只需要使用循环?
感谢
答案 0 :(得分:7)
嗯,首先,你几乎从未真正想要计算一个决定因素,你只是认为你做了。事实上,它几乎从来都不是一件好事,因为决定因素的规模很小。它们常常被用来推断矩阵的奇点状态,这在数值分析方面是一件很糟糕的事情。
总体上说明了我对决定因素的迷恋......
选项1:
将三维数组转换为方形矩阵的单元格数组,数组的每个平面都作为一个单元格。 mat2cell可以轻松有效地完成这项工作。
接下来,在单元阵列上使用cellfun。 cellfun可以将函数(@det)应用于每个单元格,然后返回一个行列式的向量。这难以置信吗?只要在编写循环时预先预先分配向量,它可能不会比在循环中应用det有很大的收获。
选项2:
如果矩阵很小,比如2x2或3x3矩阵,则将行列式的乘法展开为显式向量乘法。我认为这不清楚,因为我正在写它,所以对于2x2的情况,其中Y是2x2xn:
d = Y(1,1,:).*Y(2,2,:) - Y(1,2,:).*Y(2,1,:);
当然,你会发现这形成了矩阵Y的每个平面的2x2行列式的向量.3x3的情况也足够简单,也可以作为6个3向乘积的项。我没有仔细检查下面的3x3案例,但它应该是关闭的。
d = Y(1,1,:).*Y(2,2,:).*Y(3,3,:) + ...
Y(2,1,:).*Y(3,2,:).*Y(1,3,:) + ...
Y(3,1,:).*Y(1,2,:).*Y(2,3,:) - ...
Y(3,1,:).*Y(2,2,:).*Y(1,3,:) - ...
Y(2,1,:).*Y(1,2,:).*Y(3,3,:) - ...
Y(1,1,:).*Y(3,2,:).*Y(2,3,:);
正如您所看到的,OPTION 2将非常快,并且它是矢量化的。
编辑:作为对Chris的回应,所需时间存在显着差异。考虑一组1e5矩阵所需的时间。
p = 2;
n = 1e5;
Y = rand(p,p,n);
tic,
d0 = squeeze(Y(1,1,:).*Y(2,2,:) - Y(2,1,:).*Y(1,2,:));
toc
Elapsed time is 0.002141 seconds.
tic,
X = squeeze(mat2cell(Y,p,p,ones(1,n)));
d1= cellfun(@det,X);
toc
Elapsed time is 12.041883 seconds.
这两个调用将相同的值返回到浮点垃圾桶中。
std(d0-d1)
ans =
3.8312e-17
循环不会更好,事实上肯定更糟。那么我是否要编写一段代码,该代码将面临为数组中的许多此类矩阵生成行列式的任务,我将特别考虑2x2和3x3矩阵的代码。我甚至可以写出4x4矩阵。是的,写出来是一团糟,但所需的时间差别很大。
一个原因是MATLAB的det使用对LU的调用,对矩阵进行分解。理论上这比中等大型矩阵的乘法更好,但对于2x2或3x3,额外的开销是杀手。 (我不会猜测收支平衡点落在哪里,但人们可以很容易地测试它。)
答案 1 :(得分:3)
我会使用arrayfun:
d = arrayfun(@(w) det(Y(:, :, w)), 1 : size(Y, 3));
编辑:速度测试:
p = 10;
n = 1e4;
Y = rand(p,p,n);
测试1:
>> tic, d1 = arrayfun(@(w) det(Y(:, :, w)), 1 : size(Y, 3)); toc
Elapsed time is 0.139030 seconds.
测试2(通过木片):
>> tic, X = squeeze(mat2cell(Y,p,p,ones(1,n))); d2= cellfun(@det,X); toc
Elapsed time is 1.318396 seconds.
测试3(天真的方法):
>> p = 10;
>> n = 1e4;
>> Y = rand(p,p,n);
>> tic; d = nan(n, 1); for w = 1 : length(d), d(w) = det(Y(:, :, w)); end; toc
Elapsed time is 0.069279 seconds.
结论:天真的方法是最快的。