有没有一种算法可以就地增加平方矩阵?

时间:2014-08-22 15:43:21

标签: algorithm matrix graphics language-agnostic linear-algebra

用于乘以4x4矩阵的朴素算法如下所示:

void matrix_mul(double out[4][4], double lhs[4][4], double rhs[4][4]) {
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            out[i][j] = 0.0;
            for (int k = 0; k < 4; ++k) {
                out[i][j] += lhs[i][k] * rhs[k][j];
            }
        }
    }
}

显然,如果out == lhsout == rhs(此处==表示参考相等),此算法会给出虚假结果。是否有允许这些案例中的一个或两个不仅仅复制矩阵的版本?如果有必要,我很高兴为每个案例提供不同的功能。

我找到了this论文,但它讨论了Strassen-Winograd算法,这对我的小矩阵来说太过分了。 this问题的答案似乎表明,如果out == lhs && out == rhs(即,我们试图对矩阵求平方),那么它就不能在适当的位置完成,但即使在那里也没有令人信服的证据或证明。

3 个答案:

答案 0 :(得分:7)

我对这个答案并不感到兴奋(我发布的主要是为了沉默“显然无法完成”人群),但我怀疑是否有可能做得更好,真正的 - 放置算法(O(1)额外的存储字,用于乘以两个nxn矩阵)。让我们将两个矩阵称为A和B的乘法。假设A和B没有别名。

如果A是上三角形,则乘法问题看起来像这样。

[a11 a12 a13 a14] [b11 b12 b13 b14]
[ 0  a22 a23 a24] [b21 b22 b23 b24]
[ 0   0  a33 a34] [b31 b32 b33 b34]
[ 0   0   0  a44] [b41 b42 b43 b44]

我们可以将产品计算到B中,如下所示。将第一行B乘以a11。将第二行B的a12次加到第一行。将第三行B的a13次加到第一行。将第四行B的a14次加到第一行。

现在,我们用正确的产品覆盖了B的第一行。幸运的是,我们不再需要它了。将第二行B乘以a22。将第三行B的a23次加到第二行。 (你明白了。)

同样,如果A是单位下三角形,则乘法问题看起来像这样。

[ 1   0   0   0 ] [b11 b12 b13 b14]
[a21  1   0   0 ] [b21 b22 b23 b24]
[a31 a32  1   0 ] [b31 b32 b33 b34]
[a41 a42 a43  1 ] [b41 b42 b43 b44]

a43次添加到第三行B到第四行。将第二行B的a42次加到第四行。将第一行B的a41次加到第四行。将第二行B的a32次加到第三行。 (你明白了。)

完整的算法是将LU分解到位,将UB乘以B,将LB乘以B,然后将LU未分解A到位(我不确定是否有人这样做过,但似乎很容易扭转步骤)。有大约一百万个理由不在实践中实现这一点,其中两个原因是A可能不是LU可分解的,并且A不会通过浮点算法完全重建。

答案 1 :(得分:7)

这个答案比我的另一个更明智,尽管它使用了一整列额外的存储空间,并且具有与天真复制算法相同的数据移动量。要将A与B相乘,将乘积存储在B中(再次假设A和B分别存储):

For each column of B,
    Copy it into the auxiliary storage column
    Compute the product of A and the auxiliary storage column into that column of B

我先切换伪代码进行复制,因为对于大型矩阵,缓存效果可能会使A乘以连续的辅助列而不是B中的非连续条目更有效。

答案 2 :(得分:0)

这个答案大约是4x4矩阵。假设,正如您所建议的,out可以引用lhsrhs,并且A和B具有统一比特长度的单元格,以便在技术上能够执行乘法在适当的位置,A和B的元素,如有符号整数,通常不能大于或小于± floor (sqrt (2 ^ (cellbitlength - 1) / 4))

在这种情况下,我们可以通过位移或位标志和模运算的组合将A的元素破解为B(反之亦然),并将产品计算为前一矩阵。如果A和B紧密包装,除特殊情况或限制外,我们无法承认out引用lhsrhs

现在使用朴素方法与David的第二个算法描述不同,只是存储在A或B本身中的额外列。或者,我们可以根据以下时间表实施Strassen-Winograd算法,同样在lhsrhs之外没有存储。 (p0,...,p6C的表述取自Jonathan Golan的线性代数和初级研究生应该知道的第166页。)

p0 = (a11 + a12)(b11 + b12), p1 = (a11 + a22)b11, p2 = a11(b12 - b22),
p3 = (a21 - a11)(b11 + b12), p4 = (a11 + a12)b22, p5 = a22(b21 - b11),
p6 = (a12 - a22)(b21 + b22)
    ┌                                      ┐
c = │ p0 + p5 - p4 + p6,      p2 + p4      │ 
    │   p1 + p5        , p0 - p1 + p2 + p3 │
    └                                      ┘

时间表:

下面的每个p都是2x2象限; “x”表示未分配; “nc”,没有变化。为了计算每个p,我们使用未分配的2x2象限来叠加2x2块矩阵加法或减法的(一个或两个)结果,使用上面相同的位移或模块化方法;然后我们将它们的产品(产生单个元素的七个乘法)以任意顺序直接添加到目标块中(请注意,对于2x2大小的p2p4,我们使用西南象限rhs,此时不再需要)。例如,要写入第一个2x2大小的p6,我们将块矩阵减法rhs(a12) - rhs(a22)和块矩阵加法rhs(b21) + rhs(b22)叠加到lhs21子矩阵上;然后将该块乘法{7}的七个单元素p中的每一个直接添加到(a12 - a22) X (b21 + b22)子矩阵。

lhs11