C++建立矩阵的类与求矩阵的逆

前言

最近有一位友人的选题是关于矩阵的C++实现,因而我也得以对其中一些部分稍稍接触,其中对于矩阵的求逆的运算挺有意思的,它看上去简单明了,但实际上要想去实现它还是值得思考的。做完后,觉得还是挺有价值的,所以记录下我的过程,以供分享。

建立矩阵类

在这里,我认为对于矩阵的各项属性和方法,我认为用类描述更为方便,因为以后若不只是想求逆还想进行其他运算的话加,可以直接在类里添加,明了很多。

以下是具体实现的代码:

类的成员:

class Matrix
{
public:
    int Count;  //有关这个矩阵类的阶数的参数
    double **data;  //这个矩阵的具体内容
    Matrix(int);   //矩阵类的有参构造函数
    void Set_data();    //声明一个函数,作用是给矩阵赋初值
    void Show();   //声明一个函数,作用是打印这个矩阵内容
    ~Matrix();     //矩阵类的析构函数
    Matrix Inverse_Matrix ();//声明一个函数,作用是求该矩阵的逆矩阵
};

这里因为是有要求逆的环节,所以我默认了这个矩阵类代表的是方阵,所以我直接用一个参数"Count"来表示这是几阶方阵,然后我用了一个二级指针data来表示这个矩阵内部的各个具体数值。

有参构造函数Matrix(int)和析构函数~Matrix():

因为我们在建立一个矩阵对象的时候,我们一定要对这个矩阵的阶数进行规定,所以我在这里设置了一个有参构造函数:

Matrix(int);   //矩阵类的有参构造函数

传入的参数就可以用来规定阶数了。

下面是有参构造函数的内容:

Matrix::Matrix(int M)
{
    int Count = M;
    double **data = new double* [M]; 
    //意思就是为data这个二重指针分配了内存,内存大小为M个指针,data指向这M个指针,即这个矩阵有M行
    for (int i = 0; i < M; ++i) {
        data[i] = new double [M];    
        //意思就是给这M个指针分配了内存,内存大小为M个double类型的数据,即这个矩阵有M列
    }
}

其中分配内存的方式就可以理解为:我为data分配了M个指针的内存,同时这M个指针我又分配给他们M个double类型的内存。
这样,一个M行,M列的矩阵就建立了。

之后在析构函数中要注意要把内存释放:

Matrix::~Matrix(){
    delete data;
}

打印矩阵void Show():

这个没太多好说的,就是将矩阵打印出来

void Matrix::Show() //这个函数的功能是打印这个矩阵
{
    for (int i = 0; i < Count; ++i) {
        for (int j = 0; j < Count; ++j) {
            cout << data[i][j] << '\t';
        }
        cout << '\n';
    }
}

矩阵赋初值void Set_data():

void Matrix::Set_data()
{
    //这个要自己写了,就是把文件中的数据记录到矩阵中
}

这里是因为我的友人是利用文件输入的,所以需要他自己书写,如果就是手动输入的话,其实就是打印矩阵的逆向输入,很好实现。

实现矩阵求逆

重头戏来了,对于矩阵求逆的实现:
之前有想过利用伴随矩阵求逆,但是仔细思考发现:
虽然矩阵的行列式还是有办法求,但是伴随矩阵实在是太难了
这样的话,自然想到了利用初等变换:

矩阵的初等变换求逆:

这边援引百度知道上的概述:

我们假设给了一个A矩阵,则如何求A得逆du矩阵呢
我们知道如果PA=E1,则P矩阵是A的逆矩阵。
然而A矩阵的每一次行变换都相当于A矩阵左乘了一个初等矩阵P1,所以A的所有行变换可以看为多个初等矩阵左乘A矩阵,即P1P2P3…Pn=P,还有一个条件就是PE2=P,由此可以看出,当A和E2做相同的行变换,且A变成E1矩阵时,E2矩阵变为P矩阵,即A的逆矩阵,这里E矩阵标12是为了帮助理解区分,E1 E2都是单位矩阵。
接下来你只需要在A矩阵右边加一个单位矩阵,然后在对这个组合矩阵进行行变换,使A矩阵变为E矩阵,右边则得到了P矩阵,即A的逆矩阵。

那么具体实现呢:

Matrix Matrix::Inverse_Matrix()
{
    Matrix result(Count);   //建立一个结果矩阵,用来表示这个矩阵的逆矩阵
    //将结果矩阵变为单位阵
    for (int i = 0; i < Count; ++i) {
        for (int j = 0; j < Count; ++j) {
            if (i == j)
                result.data[i][j] = 1;
            else
                result.data[i][j] = 0;
        }
    }
    //第一次变换(矩阵化成三角阵)
    for (int k = 0; k < Count-1; ++k) {
        for (int i = k+1; i < Count; ++i) {
            double change = data[i][k]/data[k][k];
            for (int j = 0; j < Count; ++j) {
                data[i][j] -= change*data[k][j];
                result.data[i][j] -= change*data[k][j]; // 这边单位矩阵进行相同的变换
            }
        }
    }
    //第二次变换(三角阵化成对角阵)
    for (int j = Count - 1; j >= 1 ; --j) {
        for (int i = j-1; i >= 0 ; --i) {
            double change = data[i][j] / data[j][j];
            data[i][j] -= change*data[j][j];
            result.data[i][j] -= change*data[j][j];
        }
    }
    //第三次变换(对角阵化为单位阵)
    for (int l = 0; l < Count; ++l) {
        data[l][l] /= data[l][l];
        result.data[l][l] /= result.data[l][l];
    }
    //返回得到的逆矩阵
    return result;
}

我们新建立了一个新的矩阵对象result,作用是作为增广的单位阵,来参与矩阵的变换。在每一次原矩阵的变换同时,单位阵也进行的相同的变换。最终当原矩阵变为单位阵后,它也将转变为原矩阵的逆矩阵。

其中变换的代码实现方法值得我们品味,其中最为重要的就是化为三角阵的第一步,我们用到了三重循环。第一重循环的作用是确定以哪一行作为减法的底,之后的其他行就是来减这一行的。第二重循环就是确定被减的一行,在这里,我们可以确定相减的倍数,这里我用参数change来表示。最后一重循环,则是让被减行的每一个数字都执行相同的运算。最终循环下来,三角阵就形成了。这里的三重循环是矩阵求逆的最难点,把此处理解了,也就没什么很难得地方了。

一些待完善的地方

这里我还留了一些小的BUG,比方说应该还增加一个过程,判断矩阵的逆是否存在。其实这里也比较好完成,可以判断矩阵的行列式是否为零(其实一般不是直接说是否等于零,而是判断是否小于eps(可以表示的最小数))

尾声

个人认为算是比较好理解了?如果有任何不足的话欢迎评论一起探讨。如果认为有帮助的话也请点个赞吧。蟹蟹!
在这里插入图片描述

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值