运算符重载矩阵乘法
Operator Overloading Matrix Multiplication
我遇到的问题是如何为 K 的最内层循环获取正确的数字列。
一个例子是一个 2x3 矩阵和一个 3x2 矩阵相乘。
结果应该是一个 2x2 矩阵,但目前我不知道如何将 2 的值发送给运算符重载函数。
它应该是
诠释 k = 0; k < 第一个矩阵的列;k++
Matrix::Matrix(int row, int col)
{
rows = row;
cols = col;
cx = (float**)malloc(rows * sizeof(float*)); //initialize pointer to pointer matrix
for (int i = 0; i < rows; i++)
*(cx + i) = (float*)malloc(cols * sizeof(float));
}
Matrix Matrix::operator * (Matrix dx)
{
Matrix mult(rows, cols);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
mult.cx[i][j] = 0;
for (int k = 0; k < ?;k++) //?????????????
{
mult.cx[i][j] += cx[i][k] * dx.cx[k][j];
}
}
}
mult.print();
return mult;
//calling
Matrix mult(rowA, colB);
mult = mat1 * mat2;
}
线性代数规则说结果应该有维度行 x dx.cols
Matrix Matrix::operator * (Matrix dx)
{
Matrix mult(rows, dx.cols);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
mult.cx[i][j] = 0;
for (int k = 0; k < cols;k++) //?????????????
{
mult.cx[i][j] += cx[i][k] * dx.cx[k][j];
}
}
}
mult.print();
return mult;
一些随机提示:
- 你的代码基本是C;它不使用(例如)C++ 中重要的内存安全特性。 (运算符重载是唯一在使用的类似 C++ 的功能。)我建议您多利用 C++。
- 严格避免在 C++ 中使用
malloc()
。使用 std::make_unique(...)
或者,如果没有其他方法,则使用原始 new
运算符。 (顺便说一句,还有 always 另一种方式。)在后一种情况下,确保有一个带有 delete
或 delete[]
的析构函数。在您的代码段中使用 malloc()
闻起来像内存泄漏。
- 可以是
const
应该是const
。在构造函数的初始化列表中初始化尽可能多的 class 成员,并在适当的情况下使它们成为 const
。 (例如,Matrix
尺寸不变,应为 const
。)
- 当写一个类似容器的 class 时(从某种意义上说,
Matrix
可能是),不要将其限制为单一数据类型;你未来的自己会感谢你。 (如果您需要 double
而不是 float
怎么办?是单行编辑还是通宵搜索遗忘的 float
侵蚀了您的精度? )
这是一个显示矩阵乘法的快速而粗略的可运行示例:
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <memory>
namespace matrix {
using std::size_t;
template<typename Element>
class Matrix {
class Accessor {
public:
Accessor(const Matrix& mat, size_t m) : data_(&mat.data_[m * mat.n_]) {}
Element& operator [](size_t n) { return data_[n]; }
const Element& operator [](size_t n) const { return data_[n]; }
private:
Element *const data_;
};
public:
Matrix(size_t m, size_t n) : m_(m), n_(n),
data_(std::make_unique<Element[]>(m * n)) {}
Matrix(Matrix &&rv) : m_(rv.m_), n_(rv.n_), data_(std::move(rv.data_)) {}
Matrix operator *(const Matrix& right) {
Matrix result(m_, right.n_);
for (size_t i = 0; i < m_; ++i)
for (size_t j = 0; j < right.n_; ++j) {
result[i][j] = Element{};
for (size_t k = 0; k < n_; ++k) result[i][j] +=
(*this)[i][k] * right[k][j];
}
return result;
}
Accessor operator [](size_t m) { return Accessor(*this, m); }
const Accessor operator [](size_t m) const { return Accessor(*this, m); }
size_t m() const { return m_; }
size_t n() const { return n_; }
private:
const size_t m_;
const size_t n_;
std::unique_ptr<Element[]> data_;
};
template<typename Element>
std::ostream& operator <<(std::ostream &out, const Matrix<Element> &mat) {
for (size_t i = 0; i < mat.m(); ++i) {
for (size_t j = 0; j < mat.n(); ++j) out << std::setw(4) << mat[i][j];
out << std::endl;
}
return out;
}
} // namespace matrix
int main() {
matrix::Matrix<int> m22{2, 2};
m22[0][0] = 0; // TODO: std::initializer_list
m22[0][1] = 1;
m22[1][0] = 2;
m22[1][1] = 3;
matrix::Matrix<int> m23{2, 3};
m23[0][0] = 0; // TODO: std::initializer_list
m23[0][1] = 1;
m23[0][2] = 2;
m23[1][0] = 3;
m23[1][1] = 4;
m23[1][2] = 5;
matrix::Matrix<int> m32{3, 2};
m32[0][0] = 5; // TODO: std::initializer_list
m32[0][1] = 4;
m32[1][0] = 3;
m32[1][1] = 2;
m32[2][0] = 1;
m32[2][1] = 0;
std::cout << "Original:\n\n";
std::cout << m22 << std::endl << m23 << std::endl << m32 << std::endl;
std::cout << "Multiplied:\n\n";
std::cout << m22 * m22 << std::endl
<< m22 * m23 << std::endl
<< m32 * m22 << std::endl
<< m23 * m32 << std::endl
<< m32 * m23 << std::endl;
}
可能的改进和其他建议:
- 添加一致性检查。
throw
,例如,std::invalid_argument
当维度在乘法上不匹配时,即当 m_ != right.n_
时,std::range_error
当 operator []
得到一个输出时越界论证。 (检查可能是可选的,可以激活(例如)使用 if constexpr
进行调试。)
- 使用
std::initializer_list
等进行初始化,这样您就可以(例如)在线初始化 const Matrix
。
- 始终 使用
valgrind
检查您的代码。 (提示:使用 -g
构建可以让 valgrind
也打印出发生错误的行号(或者相关的先前(取消)分配发生的地方)。)
- 我可以通过 not 在任何地方使用
operator []
并从中获得一些乐趣 - 我可以使代码更短更优雅(不一定更有效;编译器优化现在很神奇)取而代之的是指针算法。
- 改进类型系统,以便(例如)
Matrix
个不同类型的实例可以很好地相互配合。也许 Matrix<int>
乘以 Matrix<double>
可以产生 Matrix<double>
等。还可以支持标量值与 Matrix
之间的乘法。或者在 Matrix
和 std::array
、std::vector
等之间
我遇到的问题是如何为 K 的最内层循环获取正确的数字列。 一个例子是一个 2x3 矩阵和一个 3x2 矩阵相乘。 结果应该是一个 2x2 矩阵,但目前我不知道如何将 2 的值发送给运算符重载函数。 它应该是 诠释 k = 0; k < 第一个矩阵的列;k++
Matrix::Matrix(int row, int col)
{
rows = row;
cols = col;
cx = (float**)malloc(rows * sizeof(float*)); //initialize pointer to pointer matrix
for (int i = 0; i < rows; i++)
*(cx + i) = (float*)malloc(cols * sizeof(float));
}
Matrix Matrix::operator * (Matrix dx)
{
Matrix mult(rows, cols);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
mult.cx[i][j] = 0;
for (int k = 0; k < ?;k++) //?????????????
{
mult.cx[i][j] += cx[i][k] * dx.cx[k][j];
}
}
}
mult.print();
return mult;
//calling
Matrix mult(rowA, colB);
mult = mat1 * mat2;
}
线性代数规则说结果应该有维度行 x dx.cols
Matrix Matrix::operator * (Matrix dx)
{
Matrix mult(rows, dx.cols);
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
mult.cx[i][j] = 0;
for (int k = 0; k < cols;k++) //?????????????
{
mult.cx[i][j] += cx[i][k] * dx.cx[k][j];
}
}
}
mult.print();
return mult;
一些随机提示:
- 你的代码基本是C;它不使用(例如)C++ 中重要的内存安全特性。 (运算符重载是唯一在使用的类似 C++ 的功能。)我建议您多利用 C++。
- 严格避免在 C++ 中使用
malloc()
。使用std::make_unique(...)
或者,如果没有其他方法,则使用原始new
运算符。 (顺便说一句,还有 always 另一种方式。)在后一种情况下,确保有一个带有delete
或delete[]
的析构函数。在您的代码段中使用malloc()
闻起来像内存泄漏。 - 可以是
const
应该是const
。在构造函数的初始化列表中初始化尽可能多的 class 成员,并在适当的情况下使它们成为const
。 (例如,Matrix
尺寸不变,应为const
。) - 当写一个类似容器的 class 时(从某种意义上说,
Matrix
可能是),不要将其限制为单一数据类型;你未来的自己会感谢你。 (如果您需要double
而不是float
怎么办?是单行编辑还是通宵搜索遗忘的float
侵蚀了您的精度? )
这是一个显示矩阵乘法的快速而粗略的可运行示例:
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <memory>
namespace matrix {
using std::size_t;
template<typename Element>
class Matrix {
class Accessor {
public:
Accessor(const Matrix& mat, size_t m) : data_(&mat.data_[m * mat.n_]) {}
Element& operator [](size_t n) { return data_[n]; }
const Element& operator [](size_t n) const { return data_[n]; }
private:
Element *const data_;
};
public:
Matrix(size_t m, size_t n) : m_(m), n_(n),
data_(std::make_unique<Element[]>(m * n)) {}
Matrix(Matrix &&rv) : m_(rv.m_), n_(rv.n_), data_(std::move(rv.data_)) {}
Matrix operator *(const Matrix& right) {
Matrix result(m_, right.n_);
for (size_t i = 0; i < m_; ++i)
for (size_t j = 0; j < right.n_; ++j) {
result[i][j] = Element{};
for (size_t k = 0; k < n_; ++k) result[i][j] +=
(*this)[i][k] * right[k][j];
}
return result;
}
Accessor operator [](size_t m) { return Accessor(*this, m); }
const Accessor operator [](size_t m) const { return Accessor(*this, m); }
size_t m() const { return m_; }
size_t n() const { return n_; }
private:
const size_t m_;
const size_t n_;
std::unique_ptr<Element[]> data_;
};
template<typename Element>
std::ostream& operator <<(std::ostream &out, const Matrix<Element> &mat) {
for (size_t i = 0; i < mat.m(); ++i) {
for (size_t j = 0; j < mat.n(); ++j) out << std::setw(4) << mat[i][j];
out << std::endl;
}
return out;
}
} // namespace matrix
int main() {
matrix::Matrix<int> m22{2, 2};
m22[0][0] = 0; // TODO: std::initializer_list
m22[0][1] = 1;
m22[1][0] = 2;
m22[1][1] = 3;
matrix::Matrix<int> m23{2, 3};
m23[0][0] = 0; // TODO: std::initializer_list
m23[0][1] = 1;
m23[0][2] = 2;
m23[1][0] = 3;
m23[1][1] = 4;
m23[1][2] = 5;
matrix::Matrix<int> m32{3, 2};
m32[0][0] = 5; // TODO: std::initializer_list
m32[0][1] = 4;
m32[1][0] = 3;
m32[1][1] = 2;
m32[2][0] = 1;
m32[2][1] = 0;
std::cout << "Original:\n\n";
std::cout << m22 << std::endl << m23 << std::endl << m32 << std::endl;
std::cout << "Multiplied:\n\n";
std::cout << m22 * m22 << std::endl
<< m22 * m23 << std::endl
<< m32 * m22 << std::endl
<< m23 * m32 << std::endl
<< m32 * m23 << std::endl;
}
可能的改进和其他建议:
- 添加一致性检查。
throw
,例如,std::invalid_argument
当维度在乘法上不匹配时,即当m_ != right.n_
时,std::range_error
当operator []
得到一个输出时越界论证。 (检查可能是可选的,可以激活(例如)使用if constexpr
进行调试。) - 使用
std::initializer_list
等进行初始化,这样您就可以(例如)在线初始化const Matrix
。 - 始终 使用
valgrind
检查您的代码。 (提示:使用-g
构建可以让valgrind
也打印出发生错误的行号(或者相关的先前(取消)分配发生的地方)。) - 我可以通过 not 在任何地方使用
operator []
并从中获得一些乐趣 - 我可以使代码更短更优雅(不一定更有效;编译器优化现在很神奇)取而代之的是指针算法。
- 改进类型系统,以便(例如)
Matrix
个不同类型的实例可以很好地相互配合。也许Matrix<int>
乘以Matrix<double>
可以产生Matrix<double>
等。还可以支持标量值与Matrix
之间的乘法。或者在Matrix
和std::array
、std::vector
等之间