Использование bsxfun с одноэлементным расширением с матрицами трех измерений

Я использую bsxfun векторизовать операцию с одноэлементным расширением между матрицами размеров:

MS: (nms, nls)
KS: (nks, nls)

Операция представляет собой сумму абсолютных разностей между каждым значением MS(m,l) с m в 1:nms а также l в 1:nlsи каждый KS(k,l) с k в 1:nks,

Я достигаю этого через код:

[~, nls] = size(MS);
MS = reshape(MS',1,nls,[]);
R = sum(abs(bsxfun(@minus,MS,KS)));

R имеет размер (nls, nms),

Я хочу обобщить эту операцию для списка образцов, поэтому новые размеры будут:

MS: (nxs, nls, nms)
KS: (nxs, nls, nks)

Это может быть легко достигнуто с помощью цикла for, который выполняет первый фрагмент кода для каждой 2-мерной матрицы, но я подозреваю, что производительность может быть намного лучше, если обобщить предыдущий код, добавив новое измерение.

R имеет размер: (nxs, nls, nms)

Я пытался изменить MS до 4-х измерений без успеха. Может ли это быть сделано с изменением формы и bsxfun?

1 ответ

Решение

Вам может понадобиться это:

% generate small dummy data
nxs = 2;
nls = 3;
nms = 4;
nks = 5;
MS = rand(nxs, nls, nms);
KS = rand(nxs, nls, nks);

R = sum(abs(bsxfun(@minus,MS,permute(KS,[1,2,4,3]))),4)

Это даст матрицу размера [2,3,4]т.е. [nxs,nls,nms], Каждый элемент [k1,k2,k3] будет соответствовать

R(k1,k2,k3) == sum_k abs(MS(k1,k2,k3) - KS(k1,k2,k))

Например, в моем случайном прогоне

R(2,1,3)

ans =

   1.255765020150647

>> sum(abs(MS(2,1,3)-KS(2,1,:)))

ans =

   1.255765020150647

Хитрость заключается в том, чтобы ввести одноэлементные размеры с permute: permute(KS,[1,2,4,3]) имеет размер [nxs,nls,1,nks], в то время как MS размера [nxs,nls,nms] неявно также имеет размер [nxs,nls,nms,1]: предполагается, что каждый массив в MATLAB обладает счетным бесконечным числом конечных одноэлементных измерений. Отсюда легко увидеть, как вы можете bsxfun вместе массивы размера [nxs,nls,nms,1] а также [nxs,nls,1,nks]соответственно получить один с размером [nxs,nls,nms,nks], Суммирование по измерению 4 запечатывает сделку.


Я отметил в комментарии, что это может быть быстрее permute индекс суммирования должен быть на первом месте. Оказывается, это само по себе замедляет выполнение кода. Однако за счет изменения размеров массивов в уменьшающиеся размерные размеры общая производительность увеличивается (благодаря оптимальному доступу к памяти). Сравните это:

% generate larger dummy data
nxs = 20;
nls = 30;
nms = 40;
nks = 500;
MS = rand(nxs, nls, nms);
KS = rand(nxs, nls, nks);

MS2 = permute(MS,[4 3 2 1]);
KS2 = permute(KS,[3 4 2 1]);
R3 = permute(squeeze(sum(abs(bsxfun(@minus,MS2,KS2)),1)),[3 2 1]);

То, что я сделал, это положил суммирование nks Размер на первое место, и упорядочить остальные размеры в порядке убывания. Это может быть сделано автоматически, я просто не хочу переусердствовать с примером. В вашем случае вы, вероятно, в любом случае будете знать величину размеров.

Время выполнения с двумя вышеуказанными кодами: 0, 07028 с для оригинала, 0, 051162 с для переупорядоченного (лучший из 5). Большие примеры, к сожалению, не вписываются в мою память.

Другие вопросы по тегам