Генерация массивов с использованием bsxfun с анонимной функцией и для поэлементного вычитания - MATLAB

У меня такой код:

n = 10000; 
s = 100;
Z = rand(n, 2);
x = rand(s, 1);
y = rand(s, 1);
fun = @(a) exp(a);

В принципе, анонимная функция f может иметь другую форму. Мне нужно создать два массива.

Во-первых, мне нужно создать массив размером n x s x s с общими элементами

fun(Z(i, 1) - x(j)) * fun(Z(i, 2) - y(k))

где i=1,...n, а j,k=1,...,s. Что я могу легко сделать, так это построить матрицы, используя bsxfun, например.

bsxfun(@(x, y) fun(x - y), Z(:, 1), x');
bsxfun(@(x, y) fun(x - y), Z(:, 2), y');

Но тогда мне нужно было бы объединить их в массив 3D, поэлементно умножая каждый столбец этих двух матриц.


На втором этапе мне нужно создать массив размером n x 3 x s x s, который с одной стороны выглядел бы как следующая матрица

[ones(n, 1), Z(:, 1) - x(i), Z(:, 2) - y(j);]

где i=1,...s, j=1,...s. Я мог бы перебрать два дополнительных измерения с помощью чего-то вроде

A = [ones(n, 1), Z(:, 1) - x(1), Z(:, 2) - y(1)];
for i = 1:s
    for j = 1:s
        A(:, :, i, j) = [ones(n, 1), Z(:, 1) - x(i), Z(:, 2) - y(j);];
    end
end

Есть ли способ избежать петель?


На третьем шаге предположим, что после получения массива out1 (результат первого шага) я хочу создать новый массив out3 размерности n x n x s x s, который содержит исходный массив out1 по главной диагонали, то есть out3(i,i,s,s) = out1(i, s, s) и out3(i,j,s,s)=0 для всех i~=j. Есть ли альтернатива diag для создания «диагональных массивов»? В качестве альтернативы, если я создам массив n x n x s x s нулей, есть ли способ поместить out1 на главной диагонали?


person Laimond    schedule 27.10.2014    source источник


Ответы (1)


Код

exp_Z_x = exp(bsxfun(@minus,Z(:,1),x.')); %//'
exp_Z_y = exp(bsxfun(@minus,Z(:,2),y.')); %//'
out1 = bsxfun(@times,exp_Z_x,permute(exp_Z_y,[1 3 2]));

Z1 = [ones(n,1) Z(:,1) Z(:,2)];
X1 = permute([ zeros(s,1) x zeros(s,1)],[3 2 1]);
Y1 = permute([ zeros(s,1) zeros(s,1) y],[4 2 3 1]);
out2 = bsxfun(@minus,bsxfun(@minus,Z1,X1),Y1);

out3 = zeros(n,n,s,s); %// out3(n,n,s,s) = 0; could be used for performance
out3(bsxfun(@plus,[1:n+1:n*n]',[0:s*s-1]*n*n)) = out1; %//'

%// out1, out2 and out3 are the desired outputs
person Divakar    schedule 27.10.2014
comment
@Laimond Был там баг со второй частью, большой. Пожалуйста, проверьте правки для исправления! - person Divakar; 27.10.2014
comment
Я допустил ошибку. В третьем столбце Z1 должно быть Z (:, 2). Спасибо! - person Laimond; 27.10.2014
comment
@Laimond Отредактировано! :) Просто любопытно, если вы не против, чтобы вас беспокоили - как эти решения сравниваются с вашим bsxfun with anonymous functions? Любые сравнения времени выполнения? - person Divakar; 27.10.2014
comment
Действительно, это очень хорошее ускорение. Выполнение bsxfun с @minus более чем в 50 раз быстрее, чем bsxfun с анонимной функцией (я провел отдельное моделирование). Я очень впечатлен! :) Спасибо! - person Laimond; 27.10.2014
comment
@Laimond Отлично, и спасибо за эти тесты !! Что ж, у меня тоже было это предчувствие, и я просто хотел усилить это! :) - person Divakar; 27.10.2014
comment
Еще один вопрос :). Предположим, что после получения массива out1 я хочу создать новый массив out3 размерности n x n x s x s, который содержит исходный массив out1 по главной диагонали, то есть out3(i,i,s,s) = out1(i, s, s) и out3(i,j,s,s)=0 для всех i~=j. Есть ли альтернатива diag для создания диагональных массивов? В качестве альтернативы, если я создам n x n x s x s массив нулей, есть ли способ поместить out1 на главную диагональ? - person Laimond; 27.10.2014
comment
@Laimond Перед этим вы понимаете, что это создаст огромный массив 10k x 10k x 100 x 100, верно? Может ли ваш MATLAB справиться с этим? - person Divakar; 27.10.2014
comment
Понятно ... Предположим, я могу уменьшить S до 20, а n можно уменьшить до 1000. - person Laimond; 27.10.2014
comment
@Laimond Не могли бы вы добавить этот новый материал в конце вопроса? - person Divakar; 27.10.2014
comment
Я понимаю вашу точку зрения. Мне должно быть проще решить мою проблему без явного определения этого нового массива. - person Laimond; 27.10.2014
comment
@Laimond Хорошо, я внес правку, но поскольку это не упоминается в вопросе, будущие читатели могут запутаться. Итак, просто для синхронизации, я думаю, что внесение этого изменения в вопрос имело бы смысл :) Кроме того, убедитесь, что редактирование работает для вас. - person Divakar; 27.10.2014
comment
Спасибо за обсуждение. Это было очень познавательно! Я постараюсь избегать создания больших массивов. У вас правки вроде работают :). - person Laimond; 28.10.2014
comment
И спасибо, что отредактировали вопрос и показали, как его правильно отформатировать. - person Laimond; 28.10.2014