C++矩阵运算1

        在很多科学计算当中,矩阵运算可以说是必不可少。在密码学,计算机图形科学里面都有使用到矩阵。笔者这里自己实现了一个矩阵运算的模板,不过讲实话的,笔者并不认为写得有多好,不过足以给刚学习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定义为:

C_{ij}=A_{ij}+B_{ij}

其中,i表示行索引,j表示列索引

矩阵减法

矩阵减法则是通过对应位置的元素相减来实现的。具体而言,如果矩阵A和矩阵B都是同型矩阵,则它们的差D = A - B定义为:

D_{ij}=A_{ij}-B_{ij}

其中,i表示行索引,j表示列索引

在代码中的体现

#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} = \sum_{k=1}^{n}a_{ik}b_{kj}
其中,C_{ij}是结果矩阵C的第ij个元素,a_{ik}是矩阵A的第i行第k列元素,而b_{kj}​是矩阵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 矩阵 A,那么它的转置矩阵记为 A^{T},是一个 n × m 矩阵,其中 A^{T} 的第 i行第 j列元素等于 A 的第 j行第 i列元素

在代码中的体现

#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、总结

        笔者的矩阵算法并不是很好,网上也有很多开源的矩阵计算项目。笔者更希望这个程序的设计能给大家带来一些启发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值