внедряване на 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

Благодаря предварително!!

РЕДАКТИРАНЕ: След дадените решения

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

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 1 000? а не 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 е 1 000? Какво е това 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
@Divakar Невероятно! Благодаря ви много, отсега нататък ще използвам този трик, за да изчислявам персонализирани матрични умножения (известен още като обобщен матричен продукт). - person gaborous; 14.06.2014
comment
@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

Това ще ви даде 3D матрица, като всяко 3-то измерение е резултат от вашето умножение. Ако това не е това, което търсите, ще добавя друго решение, което може да е по-ефективно във времето с по-големи матрици и вектори.

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