【问题标题】:bsxfun implementation in matrix multiplication矩阵乘法中的 bsxfun 实现
【发布时间】:2014-07-11 13:23:24
【问题描述】:

一如既往地尝试向您学习更多信息,我希望能通过以下代码获得一些帮助。

我需要完成以下工作:

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]

我需要能够将来自xeach 值与Aevery 值相乘,这意味着:

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

这将给我这个new_matrix 大小为(12*m x n) 假设A (mxn)。在这种情况下(12*4x3)

如何使用 matlab 中的bsxfun 来做到这一点?而且,这种方法会比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.

【问题讨论】:

  • for循环可以通过预先定义new_matrix的大小来完成,就像你自己说的那样,然后使用索引告诉你的new_matrix你希望这些元素保存在哪里,例如在您上面给出的代码new_matrix(((i-1)*12+1):(i*12))) = A.*x(i) 中,我只是在这里写的,所以不确定它是否有效。
  • 谢谢@Minion,我会检查它是否有效,我会告诉你!
  • @Minion 它几乎可以工作,我在1*new_matrix2*new_matrix 3*new_matrix ...等等其他一些我不知道它们来自哪里的计算之间得到了一些东西。
  • @SergioHaram 感谢您发布这个问题!希望这对对bsxfun 感兴趣的人会派上用场。
  • 酷!一些基准测试结果!!感谢您发布这些!

标签: performance matlab matrix matrix-multiplication bsxfun


【解决方案1】:

x发送到第三维,这样当bsxfunA相乘时,单例展开生效,将乘积结果扩展到第三维。然后,执行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这个词!呜!! :)

【讨论】:

  • 这应该工作得很好。至少它对我的测试有用。但我真的不明白代码。为什么要换行?你介意编辑你的答案并解释一下吗?
  • 再次感谢@Divakar :D!而且,我也将非常感谢您的解释!
  • @Divakar 好的,我理解x 扩展到三维,这是因为Adim,对吧?在第二行中,您将mapping 带回所需的dim。但是,如果我将数字 [3 1 2] 更改为任何其他顺序,代码是否仍然有效?你的第二行有[1 3 2](!)?关于第二行:size(val,3)[] 是做什么的?
  • 谢谢大家!我担心我发布了一个非常无聊的问题......(可能是)但我这个小时学到了很多东西!!!
  • @Divakar 很好地使用了bsxfun
【解决方案2】:

kron 函数正是这样做的:

kron(x.',A)

【讨论】:

  • Kronecker product(!) 不错的@Luis Mendo!
  • 谢谢大家!我担心我发布了一个非常无聊的问题......(可能是)但我这个小时学到了很多东西!!!
  • @Divakar 是的,很棒的工具。如果您需要对总和而不是产品做同样的事情,请查看kron 的代码:它使用meshgrid 生成所有组合,仅此而已
【解决方案3】:

这里是我目前提到的方法的基准,以及我自己的一些补充:

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)是这种情况下的最佳解决方案。

当然你可以改变上面的基准参数(xA的大小)并得出你自己的结论:)

【讨论】:

    【解决方案4】:

    只是添加一个替代方案,您也许可以使用 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

    【讨论】:

      【解决方案5】:

      如果您的向量 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 维都是乘法的结果。如果这不是您正在寻找的,我将添加另一个解决方案,它可能对更大的矩阵和向量更有效率。

      【讨论】:

      • 非常感谢!我确实得到了我需要的矩阵,但是我只需要一个 new_matrix 包含我在使用你的代码时得到的所有结果。是的,我正在处理非常大的矩阵,在这里,我只是举了一个简单的例子来让其他人理解我的问题。
      • 是的,当我看到 Divakar 的回答后我注意到了!我误读并认为这是一个 12xNxM 矩阵,而不是 12*NxM! Divakar 的答案对我来说似乎很整洁!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-15
      • 2017-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多