[C++程序] 矩阵类及其常规运算(加、减、乘、转置、求逆、行列式、代数余子式、伴随矩阵)

写在前面

本程序中矩阵类的定义为师长给予(两个SetData函数除外),在此不作详细说明。其中涉及的对新旧数据的处理、对函数的封装与保护、对指针的运用及其他理念,由读者自行体会。外框打印只有在极个别情况下才会实现完美对齐,目前还没有找到普适方法。

代码实现

#include <iostream>
#include <iomanip>
#include <cstring>
#include <cmath>
using namespace std;

#define ull unsigned long long

class Matrix
{
	double* pData;
	int rowCount;
	int colCount;

public:
	Matrix(int rows, int cols) :
		pData(nullptr), rowCount(0), colCount(0)
	{
		CreateData(rows, cols);
		ClearData();
	}

	Matrix(int rows, int cols, double value) :
		pData(nullptr), rowCount(0), colCount(0)
	{
		CreateData(rows, cols);
		ClearData(value);
	}

	Matrix(int rows, int cols, const double* initArr, int initArrSize) :
		pData(nullptr), rowCount(0), colCount(0)
	{
		CreateData(rows, cols);
		CopyDataFrom(initArr, initArrSize);
	}

	~Matrix()
	{
		if (pData) delete[]pData;
	}

	Matrix(const Matrix& m) :
		pData(nullptr), rowCount(0), colCount(0)
	{
		CreateData(m.rowCount, m.colCount);
		CopyDataFrom(m.pData, m.rowCount * m.colCount);
	}

	Matrix& operator =(const Matrix& m)
	{
		CreateData(m.rowCount, m.colCount);
		CopyDataFrom(m.pData, m.rowCount * m.colCount);

		return *this;
	}

	double GetData(int rowIndex, int colIndex) const
	{
		return pData[rowIndex * colCount + colIndex];
	}

	double* SetData() const
	{
		return pData;
	}

private:
	void CreateData(int rows, int cols)
	{
		if (rows <= 0) rows = 1;
		if (cols <= 0) cols = 1;

		double* pOldData = nullptr;
		if (pData)
			if (rowCount * colCount != rows * cols)
				pOldData = pData;

		pData = new double[rows * cols];
		this->rowCount = rows;
		this->colCount = cols;
	}

	void CopyDataFrom(const double* newData, int newDataSize, bool clearOther = true)
	{
		double* pTarget = pData;
		const double* pSource = newData;

		double* pTargetEnd1 = pData + rowCount * colCount;
		double* pTargetEnd2 = pData + newDataSize;

		double* pTargetEnd = (pTargetEnd1 < pTargetEnd2) ? pTargetEnd1 : pTargetEnd2;

		while (pTarget < pTargetEnd)
			*(pTarget++) = *(pSource++);

		/*   for (; pTarget < pTargetEnd; ++pTarget, ++pSource)
			   *pTarget = *pSource;

		   for (int i = 0; i < newDataSize && i < rowCount * colCount; ++i)
			   pData[i] = newData[i];*/ //👈上方循环的另外两种写法。

		if (clearOther)
			while (pTarget < pTargetEnd1)
				*pTarget++ = 0;
	}

	void ClearData(double value = 0)
	{
		double* pTarget = pData;
		double* pTargetEnd = pData + rowCount * colCount;

		while (pTarget < pTargetEnd)
			*(pTarget++) = value;
	}

	int GetRowCount() const { return rowCount; }
	int GetColCount() const { return colCount; }
	friend ostream& operator << (ostream& out, const Matrix& m);
	friend int GetRowCount(const Matrix& m);
	friend int GetColCount(const Matrix& m);
	friend double* SetData(Matrix& m, const int rowIndex, const int colIndex);
}; //矩阵类

int GetRowCount(const Matrix& m)
{
	return m.rowCount;
}

int GetColCount(const Matrix& m)
{
	return m.colCount;
}

//返回矩阵指向特定行列的指针
double* SetData(Matrix& m, const int rowIndex, const int colIndex) 
{
	double* p = m.pData;
	p += rowIndex * m.colCount + colIndex;
	return p;
}

//输出流重载(使其能够打印矩阵)👇
ostream& operator << (ostream& out, const Matrix& m) 
{
	int x = 0, y = 0, i = 0; 
	ull blank = 0; ull numsize = 0;
	double temp = 0, edge = 0;

	blank = 9 * (ull)m.colCount - 1;
	out << "┏" << setw(blank) << "┓" << endl;
	for (y = 0; y < m.rowCount; ++y)
	{
		out << "┃";
		
		for (x = 0; x < m.colCount; ++x)
		{
			temp = m.GetData(y, x);  //存储本次要打印的值
			if (temp < 1e-10) temp = 0; //temp过小直接设为0
			edge = fabs(temp - (long)temp); //判断是否为整数

			if (temp != 0 && edge < 1e-4) edge = 0; //不为整数,将edge设为0,将其当作整数
			else if (edge >= 1e-4) edge = 1; //将其当作小数

			numsize = (ull)log10(fabs(temp)) + 1;
			if (temp < 0) numsize += 1;
			if (fabs(temp) < 1) numsize = 0;

			if (numsize == 0 && edge == 1) //处理小于1且带小数的情况。
				out << setprecision(5) << setw(8) << temp;
			else if (numsize == 1)
			{
				if (edge == 1)
					out << setprecision(5) << setw(8) << temp;
				else
					out << setw(5) << temp << setw(3) << "";
			}
			else 
				out << setw(5) << temp << setw(3) << "";

			if (x == m.colCount - 1) out << "┃";
		}
		out << endl;
		out << (y == m.rowCount - 1 ? "┗" : "");
		if (y == m.rowCount - 1) out << setw(blank);
		out << (y == m.rowCount - 1 ? "┛" : "");}
	
	return out;
}

//矩阵转置👇
Matrix operator ~ (const Matrix& m) 
{
	Matrix mT(GetColCount(m), GetRowCount(m));
	double* p = mT.SetData();
	for (int row = 0; row < GetRowCount(mT); ++row)
	{
		for (int col = 0; col < GetColCount(mT); ++col)
			*p++ = m.GetData(col, row);
	}

	return mT;
}

//矩阵乘法👇
Matrix operator * (const Matrix& m1, const Matrix& m2) 
{
	Matrix mX(GetRowCount(m1), GetColCount(m2));
	Matrix ErrormX(1, 1, 0);
	if (GetColCount(m1) != GetRowCount(m2))
	{
		cout << "两矩阵行列不对应,无法相乘。返回错误值:1阶0矩阵。" << endl;
		return ErrormX;
	}

	int t1 = GetColCount(m1), t2 = GetColCount(m2), t3 = GetRowCount(m1);
	int i = 0, x = 0, y = 0;

	double* pTarget = mX.SetData();

	while (x < t3) //t3为m1的行数
	{
		while (y < t2) //t2为m2的列数
		{
			while (i < t1) //t1为m1的列数(也是m2的行数),对每一个y(即m2的每一列),循环t1次计算出mX当前位置的值,然后y+1
			{
				*pTarget += m1.GetData(x, i) * m2.GetData(i, y);
				i++;
			}
			i = 0; y++; pTarget++;
		}
		y = 0; x++;
	}

	return mX;
}

//矩阵加法👇
Matrix operator + (const Matrix& m1, const Matrix& m2)
{
	Matrix mAdd(GetRowCount(m1), GetColCount(m1));
	Matrix m_Error(1, 1, 0);
	double* p = mAdd.SetData();

	if (GetRowCount(m1) != GetRowCount(m2) ||
		GetColCount(m1) != GetColCount(m2))
	{
		cout << "两矩阵行列不完全相同,无法相加!返回错误值:1阶0矩阵。";
		return m_Error;
	}

	for (int row = 0; row < GetRowCount(m1); row++)
	{
		for (int col = 0; col < GetColCount(m1); col++)
			*p++ = m1.GetData(row, col) + m2.GetData(row, col);
	}

	return mAdd;
}

//矩阵减法
Matrix operator - (const Matrix& m1, const Matrix& m2)
{
	Matrix mMinus(GetRowCount(m1), GetColCount(m1));
	Matrix m_Error(1, 1, 0);
	double* p = mMinus.SetData();

	if (GetRowCount(m1) != GetRowCount(m2) ||
		GetColCount(m1) != GetColCount(m2))
	{
		cout << "两矩阵行列不完全相同,无法相减!返回错误值:1阶0矩阵。";
		return m_Error;
	}

	for (int row = 0; row < GetRowCount(m1); row++)
	{
		for (int col = 0; col < GetColCount(m1); col++)
			*p++ = m1.GetData(row, col) - m2.GetData(row, col);
	}

	return mMinus;
}


//交换函数,用于进行两行交换
inline void swap(Matrix& mdet, int r1, int r2)
{
	int t = GetColCount(mdet);
	for (int i = 0; i < t; i++)
		swap(*SetData(mdet, r1, i), *SetData(mdet, r2, i));
}
//乘积函数,用于对矩阵指定行实行数乘
inline void Matrix_Multiline(Matrix& m, double k, int R)
{
	int C = GetColCount(m);
	for (int i = 0; i < C; i++)
		*SetData(m, R, i) *= k;
}

// 高斯消元求行列式
double fabs(Matrix& m)
{
	Matrix mDen = m;
	int row = GetRowCount(m), col = GetColCount(m);
	if (row != col) //判断是否能求行列式。
	{
		cout << "该矩阵不是方阵,无法求行列式. 返回错误值0." << endl;
		cout << "◢";
		return 0;
	}

	if (row == 2)
		return m.GetData(0, 0) * m.GetData(1, 1) - m.GetData(0, 1) * m.GetData(1, 0);
	
	/*
	参数说明:
	1. k    : 存储最终计算结果。
	2. temp : 当目前循环至t列时,存储t+1行位于主对角线下方的数值。
	3. i,j  : 控制循环需要。
	4. count: 判断首列是否全为0.
	5. t    : 控制循环沿对角线方向下降。
	*/
	double k = 1, temp = 0; int i = 0, j = 0, count = 0, t = 0;

	for (i = 0; i < row; i++)
	{
		if (mDen.GetData(i, 0) == 0)
			count += 1;
	}
	if (count == row) return 0;

	for (t = 0; t < col; t++)//从首列开始,将矩阵变为上三角。沿对角线方向下降,下文将该列主对角线上的元素称为该列首项。
	{
		// 将该列首项变为1. 👇
		if (mDen.GetData(t, t) == 1) {} //若该列首项为1,不做任何事。
		else
		{
			for (i = t; i < row; i++) //从该列首项开始。
			{
				if (mDen.GetData(i, t) == 1) //如果该列i行为1,将其与该列首项所在行对换,跳出行循环
				{
					if (i != t)
					{
						swap(mDen, i, t); //执行对换。
						k = (i == t ? k : -k); //行对换,符号改变。
					}
					goto Deal; //跳转至处理函数。
				}
				else
				{
					if (mDen.GetData(i, t) == 0) //遇到0,进入下一行
						continue;
					else //如果该列i行不为0且不为1,用k存储该行首项的值,并将该行首项变为1,之后将该行与该列首项所在行对换。
					{
						temp = mDen.GetData(i, t); //存储该行首项的值。
						k *= temp; 
						//cout << "k = " << k << endl << endl;
						Matrix_Multiline(mDen, (1 / temp), i); //将该行首项变为1.
						if (i != t)
						{
							swap(mDen, i, t); //交换该行与该列首项所在行。
							k = -k; //改变符号。
						}
						goto Deal; //跳转至处理函数。
					}
				}
			}
			if (t == 0 && count == row) return 0; // 首列全为0,返回0。
		}

	Deal: //将该列除首项外所有元素变为0.👇
		for (i = t + 1; i < row; i++) //从该列首项所在下一行开始。
		{
			temp = mDen.GetData(i, t); //存储该行首项的值。
			if (temp != 0) //如果不为0
			{
				for (j = t; j < col; j++) //从该行主对角线下项开始,每一项都减去将该列首项(即1)所在的行乘上temp。将该行首项变为0.
				{
					//cout << "第" << i + 1 << "行" << j + 1 << "列的元素为" << mDen.GetData(i, j) << endl; 👈调试用语句
					*SetData(mDen, i, j) -= temp * *SetData(mDen, t, j);
					//cout << "第" << i + 1 << "行" << j + 1 << "列的元素为" << mDen.GetData(i, j) << endl; 👈调试用语句
					//cout << "k = " << k << endl; 👈调试用语句
				}
			}
		}
	}

	return k;
}

// 提取余子式对应矩阵
Matrix alogcom(Matrix& m, const int rowIndex, const int colIndex)
{
	int row = GetRowCount(m), col = GetColCount(m);
	int x = 0, y = 0, i = 0, j = 0;
	Matrix mC(row - 1, col - 1);

	for (y = 0; y < row; y++)
	{
		for (x = 0; x < col; x++)
		{
			if (y != rowIndex && x != colIndex)
			{
				*SetData(mC, i, j++) = *SetData(m, y, x);
				if (j == row - 1)
				{
					j = 0; i++;
					break;
				}
			}
		}
	}
	//cout << mC << endl << endl; 👈调试用语句
	return mC;
}
// 计算伴随矩阵
Matrix adjoint(Matrix& m)
{
	int row = GetRowCount(m), col = GetColCount(m);
	short sign = -1;
	Matrix mCom(row, col);
	Matrix mTemp(row, col);
	for (int y = 0; y < row; y++)
	{
		for (int x = 0; x < col; x++)
		{
			mTemp = alogcom(m, x, y);
			//cout << "A" << x + 1 << y + 1 << " = " << fabs(mTemp) << endl; 👈调试用语句
			*SetData(mCom, y, x) = pow(sign, x + y + 2) * fabs(mTemp);
			//cout << mCom << endl; 👈调试用语句
		}
	}

	//cout << mCom << endl; 👈调试用语句
	return mCom;
}
// 求逆矩阵
Matrix operator !(Matrix& m)
{
	int row = GetRowCount(m), col = GetColCount(m);
	Matrix m_(row, col);
	Matrix mCom = adjoint(m);
	Matrix Errorm(1, 1, 0);
	double Den = fabs(m);
	//cout << Den << endl; 👈调试用语句
	if (row != col || Den == 0)
	{
		cout << "该矩阵不可逆。返回错误值:1阶0矩阵。" << endl;
		return Errorm;
	}

	for (int y = 0; y < row; y++)
	{
		for (int x = 0; x < col; x++)
		{
			*SetData(m_, y, x) = mCom.GetData(y, x) / Den;
			//cout << m_ << endl; 👈调试用语句
		}
	}


	return m_;
}

int main()
{
	double arr[15] = { 1, 2, 3, 4, 5, 6, 1, 3, 4, 2, 3, 1, 5, 0.1, -0.5 };
	double arr_[9] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
	Matrix m1(3, 3, arr, 9);
	Matrix m2 = ~m1;
	Matrix m3 = m1 * ~m1;
	Matrix m4(3, 3, arr_, 9);
	Matrix m5 = !m1;
	Matrix m6 = m1 * !m1;
	Matrix m7 = adjoint(m1);

	cout << "m1 = " << endl << m1 << endl << endl;
	cout << "m1^T(转置) = " << endl << m2 << endl << endl;
	cout << "m1 * m1^T = " << endl << m3 << endl << endl;
	cout << "m1^*(伴随) = " << endl << m7 << endl << endl;
	cout << "m1 * m1^* = "  << endl << m1 * m7 << endl << endl;
	cout << "|m1|(行列式) = " << fabs(m1) << endl << endl;
	cout << "m1^-1(逆矩阵) = " << endl << m5 << endl << endl;
	cout << "m1 * m1^-1 = " << endl << m6 << endl << endl;
	cout << "m4 = " << endl << m4 << endl << endl;
	cout << "|m4| = " << fabs(m4) << endl << endl;
	cout << "m1 + m4 = " << endl << m1 + m4 << endl << endl;
	cout << "m1 - m4 = " << endl << m1 - m4 << endl << endl;

	system("pause");
	return 0;
}

运行结果

在这里插入图片描述

写在后面

矩阵的其他运算,等高等代数学明白后会补上😥

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值