Реализация bsxfun в умножении матриц

Как всегда, пытаясь узнать больше от вас, я надеялся, что смогу получить помощь со следующим кодом.

Мне нужно выполнить следующее:

1) У меня есть вектор:

x = [1 2 3 4 5 6 7 8 9 10 11 12]

2) и матрица:

A =[11    14    1
    5     8    18
    10    8    19
    13    20   16]

Мне нужно иметь возможность умножать значение each из x на значение every из A, это означает:

new_matrix = [1* A
              2* A
              3* A
               ...
              12* A]

Это даст мне new_matrix размера (12*m x n) при условии A (mxn). И в этом случае (12*4x3)

Как я могу сделать это, используя bsxfun из Matlab? и будет ли этот метод быстрее, чем for-loop?

Что касается моего for-loop, мне здесь тоже нужна помощь... Я не могу хранить каждое "new_matrix" по мере выполнения цикла :(

for i=x
new_matrix = A.*x(i)
end

Заранее спасибо!!

EDIT: после того, как были даны решения

Первое решение

clear all
clc
x=1:0.1:50;
A = rand(1000,1000);
tic
val = bsxfun(@times,A,permute(x,[3 1 2]));
out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[]);
toc

Вывод:

Elapsed time is 7.597939 seconds.

Второе решение

clear all
clc
x=1:0.1:50;
A = rand(1000,1000);
tic
Ps = kron(x.',A);
toc

Вывод:

Elapsed time is 48.445417 seconds.

person Sergio Haram    schedule 22.05.2014    source источник
comment
Цикл for может быть выполнен путем предварительного определения вашего new_matrix размером (12*m,n), как вы сказали сами, а затем с помощью индексов, чтобы сообщить вашему new_matrix, где вы хотите сохранить эти элементы, например. в приведенном выше коде new_matrix(((i-1)*12+1):(i*12))) = A.*x(i) я написал его только здесь, поэтому не уверен, что он работает.   -  person The Minion    schedule 22.05.2014
comment
Спасибо @Minion, я проверю, работает ли это, и дам вам знать!   -  person Sergio Haram    schedule 22.05.2014
comment
@Minion Это почти работает, я получаю что-то среднее между 1*new_matrix, 2*new_matrix 3*new_matrix ... и т. Д., Некоторые другие вычисления, которые я не могу понять, откуда они берутся.   -  person Sergio Haram    schedule 22.05.2014
comment
@SergioHaram Спасибо, что разместили этот вопрос! Надеюсь, это пригодится людям, интересующимся bsxfun.   -  person Divakar    schedule 22.05.2014
comment
Круто! Некоторые результаты тестов!! Спасибо, что разместили их!   -  person Divakar    schedule 22.05.2014


Ответы (5)


Отправьте x в третье измерение, чтобы одноэлементное расширение вступило в силу, когда bsxfun используется для умножения на A, расширяя результат произведения до третьего измерения. Затем выполните bsxfun умножение -

val = bsxfun(@times,A,permute(x,[3 1 2])) 

Теперь val представляет собой матрицу 3D, и ожидается, что желаемый результат будет матрицей 2D, объединенной по столбцам в третьем измерении. Это достигается ниже -

out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[])

Надеюсь, это имело смысл! Распространите bsxfun слово вокруг! вау!! :)

person Divakar    schedule 22.05.2014
comment
Это должно работать достаточно хорошо. По крайней мере, так было для моего теста. Но я не очень понимаю код. Почему перестановка? Не могли бы вы отредактировать свой ответ и немного объяснить его? - person The Minion; 22.05.2014
comment
Спасибо @Divakar :D еще раз!, и я также буду очень признателен за объяснение! - person Sergio Haram; 22.05.2014
comment
@Divakar Хорошо, я понимаю расширение x до третьего измерения, это из-за dim из A, верно? А во второй строке вы возвращаете mapping обратно к нужному dim. Но если я изменю числа [3 1 2] на любой другой порядок, будет ли код работать? у вас есть [1 3 2] во второй строке (!)? а что касается второй строки: что делают size(val,3) и []? - person Sergio Haram; 22.05.2014
comment
Спасибо, парни! Я волновался, что разместил очень скучный вопрос... (может быть, это так), но я многому научился за этот час!!! - person Sergio Haram; 22.05.2014
comment
@Divakar Хорошее использование bsxfun - person Luis Mendo; 22.05.2014
comment
@Divakar Что, если мой A из dim 1000? а не 3D? как это повлияет на код? - person Sergio Haram; 22.05.2014
comment
@SergioHaram Ты шутишь? Это отличный вопрос! Итак, что касается ваших сомнений, да, расширенное x до третьего измерения, потому что A является 2D, и оно не будет работать, если вы замените [3 1 2] на что-либо другое. Во второй строке я сделал permute [1 3 2], чтобы элементы столбца в третьем измерении входили в последовательные индексы при линейной индексации, а остальные изменялись до нужного размера. - person Divakar; 22.05.2014
comment
@SergioHaram A это 1000? Что это 000? - person Divakar; 22.05.2014
comment
@Divakar извините, я не объяснила себя ясно. Что произойдет, если моя матрица A будет (1000x1000), а мой вектор x будет (500x1) (просто для примера), синглтон будет вне dim, если у меня только три переменные (3 1 2) в val = bsxfun(@times,A,permute(x,[3 1 2])), или я ошибаюсь? - person Sergio Haram; 22.05.2014
comment
Я просто пытаюсь понять, как я могу перевести ваш код в более высокие измерения. - person Sergio Haram; 22.05.2014
comment
Вы знаете @Divakar, неважно!!! Теперь я понимаю, насколько глупым был мой последний вопрос! - person Sergio Haram; 22.05.2014
comment
@SergioHaram Если A равно (1000x1000), это все еще 2D, и поэтому опубликованный код будет работать. - person Divakar; 22.05.2014
comment
Да, я просто понимаю, что :D извините! - person Sergio Haram; 22.05.2014
comment
@SergioHaram, ха-ха, все отлично! - person Divakar; 22.05.2014
comment
@Дивакар Удивительно! Большое вам спасибо, теперь я буду использовать этот трюк для вычисления пользовательских матричных умножений (также известных как Generalized Matrix Product). - person gaborous; 14.06.2014
comment
@user1121352 user1121352 Да, bsxfun действительно отличный инструмент, и если вы заинтересованы в умножении многомерных массивов, вы также можете использовать bsxfun там. Эта проблема говорит об этом, посмотри! - person Divakar; 14.06.2014

Функция kron делает именно это:

kron(x.',A)
person Luis Mendo    schedule 22.05.2014
comment
Kronecker product(!) симпатичный @Luis Mendo! - person Sergio Haram; 22.05.2014
comment
Спасибо, парни! Я волновался, что разместил очень скучный вопрос... (может быть, это так), но я многому научился за этот час!!! - person Sergio Haram; 22.05.2014
comment
@Divakar Да, отличный инструмент. Если вам когда-нибудь понадобится сделать то же самое, скажем, с суммами вместо произведений, взгляните на код kron: он использует meshgrid для генерации всех комбинаций и не более того. - person Luis Mendo; 22.05.2014

Вот мой тест методов, упомянутых до сих пор, вместе с несколькими моими дополнениями:

function [t,v] = testMatMult()
    % data
    %{
    x = [1 2 3 4 5 6 7 8 9 10 11 12];
    A = [11 14 1; 5 8 18; 10 8 19; 13 20 16];
    %}
    x = 1:50;
    A = randi(100, [1000,1000]);

    % functions to test
    fcns = {
        @() func1_repmat(A,x)
        @() func2_bsxfun_3rd_dim(A,x)
        @() func2_forloop_3rd_dim(A,x)
        @() func3_kron(A,x)
        @() func4_forloop_matrix(A,x)
        @() func5_forloop_cell(A,x)
        @() func6_arrayfun(A,x)
    };

    % timeit
    t = cellfun(@timeit, fcns, 'UniformOutput',true);

    % check results
    v = cellfun(@feval, fcns, 'UniformOutput',false);
    isequal(v{:})
    %for i=2:numel(v), assert(norm(v{1}-v{2}) < 1e-9), end
end

% Amro
function B = func1_repmat(A,x)
    B = repmat(x, size(A,1), 1);
    B = bsxfun(@times, B(:), repmat(A,numel(x),1));
end

% Divakar
function B = func2_bsxfun_3rd_dim(A,x)
    B = bsxfun(@times, A, permute(x, [3 1 2]));
    B = reshape(permute(B, [1 3 2]), [], size(A,2));
end

% Vissenbot
function B = func2_forloop_3rd_dim(A,x)
    B = zeros([size(A) numel(x)], 'like',A);
    for i=1:numel(x)
        B(:,:,i) = x(i) .* A;
    end
    B = reshape(permute(B, [1 3 2]), [], size(A,2));
end

% Luis Mendo
function B = func3_kron(A,x)
    B = kron(x(:), A);
end

% SergioHaram & TheMinion
function B = func4_forloop_matrix(A,x)
    [m,n] = size(A);
    p = numel(x);
    B = zeros(m*p,n, 'like',A);
    for i=1:numel(x)
        B((i-1)*m+1:i*m,:) = x(i) .* A;
    end
end

% Amro
function B = func5_forloop_cell(A,x)
    B = cell(numel(x),1);
    for i=1:numel(x)
        B{i} = x(i) .* A;
    end
    B = cell2mat(B);
    %B = vertcat(B{:});
end

% Amro
function B = func6_arrayfun(A,x)
    B = cell2mat(arrayfun(@(xx) xx.*A, x(:), 'UniformOutput',false));
end

Результаты на моей машине:

>> t
t =
    0.1650    %# repmat (Amro)
    0.2915    %# bsxfun in the 3rd dimension (Divakar)
    0.4200    %# for-loop in the 3rd dim (Vissenbot)
    0.1284    %# kron (Luis Mendo)
    0.2997    %# for-loop with indexing (SergioHaram & TheMinion)
    0.5160    %# for-loop with cell array (Amro)
    0.4854    %# arrayfun (Amro)

(Эти временные интервалы могут немного различаться между разными запусками, но это должно дать нам представление о том, как сравниваются методы)

Обратите внимание, что некоторые из этих методов будут вызывать ошибки нехватки памяти для больших входных данных (например, мое решение, основанное на repmat, может легко исчерпать память). Другие будут работать значительно медленнее для больших размеров, но не будут ошибаться из-за исчерпания памяти (например, решение kron).

Я думаю, что метод bsxfun func2_bsxfun_3rd_dim или прямой цикл for func4_forloop_matrix (благодаря MATLAB JIT) являются лучшими решениями в этом случае.

Конечно, вы можете изменить приведенные выше параметры бенчмарка (размер x и A) и сделать свои собственные выводы :)

person Amro    schedule 24.06.2014

Просто чтобы добавить альтернативу, вы, возможно, можете использовать cellfun для достижения того, чего хотите. Вот пример (немного измененный из вашего):

x = randi(2, 5, 3)-1;
a = randi(3,3);
%// bsxfun 3D (As implemented in the accepted solution)
val = bsxfun(@and, a, permute(x', [3 1 2])); %//'
out = reshape(permute(val,[1 3 2]),size(val,1)*size(val,3),[]);
%// cellfun (My solution)
val2 = cellfun(@(z) bsxfun(@and, a, z), num2cell(x, 2), 'UniformOutput', false);
out2 = cell2mat(val2); % or use cat(3, val2{:}) to get a 3D matrix equivalent to val and then permute/reshape like for out
%// compare
disp(nnz(out ~= out2));

Оба дают одинаковый точный результат.

Для получения дополнительной информации и рекомендаций по использованию Cellfun см.: http://matlabgeeks.com/tips-tutorials/computation-using-cellfun/

А также это: https://stackoverflow.com/a/1746422/1121352

person gaborous    schedule 14.06.2014

Если ваш вектор x имеет длину = 12, а ваша матрица размером 3x4, я не думаю, что использование того или другого сильно изменится с точки зрения времени. Если вы работаете с матрицей и вектором большего размера, это может стать проблемой.

Итак, прежде всего, мы хотим умножить вектор на матрицу. В методе for-loop это даст что-то вроде этого:

s = size(A);
new_matrix(s(1),s(2),numel(x)) = zeros;   %This is for pre-allocating. If you have a big vector or matrix, this will help a lot time efficiently.

for i = 1:numel(x)
    new_matrix(:,:,i)= A.*x(i)
end

Это даст вам трехмерную матрицу, где каждое третье измерение является результатом вашего умножения. Если это не то, что вы ищете, я добавлю другое решение, которое может быть более эффективным с большими матрицами и векторами.

person Vissenbot    schedule 22.05.2014
comment
большое спасибо! Я действительно получаю нужные мне матрицы, но мне нужна только одна new_matrix, содержащая все результаты, которые я получаю при использовании вашего кода. И да, я работаю с очень большими матрицами, здесь я только что привел простой пример, чтобы другие поняли мою проблему. - person Sergio Haram; 22.05.2014
comment
Да, я заметил после того, как увидел ответ Дивакара! Я неправильно прочитал и подумал, что это матрица 12xNxM, а не 12*NxM! Ответы Divakar кажутся мне довольно аккуратными! - person Vissenbot; 22.05.2014