行列にA
と言わせましょうA = magic(100);
。行列のすべての要素の合計を計算する2つの方法を見てきA
ました。
sumOfA = sum(sum(A));
または
sumOfA = sum(A(:));
それらの1つは他よりも速い(またはより良い練習)ですか?もしそうなら、それはどれですか?それとも、どちらも同じくらい速いですか?
行列にA
と言わせましょうA = magic(100);
。行列のすべての要素の合計を計算する2つの方法を見てきA
ました。
sumOfA = sum(sum(A));
または
sumOfA = sum(A(:));
それらの1つは他よりも速い(またはより良い練習)ですか?もしそうなら、それはどれですか?それとも、どちらも同じくらい速いですか?
パフォーマンスと浮動小数点の精度のどちらが重要かを判断できないようです。
浮動小数点の精度が最も重要な場合は、正の要素と負の要素を分離して、各セグメントを並べ替えます。次に、絶対値の昇順で合計します。ええ、私は知っています、それは誰よりも多くの仕事をします、そしてそれはおそらく時間の無駄になるでしょう。
代わりに、発生したエラーが無関係になるように適切な精度を使用してください。問題が発生しないように、テストなどに関する適切な数値手法を使用してください。
時が経つ限り、NxMアレイの場合、
sum(A(:))には、N*M-1の追加が必要です。
sum(sum(A))には、(N-1)* M + M-1 = N*M-1の追加が必要です。
どちらの方法でも同じ数の追加が必要なので、大きな配列の場合、インタープリターが両方とも同じ操作であることを認識できるほど賢くなくても、誰が気にしますか?
それは単に問題ではありません。これを心配するためにほくろの丘から山を作らないでください。
編集:あるメソッドのエラーに関するAmroのコメントに応えて、制御できるものはほとんどありません。追加は異なる順序で行われますが、どちらのシーケンスが優れているかについての保証はありません。
A = randn(1000);
format long g
2つのソリューションは非常に近いものです。実際、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(:))としての演算の方がわずかに正確である可能性があります。そうですか?累積に高精度を使用するとどうなりますか?したがって、最初に、doubleを使用して列の合計を作成し、次に小数点以下25桁の精度に変換して、行を合計します。(ここでは20桁のみを表示し、5桁は保護桁として非表示にしています。)
sum(hpf(sum(A)))
ans =
945.76066810244807408
または、代わりに、すぐに25桁の精度に変換してから、結果を合計します。
sum(hpf(A(:))
945.76066810244749807
したがって、倍精度の両方の形式は、ここでは反対方向に等しく間違っていました。結局、これはすべて議論の余地があります。なぜなら、私が示した選択肢はどれも、単純なバリエーションsum(A(:))またはsum(sum(A))と比較してはるかに時間がかかるからです。そのうちの1つを選ぶだけで、心配する必要はありません。
パフォーマンスに関しては、両方とも非常に似ていると思います(最近の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計算で異なる可能性があります。これにより、計算時間を比較するために非常に大きな要素を持つ独自の行列を使用した方がよいでしょう。