Эффективный (быстрый) способ суммирования элементов матрицы в matlab
давайте матрица A
сказать A = magic(100);
. Я видел 2 способа вычисления суммы всех элементов матрицы A
.
sumOfA = sum(sum(A));
или
sumOfA = sum(A(:));
один из них быстрее (или лучше практиковать), то другие? Если да, то какой? Или они оба одинаково быстры?
2 ответов
кажется, что вы не можете решить, является ли производительность или точность с плавающей запятой более важной.
если бы точность с плавающей запятой была первостепенной точностью, то вы бы разделили положительные и отрицательные элементы, сортируя каждый сегмент. Затем суммируем в порядке возрастания абсолютной величины. Да, я знаю, это больше работы, чем кто-либо мог бы сделать, и это, вероятно, будет пустой тратой времени.
вместо этого используйте адекватную точность, чтобы любые сделанные ошибки и не имеет значения. Используйте хорошие численные методы о тестах и т. д., Чтобы не возникало проблем.
что касается времени, для массива NxM,
sum (A (:)) потребует N*M-1 дополнений.
sum (sum (A)) потребует (N-1)*M + M-1 = N*M-1 дополнений.
для любого метода требуется одинаковое количество добавлений, поэтому для большого массива, даже если интерпретатор недостаточно умен, чтобы распознать, что они оба являются одинаковыми op, которые заботится?
это просто не проблема. Не делайте гору из кротового холма, чтобы беспокоиться об этом.
Edit: в ответ на комментарий 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 нормальных отклонений. Взгляните на некоторые из них:--8-->
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)). Просто выбери одну из них и не волнуйтесь.
производительность, я бы сказал, что оба очень похожи (предполагая недавнюю версию 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
увеличивается величина ошибки для больших матриц