矩阵乘法算法

Matrix multiplication algorithm

本文关键字:算法      更新时间:2023-10-16

我有mat2,mat3和mat4类,分别表示2x2,3x3和4x4矩阵。我正在尝试通过重载*=运算符来实现乘法算法。以 mat4 为例,下面是声明:

mat4& operator*=(const mat4 &m);

乘法算法将返回对调用对象的引用。这意味着它将返回对this的引用。乘法算法的实现定义如下:

mat4& mat4::operator *=(const mat4& m)
{
    for (uint i = 0; i < row(); i++)
    {
        for (uint j = 0; j < col(); j++)
        {
            for (uint k = 0; k < m.col(); k++)
            {
                data[i][j] += (data[i][k] * m.data[k][j]);
            }
        }
    }
    return *this;
}

其中uintunsigned int的类型定义。因为运算符重载是一个类函数,所以不需要有一个 lhs 矩阵,而是提供一个我称之为 m 的 rhs 矩阵。在这种情况下,函数 row()col() 将始终返回 4,因为它是一个 mat4x4;这些函数是 MAT4 类的一部分。属性data是具有固定大小的浮点数的二维数组。问题是这个算法没有产生正确的结果。例如:

mat4 m1(1.0, 0.0, 0.0, 30.0,
        0.0, 1.0, 0.0, 30.0,
        0.0, 0.0, 1.0, 30.0,
        0.0, 0.0, 0.0, 1.0);
mat4 m2(23.0, 21.0, 0.0, 1.0,
        10.0, 9.0, 1.0, 0.0,
        1.0, 2.0, 0.0, 0.0,
        3.0, 2.0, 9.0, 9.0);
auto result = m1 * m2;
cout << result << endl;

正如您所知,我也重载了<<运算符,以使矩阵的cout成为可能且容易。正如您也知道的那样,我的矩阵是列主矩阵,因为我打算将它们与 OpenGL 一起使用。因此,像 m1.data[3][2] 这样的东西意味着第 4 列,第 3 行。结果几乎不是它应该的样子:

mat4 = 
[ 
    24   5040   5040    54
    10   2110   2110    40
    1    212    213     31
    0    0      0       1
]

我的问题是乘法算法不起作用并产生正确的结果。如何更正算法,使其作为矩阵乘法的结果产生正确的结果?

我目前重载*运算符,如下所示:

mat4 mat4::operator *(const mat4& m) const
{
    mat4 result = *this;
    result *= m;
    return result;
}

这利用了*=运算符过载。不幸的是,*运算符过载也不起作用。

问题是你正在写入与你正在读取的相同的矩阵。这意味着您在计算中使用了结果矩阵中的某些值,而不是原始矩阵中的值。

要解决此问题,请创建一个临时的二维数组,您可以写入该数组:

mat4& mat4::operator *=(const mat4& m)
{
    float buffer[4][4]; // Temporary matrix
    for (uint i = 0; i < row(); i++)
    {
        for (uint j = 0; j < col(); j++)
        {
            // You might want to set the values in the buffer to 0 just in case:
            buffer[i][j] = 0.0f;
            for (uint k = 0; k < m.col(); k++)
            {
                buffer[i][j] += data[i][k] * m.data[k][j];
            }
        }
    }
    // Now that all the values of the new matrix have been calculated you can write to data
    for (uint i = 0; i < row(); i++)
    {
        for (uint j = 0; j < col(); j++)
        {
           data[i][j] = buffer[i][j];
        }
    }
    return *this;
}

你的问题是你在(*this)data字段中弄乱了数据(你在计算过程中读取和写入同一个变量),所以你有错误的数据。

您需要使用临时矩阵,然后在计算后复制其数据:

mat2& operator*=(const mat2 &m)
{
    // I set all the elements of the temp_data to 0. It is very important
    array<array<int, 2>,2> temp_data = {{{0,0},{0,0}}};
    for (uint i = 0; i < row(); i++)
    {
        for (uint j = 0; j < col(); j++)
        {
            for (uint k = 0; k < m.col(); k++)
            {
                temp_data[i][j] += (data[i][k] * m.data[k][j]);
            }
        }
    }
    this->data = temp_data;
    return *this;
}

使用示例:

int main()
{
    array<array<int, 2>,2> data1 = {{{1,0},{0,1}}};
    array<array<int, 2>,2> data2 = {{{1,2},{3,4}}};
    mat2 m1;
    m1.data = data1;
    mat2 m2;
    m2.data = data2;
    m1 *= m2;
    for (auto el: m1.data) {
        for (auto ele : el) {
            cout << ele << " ";
        }
        cout << endl;
    }
    return 0;
}

输出:

1 2                                                 
3 4