在很多科学计算当中,矩阵运算可以说是必不可少。在密码学,计算机图形科学里面都有使用到矩阵。笔者这里自己实现了一个矩阵运算的模板,不过讲实话的,笔者并不认为写得有多好,不过足以给刚学习C++的朋友在代码设计上给到足够的启发,废话不多说了,直接看代码吧
1、代码总览
Matrix.h文件
#pragma once
#include<iostream>
#include<vector>
#include<cmath>
#include"MatrixUtil.h"
//矩阵错误信息宏定义
#ifndef MATRIXERRINFO
#ifndef MATRIXISNULL
#define MATRIXISNULL(matrix) if(matrix.mRow * matrix.mCol == 0) throw "该矩阵的函数或者列数为0"
#endif // !MATRIXISNULL
#ifndef LEFT_SPEC_NOTEQUAL_RIGTH
#define LEFT_SPEC_NOTEQUAL_RIGTH(left,right)\
if (left.mCol != right.mCol || left.mRow != right.mRow) throw "左右矩阵的行数和列数各不相"
#endif // !LEFT_SPEC_NOTEQUAL_RIGTH
#ifndef CANTMULTIPLY
#define CANTMULTIPLY(left,right) \
if(left.mCol != right.mRow) throw "左矩阵的列数和右矩阵的函数不相等"
#endif // !CANTMULTIPLY
#ifndef MATRIX_CANT_C2_VECTOR
#define MATRIX_CANT_C2_VECTOR(matrix)\
if(matrix.mCol > 1 && matrix.mRow > 1) throw "矩阵并不是一维的,不能获取长度"
#endif // !MATRIX_CANT_C2_VECTOR
#endif // !MATRIXERRINFO
#define PI 3.141592654
template<typename T>
class Matrix {
int mRow, mCol;
T* mArray;
public:
Matrix() :mRow(0), mCol(0), mArray(nullptr) {}
Matrix(int iRow, int iCol);
Matrix(T* iArray, int iRow, int iCol);
Matrix(const Matrix& imatrix);
Matrix<T>& operator=(const Matrix& iMatrix);
~Matrix() { delete[] mArray; }
int GetRowNumber() const{ return mRow; }
int GetColNumber() const{ return mCol; }
void SetRowNumber(int iRow) { mRow = iRow; }
void SetColNumber(int iCol) { mCol = iCol; }
T& operator()(int iRow, int iCol);
const T& GetElement(int iRow, int iCol) const;
Matrix<T> GetRow(int index) const;
Matrix<T> GetCol(int index) const;
public:
//矩阵计算
Matrix<T> operator+(const Matrix& iMatrix) const;
Matrix<T> operator-(const Matrix& iMatrix) const;
Matrix<T> operator*(const Matrix& iMatrix) const;
Matrix<T> operator*(double iNumber) const;
public:
static void Tran(Matrix<T>& iMatrix);
static Matrix<T> TranCopy(const Matrix<T>& iMatrix);
static bool IsZeroMatrix(const Matrix<T>& iMatrix);
friend Matrix<T> operator*(double iNumber, const Matrix& iMatrix) { return iMatrix * iNumber; }
friend std::ostream& operator<<(std::ostream& out, const Matrix& iMatrix) {
for (int i = 0; i < iMatrix.mRow; i++) {
std::cout << "[";
for (int j = 0; j < iMatrix.mCol; j++)
std::cout << iMatrix.GetElement(i, j) << (j == iMatrix.mCol - 1 ? "" : ", ");
std::cout << "]" << std::endl;
}
return out;
}
};
template<typename T>
Matrix<T>::Matrix(T* iArray, int iRow, int iCol) {
mRow = iRow; mCol = iCol;
mArray = new T[iRow * iCol];
memcpy(mArray, iArray, iRow * iCol * sizeof(T));
}
template<typename T>
Matrix<T>::Matrix(int iRow, int iCol) {
mRow = iRow; mCol = iCol;
mArray = new T[iRow * iCol]{};
}
template<typename T>
Matrix<T>::Matrix(const Matrix& iMatrix) {
mRow = iMatrix.mRow; mCol = iMatrix.mCol;
mArray = new T[mRow * mCol];
memcpy(mArray, iMatrix.mArray, mRow * mCol * sizeof(T));
}
template<typename T>
Matrix<T>& Matrix<T>::operator=(const Matrix& iMatrix) {
mRow = iMatrix.mRow; mCol = iMatrix.mCol;
mArray = new T[mRow * mCol];
memcpy(mArray, iMatrix.mArray, mRow * mCol * sizeof(T));
return (*this);
}
template<typename T>
Matrix<T> Matrix<T>::GetRow(int index) const {
Matrix<T> oMatrix(1, mCol);
for (int j = 0; j < mCol; j++)
oMatrix(0, j) = GetElement(index, j);
return oMatrix;
}
template<typename T>
Matrix<T> Matrix<T>::GetCol(int index) const {
Matrix<T> oMatrix(mRow, 1);
for (int i = 0; i < mRow; i++)
oMatrix(i, 0) = GetElement(i, index);
return oMatrix;
}
template<typename T>
T& Matrix<T>::operator()(int iRow, int iCol) {
return mArray[iRow * mCol + iCol];
}
template<typename T>
const T& Matrix<T>::GetElement(int iRow, int iCol) const {
return mArray[iRow * mCol + iCol];
}
template<typename T>
Matrix<T> Matrix<T>::operator+(const Matrix& iMatrix) const {
LEFT_SPEC_NOTEQUAL_RIGTH(iMatrix,(*this));
Matrix oMatrix(mRow, mCol);
for (int i = 0; i < mRow; i++)
for (int j = 0; j < mCol; j++)
oMatrix(i, j) = GetElement(i, j) + iMatrix.GetElement(i, j);
return oMatrix;
}
template<typename T>
Matrix<T> Matrix<T>::operator-(const Matrix& iMatrix) const {
LEFT_SPEC_NOTEQUAL_RIGTH(iMatrix,(*this));
Matrix oMatrix(mRow, mCol);
for (int i = 0; i < mRow; i++)
for (int j = 0; j < mCol; j++)
oMatrix(i, j) = GetElement(i, j) - iMatrix.GetElement(i, j);
return oMatrix;
}
template<typename T>
Matrix<T> Matrix<T>::operator*(const Matrix& iMatrix) const {
CANTMULTIPLY((*this), iMatrix);
int m = mRow, nLeft = mCol;
int nRight = iMatrix.mRow, p = iMatrix.mCol;
Matrix oMatrix(m, p);
for (int i = 0; i < m; i++) {
for (int j = 0; j < p; j++) {
T sum = static_cast<T>(0.0f);
for (int k = 0; k < nLeft; k++)
sum += GetElement(i, k) * iMatrix.GetElement(k, j);
oMatrix(i, j) = sum;
}
}
return oMatrix;
}
template<typename T>
Matrix<T> Matrix<T>::operator*(double iNumber) const {
MATRIXISNULL((*this));
Matrix oMatrix(mRow, mCol);
for (int i = 0; i < mRow; i++)
for (int j = 0; j < mCol; j++)
oMatrix(i, j) = static_cast<T>(GetElement(i, j) * iNumber);
return oMatrix;
}
template<typename T>
void Matrix<T>::Tran(Matrix<T>& iMatrix) {
MATRIXISNULL(iMatrix);
Matrix<T> matrix = iMatrix;
iMatrix.SetRowNumber(matrix.mCol); iMatrix.SetColNumber(matrix.mRow);
for (int i = 0; i < iMatrix.mRow; i++)
for (int j = 0; j < iMatrix.mCol; j++)
iMatrix(i, j) = matrix(j, i);
}
template<typename T>
Matrix<T> Matrix<T>::TranCopy(const Matrix<T>& iMatrix) {
MATRIXISNULL(iMatrix);
Matrix<T> matrix(iMatrix.mCol, iMatrix.mRow);
for (int i = 0; i < iMatrix.mRow; i++)
for (int j = 0; j < iMatrix.mCol; j++)
matrix(j, i) = iMatrix.GetElement(i, j);
return matrix;
}
template<typename T>
bool Matrix<T>::IsZeroMatrix(const Matrix<T>& iMatrix) {
MATRIXISNULL(iMatrix);
for (int i = 0; i < iMatrix.mRow; i++)
for (int j = 0; j < iMatrix.mCol; j++)
if (abs(iMatrix.GetElement(i, j)) < 0.0001)
return true;
return false;
}
MatrixUtil.h 文件
#pragma once
#ifdef _DEBUG
#ifndef ThrowIfFailed
#define ThrowIfFailed(express) \
{ \
try{ \
express; \
} \
catch (const char * errInfo){ \
std::cout << errInfo << " FunctionName:" << __FUNCTION__ << " | Line: " << __LINE__ << std::endl; \
__debugbreak(); \
} \
}
#endif // !ThrowIfFailed
#else
#ifndef ThrowIfFailed
#define ThrowIfFailed(express) express;
#endif // !ThrowIfFailed
#endif // _DEBUG
如果读者觉得还可想要这些头文件的源码的话,请移步到这个网站https://gitee.com/HonyOrange_227/matrix-compute
复制这两个文件就可以了
2、如何使用这个类模板
矩阵加减法
矩阵加法
矩阵加法是指两个同型矩阵(即行数和列数相同的矩阵)中对应位置的元素相加。例如,如果矩阵A和矩阵B都是m×n矩阵,则它们的和C = A + B定义为:
其中,表示行索引,表示列索引
矩阵减法
矩阵减法则是通过对应位置的元素相减来实现的。具体而言,如果矩阵A和矩阵B都是同型矩阵,则它们的差D = A - B定义为:
其中,表示行索引,表示列索引
在代码中的体现
#include<iostream>
#include<string>
#include"Matrix.h"
#include"MatrixUtil.h"
int main() {
int arr1[6] = { 11,23,56, 74,11,44 };
int arr2[6] = { 22,36,54, 84,100,21 };
Matrix<int> m1(arr1,2,3); //将arr1化为两行三列的矩阵
Matrix<int> m2(arr2,2,3); //将arr2化为两行三列的矩阵
ThrowIfFailed(std::cout << "m1 + m2: \n" << m1 + m2 << std::endl);
ThrowIfFailed(std::cout << "m1 - m2: \n" << m1 - m2 << std::endl);
}
运行结果
矩阵乘法
假设我们有两个矩阵A和B,其中A是一个m×n矩阵,B是一个n×p矩阵。那么它们的乘积AB将是一个m×p矩阵,其每个元素由以下公式计算得出:
其中,是结果矩阵C的第ij个元素,是矩阵A的第i行第k列元素,而是矩阵B的第k行第j列元素
在代码中的体现
#include<iostream>
#include<string>
#include"Matrix.h"
#include"MatrixUtil.h"
int main() {
int arr1[9] = { 11,23,56, 74,11,44 , 30,25,14};
int arr2[9] = { 22,36,54, 84,100,21, 39,54,44};
Matrix<int> m1(arr1,3,3); //将arr1化为三行三列的矩阵
Matrix<int> m2(arr2,3,3); //将arr2化为三行三列的矩阵
ThrowIfFailed(std::cout << "m1 * m2: \n" << m1 * m2 << std::endl);
ThrowIfFailed(std::cout << "m2 * m1: \n" << m2 * m1 << std::endl);
}
运行结果
矩阵的转置
矩阵的转置是指将一个矩阵的行和列互换,从而形成一个新的矩阵。具体来说,如果有一个 m × n 矩阵 ,那么它的转置矩阵记为 ,是一个 n × m 矩阵,其中 的第 行第 列元素等于 的第 行第 列元素
在代码中的体现
#include<iostream>
#include<string>
#include"Matrix.h"
#include"MatrixUtil.h"
int main() {
int arr1[6] = { 11,23,56, 74,11,44 };
int arr2[6] = { 22,36,54, 84,100,21};
Matrix<int> m1(arr1,2,3); //将arr1化为两行三列的矩阵
Matrix<int> m2(arr2,2,3); //将arr2化为两行三列的矩阵
ThrowIfFailed(std::cout << "m1: \n" << m1 << std::endl);
ThrowIfFailed(std::cout << "m1^T: \n" << Matrix<int>::TranCopy(m1) << std::endl);
ThrowIfFailed(std::cout << "m2: \n" << m2 << std::endl);
ThrowIfFailed(std::cout << "m2^T: \n" << Matrix<int>::TranCopy(m2) << std::endl);
}
运行结果
3、程序设计
我们知道矩阵的运算很多都是有前置条件的,比如矩阵的加法要是同规格的矩阵才能互相加减,矩阵的乘法左边矩阵的列数需要等于右边矩阵的行数。如果,出现这样的错误,我希望能够直接停止程序,但是这种直接把程序停下来的行为又只发生在调试模式中,所以有了下面的宏定义。
不满足矩阵的算法条件的错误定义
//矩阵错误信息宏定义
#ifndef MATRIXERRINFO
#ifndef MATRIXISNULL
#define MATRIXISNULL(matrix) if(matrix.mRow * matrix.mCol == 0) throw "该矩阵的函数或者列数为0"
#endif // !MATRIXISNULL
#ifndef LEFT_SPEC_NOTEQUAL_RIGTH
#define LEFT_SPEC_NOTEQUAL_RIGTH(left,right)\
if (left.mCol != right.mCol || left.mRow != right.mRow) throw "左右矩阵的行数和列数各不相"
#endif // !LEFT_SPEC_NOTEQUAL_RIGTH
#ifndef CANTMULTIPLY
#define CANTMULTIPLY(left,right) \
if(left.mCol != right.mRow) throw "左矩阵的列数和右矩阵的函数不相等"
#endif // !CANTMULTIPLY
#ifndef MATRIX_CANT_C2_VECTOR
#define MATRIX_CANT_C2_VECTOR(matrix)\
if(matrix.mCol > 1 && matrix.mRow > 1) throw "矩阵并不是一维的,不能获取长度"
#endif // !MATRIX_CANT_C2_VECTOR
#endif // !MATRIXERRINFO
当矩阵不满矩阵算法的前置条件的时候,就抛出错误。
异常捕获
#pragma once
#ifdef _DEBUG
#ifndef ThrowIfFailed
#define ThrowIfFailed(express) \
{ \
try{ \
express; \
} \
catch (const char * errInfo){ \
std::cout << errInfo << " FunctionName:" << __FUNCTION__ << " | Line: " << __LINE__ << std::endl; \
__debugbreak(); \
} \
}
#endif // !ThrowIfFailed
#else
#ifndef ThrowIfFailed
#define ThrowIfFailed(express)
#endif // !ThrowIfFailed
#endif // _DEBUG
这个异常捕获只有在DEBUG模式下才会存在,当矩阵算法抛出异常时,将会显示算法所在的函数,以及对应的函数,并且整个程序会打断。
具体演示一下
#include<iostream>
#include<string>
#include"Matrix.h"
#include"MatrixUtil.h"
int main() {
int arr1[6] = { 11,23,56, 74,11,44 };
int arr2[6] = { 22,36,54, 84,100,21};
Matrix<int> m1(arr1,2,3); //将arr1化为三行三列的矩阵
Matrix<int> m2(arr2,2,3); //将arr2化为三行三列的矩阵
ThrowIfFailed(std::cout << "m1: \n" << m1 << std::endl);
ThrowIfFailed(std::cout << "m1^T: \n" << Matrix<int>::TranCopy(m1) << std::endl);
ThrowIfFailed(std::cout << "m2: \n" << m2 << std::endl);
ThrowIfFailed(std::cout << "m2^T: \n" << Matrix<int>::TranCopy(m2) << std::endl);
ThrowIfFailed(std::cout << "m1 * m2 \n" << m1 * m2 << std::endl);
}
m1 和 m2 是不能相乘的,左矩阵的列不等于右矩阵的行。
结果如下
程序在矩阵乘法的位置中断了
4、总结
笔者的矩阵算法并不是很好,网上也有很多开源的矩阵计算项目。笔者更希望这个程序的设计能给大家带来一些启发。