在Matlab中创建和操作三维矩阵

6
我正在拼命尝试避免在Matlab中使用for循环,但我无法想出如何做到。这是情况:
我有两个m x n矩阵A和B,以及长度为d的两个向量v和w。我想要对A和v进行外积运算,以便得到一个m x n x d矩阵,其中(i,j,k)条目是A_(i,j)*v_k,B和w同理。
之后,我想添加结果矩阵m x n x d,然后沿着最后一个维度取平均值,以获得一个m x n矩阵。
我相信我可以处理后者,但前者让我完全卡住了。我尝试使用bsxfun,但无济于事。有人知道一种有效的方法吗?非常感谢!
编辑:此版本是在下面三个很棒的答案之后。gnovice毫无疑问是回答我提出的问题的最佳答案。然而,我原本想问的问题涉及在取平均值之前对每个条目进行平方。我忘记了最初提到这一部分。鉴于这种麻烦,其他两个答案都很好,但在编码之前进行代数上的巧妙技巧这次没有帮助。感谢大家的帮助!
4个回答

7

编辑:

即使问题已经更新,仍然可以使用代数方法来简化事情。您仍然不必费心处理三维矩阵。您的结果只会是这个:

output = mean(v.^2).*A.^2 + 2.*mean(v.*w).*A.*B + mean(w.^2).*B.^2;

如果您的矩阵和向量很大,与使用BSXFUNREPMAT的解决方案相比,此解决方案将由于所需内存量的减少而提供更好的性能。


说明:

假设M是在沿第三维取平均值之前得到的m×n×d矩阵,则沿第三维度的跨度将包含以下内容:

M(i,j,:) = A(i,j).*v + B(i,j).*w;

换句话说,向量vA(i,j)进行缩放,加上向量wB(i,j)进行缩放。当你应用逐元素平方时,就会得到这个结果:
M(i,j,:).^2 = (A(i,j).*v + B(i,j).*w).^2;
            = (A(i,j).*v).^2 + ...
              2.*A(i,j).*B(i,j).*v.*w + ...
              (B(i,j).*w).^2;

现在,当您沿第三个维度取平均值时,每个元素output(i,j)的结果将如下所示:
output(i,j) = mean(M(i,j,:).^2);
            = mean((A(i,j).*v).^2 + ...
                   2.*A(i,j).*B(i,j).*v.*w + ...
                   (B(i,j).*w).^2);
            = sum((A(i,j).*v).^2 + ...
                  2.*A(i,j).*B(i,j).*v.*w + ...
                  (B(i,j).*w).^2)/d;
            = sum((A(i,j).*v).^2)/d + ...
              sum(2.*A(i,j).*B(i,j).*v.*w)/d + ...
              sum((B(i,j).*w).^2)/d;
            = A(i,j).^2.*mean(v.^2) + ...
              2.*A(i,j).*B(i,j).*mean(v.*w) + ...
              B(i,j).^2.*mean(w.^2);

@gnovice:我认为OP想要先将结果矩阵相加,然后再取平均值。因此,独立地对每个向量取平均值是行不通的。 - Lambdageek
@Lambdageek:实际上,在这种情况下确实如此。上面的代码给出了与您回答中更复杂的步骤相同的结果。我会添加一个解释。 - gnovice
2
@gnovice 嗯。是的,你说得对。由于A和B基本上参与作为标量,只需考虑sum而不是mean,你的断言就成立了。我将删除我的答案。最好通过代数方式解决这个问题。 - Lambdageek
@gnovice:谢谢。我太专注于无法完成的步骤,没有想到把代数计算做到底。不错。 - PengOne
@govice:不幸的是,我遗漏了一个细节,那就是在取平均值之前,我还需要进行一项不可交换的操作,因此我仍然需要知道如何进行矩阵转换。尽管如此,这是一个很好的答案,但我将接受“repmat”解决方案。 - PengOne
显示剩余3条评论

1

尝试将向量 vw 重新塑形为 1 x 1 x d

  mean (bsxfun(@times, A, reshape(v, 1, 1, [])) ...
        + bsxfun(@times, B, reshape(w, 1, 1, [])), 3)

在这里,我在reshape的参数中使用[],告诉它根据其他维度的乘积和向量中元素的总数来填充该维度。


这样做更快,而且更简洁。在取平均值之前,我需要对每个条目进行平方,而这种方法使我能够轻松地完成这个任务。非常感谢! - PengOne

1
使用repmat函数在第三维中复制矩阵。
A =

     1     2     3
     4     5     6

>> repmat(A, [1 1  10])

ans(:,:,1) =

     1     2     3
     4     5     6


ans(:,:,2) =

     1     2     3
     4     5     6

等等。


1

对于您的更新需求,您仍然不必使用任何显式循环或间接循环(如bsxfun等)。您可以通过以下简单的向量化解决方案实现所需功能。

output = reshape(mean((v(:)*A(:)'+w(:)*B(:)').^2),size(A));

由于 OP 只说明 vw 是长度为 d 的向量,因此上述解决方案适用于行向量和列向量。如果它们被认为是列向量,则可以将 v(:) 替换为 v,同样地,对于 w 也是如此。


您可以按照以下方式检查是否与Lambdageek的答案(修改为平方项)匹配

outputLG = mean ((bsxfun(@times, A, reshape(v, 1, 1, [])) ...
        + bsxfun(@times, B, reshape(w, 1, 1, []))).^2, 3);

isequal(output,outputLG)

ans =

     1

@PengOne:我一开始以为vw是列向量(因为你只提到它的长度为d)。所以也许你有行向量,在这种情况下,请尝试v(:)*A(:)',同样适用于w。我会编辑我的答案来包括这个。 - user616736
@yoda:转置 v 就解决了问题。这也可以正常工作。谢谢! - PengOne
@PengOne:没问题 :) 只是出于好奇,你的矩阵和向量的大小是多少? - user616736
@yoda 大约是10K乘以10K,虽然不是正方形。理想情况下它们应该是1M乘以1M(同样不是完全的正方形),但是即使对于10K来说,for循环也太慢了。这应该可以加快速度。 - PengOne
@PengOne:1M乘1M??那太疯狂了。d的大小是多少? - user616736
@尤达 我知道 :-( 幸运的是,d要合理得多...大约7K。 - PengOne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接