【问题标题】:Efficient multiplication of a matrix and a "list of matrices" in MATLABMATLAB中矩阵和“矩阵列表”的有效乘法
【发布时间】:2016-10-25 12:59:42
【问题描述】:

我想计算以下矩阵

M(i,j)=sum_(k,l) c(k,l) kron(N(:,:,k),N(:,:,l))

其中N 的前两个索引是行数和列数(在这种情况下相等),最后一个索引表示我有一个矩阵族N,而矩阵c 只是一个系数矩阵。指数 kl 从 1 到 50(在我的具体情况下)。

为此,我实现了以下算法:

c=randn(50,50);
N=randn(26,26,50);
M=zeros(size(N,1)^2);
for k=1:size(N,3)
for l=1:size(N,3)
M=M+c(k,l).*kron(N(:,:,k),N(:,:,l));
end
end

这段代码有一些有趣的地方,我不知道为什么会发生:

假设我截断了k 中的总和,直到一个有限的数字nk 从 1 变为 n 与从 1 变为 n+1 时的计算时间差远高于(大约和数量级)仅计算由索引的矩阵所需的时间k=n+1 并将其与计算到 k=n 的总矩阵相加。

我的问题是:是否有更有效的方法来计算我正在寻找的矩阵?这个算法出了什么问题?

非常感谢。

【问题讨论】:

  • zeros(n) 创建一个维度为 nxn 的 2D 方阵,因此在您的示例中,您创建 M,一个 2D 矩阵,但您尝试使用 M(:,:,k) 访问第三维
  • @obchardon 很确定这是他的代码中的错字。亚历克斯 - 检查我的编辑。
  • @Alex 您的公式与您的代码不完全匹配。那么首先,你能确认你上面的当前代码产生了正确的预期输出吗?只有这样你才应该考虑优化/矢量化这些循环..
  • @Amro 唯一缺少的是输入矩阵c 和输入矩阵系列N。其余的,代码正是我正在运行的。您可以放置​​您喜欢的矩阵,但我现在添加一些以供参考。
  • @Alex 好的。第一个想法是您可以将双循环简化为一个。请注意,[kron(A,C); kron(B,C)]kron([A;B],C) 相同。因此,您可能可以将所有切片 N(:,:,k) 连接到一个高矩阵中,然后在一个循环中将其乘以每个切片 N(:,:,l) 而不是两个。然后您将它们拆分并累积。

标签: matlab optimization matrix matrix-multiplication


【解决方案1】:

TL;DR:这比你的代码慢,我只是想分享我尝试过的......


下面是我在 cmets (funcAmro) 中提到的想法的实现,与您的原始代码 (funcAlex) 相比:

function [t,v] = testKron()
    s1 = 26;
    s3 = 50;
    c = randn(s3,s3);
    N = randn(s1,s1,s3);

    funcs = {
        @() funcAlex(N,c)
        @() funcAmro(N,c)
    };
    t = cellfun(@timeit, funcs);
    v = cellfun(@feval, funcs, 'Uniform',false);
    norm(v{1}-v{2})
end

function M = funcAlex(N,c)
    [s1,~,s3] = size(N);
    M = zeros(s1*s1);
    for i=1:s3
        for j=1:s3
            M = M + c(i,j) .* kron(N(:,:,i), N(:,:,j));
        end
    end
end

function M = funcAmro(N,c)
    [s1,~,s3] = size(N);
    % tall matrix, NN = cat(1, N(:,:,1), ..., N(:,:,s3))
    NN = reshape(permute(N, [1 3 2]), s1*s3, s1);
    % accumulate kron results
    M = zeros(s1*s1*s3, s1*s1);
    for i=1:s3
        M = M + bsxfun(@times, repelem(c(:,i), s1*s1), kron(NN, N(:,:,i)));
    end
    % split M back into s3 slices each of size s1^2-by-s1^2
    M = permute(reshape(M.', s1*s1, s1*s1, s3), [2 1 3]);
    % sum along slices
    M = sum(M,3);
end

使用您提到的大小(N 是一个 26×26×50 数组),我在运行 R2016b 的机器上得到以下时序:

>> t = testKron
t =
    3.2770    % funcAlex
    5.5119    % funcAmro

所以我的方法比较慢(更不用说可读性差了!)。我想所有的置换/重塑都有不可忽略的开销..

当然,得到的矩阵 M 是等价的(差异是 4.5801e-12 接近机器 epsilon)。

【讨论】:

  • 我应该提一下,当s1 很小(例如s1=5; s3=50;s1=5; s3=100;)时,我的方法计时更快
  • 感谢分享代码。我对这两种代码都进行了一些尝试,我发现问题不在于 Kronecker 产品,而在于总和需要永远。我想这与一些系数是我正在使用的半定程序求解包 YALMIP 的变量有关。
  • 我不熟悉那个包,但是如果求解器返回的系数变量不是常规的“双矩阵”,你应该在循环之外进行转换。 MATLAB 中的对象索引比原生数组索引慢很多
  • @Alex:我也突然想到,如果您更改代码 funcAlex 中 for 循环的顺序(先检查 j,然后检查 i),它可能会加快双循环一点点。这与 MATLAB 以列优先顺序存储 c 数组的事实有关,因此 faster 可以连续访问元素。甚至可能在外循环中存储切片N(:,:,j) 的副本,以避免在内循环中重复获取同一切片。值得一试:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 2013-06-18
相关资源
最近更新 更多