让矩阵A
说A = magic(100);
。我已经看到了两种计算矩阵所有元素之和的方法A
。
sumOfA = sum(sum(A));
或者
sumOfA = sum(A(:));
其中一个比另一个更快(或更好的练习)吗?如果有,是哪一个?还是它们都同样快?
让矩阵A
说A = magic(100);
。我已经看到了两种计算矩阵所有元素之和的方法A
。
sumOfA = sum(sum(A));
或者
sumOfA = sum(A(:));
其中一个比另一个更快(或更好的练习)吗?如果有,是哪一个?还是它们都同样快?
似乎您无法决定性能还是浮点精度更重要。
如果浮点精度是最重要的精度,那么您将分离正负元素,对每个段进行排序。然后按照绝对值递增的顺序求和。是的,我知道,它的工作量比任何人都多,而且可能会浪费时间。
相反,请使用足够的精度,以使所犯的任何错误都无关紧要。使用关于测试等的良好数值实践,这样就不会产生问题。
就时间而言,对于 NxM 阵列,
sum(A(:)) 将需要 N*M-1 加法。
sum(sum(A)) 将需要 (N-1)*M + M-1 = N*M-1 加法。
任何一种方法都需要相同数量的添加,所以对于一个大数组,即使解释器不够聪明,无法识别它们都是同一个操作,谁在乎呢?
这根本不是问题。不要为了担心这个而把鼹鼠山变成一座山。
编辑:为了回应 Amro 关于一种方法的错误的评论,您几乎无法控制。添加将以不同的顺序完成,但不能保证哪个顺序会更好。
A = randn(1000);
format long g
这两种解决方案非常接近。事实上,与 eps 相比,差异几乎不显着。
sum(A(:))
ans =
945.760668102446
sum(sum(A))
ans =
945.760668102449
sum(sum(A)) - sum(A(:))
ans =
2.72848410531878e-12
eps(sum(A(:)))
ans =
1.13686837721616e-13
假设您选择我提到的隔离和排序技巧。请注意,负部分和正部分将足够大,以至于会损失精度。
sum(sort(A(A<0),'descend'))
ans =
-398276.24754782
sum(sort(A(A<0),'descend')) + sum(sort(A(A>=0),'ascend'))
ans =
945.7606681037
因此,无论如何,您确实需要将这些片段累积在更高精度的数组中。我们可以试试这个:
[~,tags] = sort(abs(A(:)));
sum(A(tags))
ans =
945.760668102446
即使在这些测试中也会出现一个有趣的问题。是否会因为测试是在随机(正常)阵列上完成而出现问题?本质上,我们可以将 sum(A(:)) 视为随机游走,醉汉游走。但是考虑 sum(sum(A))。sum(A) 的每个元素(即内部和)本身就是 1000 个正态偏差的总和。看看其中几个:
sum(A)
ans =
Columns 1 through 6
-32.6319600960983 36.8984589766173 38.2749084367497 27.3297721091922 30.5600109446534 -59.039228262402
Columns 7 through 12
3.82231962760523 4.11017616179294 -68.1497901792032 35.4196443983385 7.05786623564426 -27.1215387236418
Columns 13 through 18
当我们将它们相加时,精度会有所下降。因此,作为 sum(A(:)) 的操作可能会更准确一些。是这样吗?如果我们使用更高的精度进行累加呢?因此,首先,我将使用双精度对列求和,然后转换为 25 位小数精度,并对行求和。(我在这里只显示了 20 个数字,留下 5 个数字作为保护数字隐藏。)
sum(hpf(sum(A)))
ans =
945.76066810244807408
或者,立即转换为 25 位精度,然后对结果求和。
sum(hpf(A(:))
945.76066810244749807
所以双精度的两种形式在这里同样是错误的,方向相反。最后,这一切都没有实际意义,因为与简单的变化 sum(A(:)) 或 sum(sum(A)) 相比,我展示的任何替代方案都更耗时。只需选择其中一个,别担心。
在性能方面,我想说两者都非常相似(假设是最近的 MATLAB 版本)。这是使用TIMEIT功能的快速测试:
function sumTest()
M = randn(5000);
timeit( @() func1(M) )
timeit( @() func2(M) )
end
function v = func1(A)
v = sum(A(:));
end
function v = func2(A)
v = sum(sum(A));
end
结果是:
>> sumTest
ans =
0.0020917
ans =
0.0017159
我担心的是浮点问题。例子:
>> M = randn(1000);
>> abs( sum(M(:)) - sum(sum(M)) )
ans =
3.9108e-11
对于较大的矩阵,误差幅度会增加
我认为一个简单的理解方法是在代码的第一个和最后一个应用“ tic_ toc ”函数。
tic
A = randn(5000);
format long g
sum(A(:));
toc
但是当你使用 randn 函数时,它的元素是随机的,并且每个周期CPU计算的计算时间可能不同。这更好地使用了一个具有如此大元素的独特矩阵来比较计算时间。