基于C++的矩阵类的编写

要求

本次实验是编写矩阵类 Matrix_4x4,使类满足以下要求:

  1. 矩阵为 4*4,数据类型为 double
  2. 默认构造函数,初始化矩阵为单位阵
  3. 拷贝构造函数
  4. 带参数构造函数,可以用一个 4x4 的二维数组初始化
  5. 重载 加(+),减(-),乘(*),幂次(^) ,输入,输出 等操作
  6. 重载 = 操作,可以实现矩阵间赋值,或者二维数组向矩阵赋值
  7. 重载( ), 实现矩阵元素访问, 例如矩阵类的对象 m 可以实现 m(1, 2)=2;
  8. 实现求逆功能,转置功能,求行列式功能

知识点

  1. 构造函数
  2. 运算符重载
  3. 友元函数的使用

实现

矩阵求逆和求行列式的原理

矩阵求逆:通过高斯-若尔当消元法,通过初等行变换将矩阵 A 化为单位矩阵 E,将矩阵 E 化为 A^(-1)。对于每一行,首先判断对角方向的元素是否为 0,然后将对角位置转换为 1,接着将该列非对角位置转换为 0。最终可得矩阵 A 的逆矩阵
矩阵求逆

行列式运算:使用高斯消去法,通过对矩阵 A 的一系列行变换,使之成为
上三角形矩阵,其主对角线上诸元素乘积即为行列式之值。判断每列的最大元素,
通过交换行放到主对角线,然后根据 $A[j][k] = A[j][k] - A[i][k] * A[j][i] / A[i][i]$,将每
一列下三角转化为 0,最终可得一个上三角行列式。
求行列式

代码

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
using namespace std;
double matE[4][4]={{1,0,0,0},
{0,1,0,0},
{0,0,1,0},
{0,0,0,1}};//单位矩阵
class Matrix{
private:
double mat[4][4];
public:
Matrix(){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
mat[i][j]=0;
}
}
};//默认构造函数,零矩阵
Matrix(const Matrix &m){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
mat[i][j]=m.mat[i][j];
}
}
};//拷贝构造函数,拷贝同类对象
Matrix(double (&m)[4][4]){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
mat[i][j]=m[i][j];
}
}
};//拷贝构造函数,拷贝二维数组

Matrix operator+(const Matrix &m){
Matrix ansM;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
ansM.mat[i][j]=mat[i][j]+m.mat[i][j];
}
}
return ansM;
};//重载加法运算
Matrix operator-(const Matrix &m){
Matrix ansM;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
ansM.mat[i][j]=mat[i][j]-m.mat[i][j];
}
}
return ansM;
};//重载减法运算
Matrix operator*(const Matrix &m){
Matrix ansM;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
for(int k=0;k<4;k++){
ansM.mat[i][j]+=mat[i][k]*m.mat[k][j];
}
}
}
return ansM;
};//重载乘法运算
Matrix operator^(int n){
Matrix ansM(matE);
bool ifInverse=false;
if(n<0){
n=-n;
ifInverse=true;
}
for(int k=0;k<n;k++){
ansM=ansM*(*this);
}
if(ifInverse)ansM=ansM.Inverse();
return ansM;
};//重载幂运算
const Matrix &operator=(const Matrix &m){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
mat[i][j]=m.mat[i][j];
}
}
return *this;
};//重载赋值运算

const double &operator()(int i,int j){
if(i<0||j<0||i>=4||j>=4){
cout<<"Wrong index!"<<endl;
}
return mat[i][j];
};//重载取值运算

friend istream & operator>>(istream &is,Matrix &m);//友元,重载输入
friend ostream & operator<<(ostream &os,const Matrix &m);//友元,重载输出

void Swap(double &a,double &b){
double tmp=a;
a=b;
b=tmp;
}//交换两个double变量
void SwapRow(int a,int b){
for(int i=0;i<4;i++){
Swap(mat[a][i],mat[b][i]);
}
}//交换矩阵的两行
void LineMultiK(int a,double k){
for(int i=0;i<4;i++)mat[a][i]*=k;
}

Matrix Inverse(){
Matrix tmpM(mat);
Matrix E(matE);
//首先将tmpM转换为上三角矩阵
for(int i=0;i<4;i++){
//判断对角方向的元素是否为0
int j=i;
while(mat[i][j]==0&&j<4)j++;
if(j==4){
cout<<"this matrix has no inverse!"<<endl;
return *this;
}
else if(i!=j)tmpM.SwapRow(i,j),E.SwapRow(i,j);
//将对角位置转换为1
E.LineMultiK(i,1.0/tmpM(i,i));
tmpM.LineMultiK(i,1.0/tmpM(i,i));
//将该列非对角位置转换为0
for(int k=0;k<4;k++){
if(k==i)continue;
for(int t=i+1;t<4;t++){
tmpM.mat[k][t]-=tmpM(i,t)*tmpM(k,i);
}
for(int t=0;t<4;t++){
E.mat[k][t]-=E(i,t)*tmpM(k,i);
}
tmpM.mat[k][i]=0;
}
}
return E;
}//矩阵求逆

Matrix Transpose(){
Matrix ansM;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
ansM.mat[i][j]=mat[j][i];
}
}
return ansM;
}//矩阵转置

Matrix Determinant(){
Matrix ansM(mat);
for(int i=0;i<4;i++){
//判断每列的最大元素,通过交换行放到主对角线
int maxi=i;
for(int j=i;j<4;j++){
if(ansM(maxi,i)<ansM(j,i))maxi=j;
}
if(i!=maxi)ansM.SwapRow(i,maxi);
if(ansM.mat[i][i]==0)continue;
//根据A[j][k] = A[j][k] - A[i][k] * A[j][i] / A[i][i],将每一列下三角转化为0
for(int k=i+1;k<4;k++){
for(int t=i+1;t<4;t++){
ansM.mat[k][t]-=ansM(i,t)*ansM(k,i)/ansM(i,i);
}
ansM.mat[k][i]=0;
}
}
return ansM;
}//行列式化简
};

istream & operator>>(istream &is,Matrix &m){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
is>>m.mat[i][j];
}
}
return is;
};//重载输入流

ostream & operator<<(ostream &os,const Matrix &m){
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
os<<m.mat[i][j]<<" ";
}
os<<endl;
}
cout<<endl;
return os;
};//重载输出流
#endif // MATRIX_H

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include "matrix.h"
using namespace std;

int main()
{
Matrix a;
cin>>a;
cout<<"a="<<endl<<a;

Matrix b(a^3);
cout<<"a^3="<<endl<<b;

Matrix c=a^-2;
cout<<"a^-2="<<endl<<c;

cout<<"a^T="<<endl<<a.Transpose();
cout<<a.Determinant();

return 0;
}

运行结果

运行结果

结语

本次实验的重点在于运算符的重载、友元函数的使用和二维数组构造函数。
写这份代码的时候时间更多的是花在矩阵的求逆和求行列式上,其他倒还好。
重载输入输出流运算符时,普通的方法需要将cin与cout放在运算符的右边,不符合代码编写习惯。通过友元函数,可以自由定义cin和cout与输入对象的相对位置,改成符合编写习惯的代码。