首页 > 其他分享 >高斯消元学习笔记

高斯消元学习笔记

时间:2024-05-31 16:57:02浏览次数:29  
标签:mat int 矩阵 long 学习 笔记 using 对角 高斯消

引入

高斯-约当消元法(Gauss–Jordan elimination)是求解线性方程组的经典算法,它在当代数学中有着重要的地位和价值,是线性代数课程教学的重要组成部分。

高斯消元法除了用于线性方程组求解外,还可以用于行列式计算、求矩阵的逆,以及其他计算机和工程方面。

过程

一个经典的问题,给定一个有 \(n\) 个未知数的线性方程组,求方程组的解。
例如:

\[\left\{ \begin{array}{} x_1 + 2x_2 = 4 \\ 2x_1 + 3x_2 = 5 \end{array} \right. \]

因为未知数的值在运算中也不会发生变化,可以尝试用矩阵将其转换。

定义

增广矩阵

一个矩阵,如果在矩阵右方有一个列向量或者一个列向量矩阵,那么这个矩阵就是增广矩阵。

将上方的例子改写成增广矩阵就是下图:

\[\left[ \begin{array} {c:|c} \begin{matrix} 1 & 2 \\ 2 & 3 \end{matrix} & \begin{matrix} 4\\ 5 \end{matrix} \end{array} \right] \]

可以发现最终的答案需要一个对角矩阵。

那么怎么将增广矩阵变换成对角矩阵呢。

将增广矩阵变成对角矩阵的过程

假定当前求解第 \(i\) 个未知数 \(x_i\)。

过程如下:

  1. 找到第一行 \(j\) 满足矩阵 \(mat\) 的 \(mat_{j,i} \neq 0 \land (mat_{j,j} \neq 1 \lor j \ge i)\) 。
  2. 交换第 \(i\) 行和第 \(j\) 行。
  3. 将其他行的第 \(i\) 列变成 \(0\)。
  4. 当 \(i \le n\) 时,重复以上步骤。

矩阵的秩

一个经过高斯消元的对角矩阵 \(A\) 如果满足第 \(i\) 行不是全零且第 \(i + 1\) 为全零,则矩阵的秩为 \(i\),记作 \(r(A)\)。

无解情况

因为最后的结果会是一个对角方阵且此方阵的秩为 \(n\) 且满足此矩阵 \(A\) 中 \(\exists i, A_{i,i} = 0\) 并且右方常数不为 \(0\)。

无数解情况

满足一个对角矩阵 \(A\),\(r(A) = n \land \exists i,A_{i,i} = 0\) 且其对应的常数为 \(0\)。

可以发现如果一个矩阵有唯一解满足 \(r(A) = n \land \forall i A_{i,i} \ne 0\)。

实现

一下代码为 SDOI 2006 线性方程组 的代码。

#include <cstdio>
#include <cmath>
#include <algorithm>

using u32 = unsigned int ;
using i64 = long long ;
using u64 = unsigned long long ;
using f32 = float ;
using f64 = double ;

const f64 eps = 1e-6 ;
const int N = 105 ;

int n;
f64 mat[N][N];

void Gauss(){
    for (int i = 1; i <= n; i++) {
        int p = 0;
        for (int j = 1; j <= n; j++) {
            if (fabs(mat[j][j]) > eps && j < i) {
                continue;
            }

            if (fabs(mat[j][i]) > fabs(mat[p][i])) {
                p = j;
                break;
            }
        }

        if (!p) {
            continue;
        }
        std::swap(mat[i], mat[p]);

        for (int j = 1; j <= n; j++) {
            if (i == j) {
                continue;
            }

            double val = mat[j][i] / mat[i][i];
            for (int k = 1; k <= n + 1; k++) {
                mat[j][k] -= mat[i][k] * val;
            }
        }
    }
}

int main(){
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 1; j++) {
            scanf("%lf", &mat[i][j]);
        }
    }

    Gauss();

    for (int i = 1; i <= n; i++) {
        if (fabs(mat[i][n + 1]) > eps && fabs(mat[i][i]) < eps) {
            puts("-1");
            return 0;
        }
    }
    
    for (int i = 1; i <= n; i++) {
        if (fabs(mat[i][n + 1]) < eps && fabs(mat[i][i]) < eps) {
            puts("0");
            return 0;
        }
    }

    for (int i = 1; i <= n; i++) {
        double ans = mat[i][n + 1] / mat[i][i];
        if (fabs(ans) < eps) {
            puts("0.00");
        } else {
            printf("%.2lf\n", ans);
        }
    }
    return 0;
}

时间复杂度

显然,\(\mathcal{O}(n ^ 3)\)。

其他应用

行列式求值

一个行列式如果它是对角矩阵,那么它的值为对角的乘积。

而高斯消元正好能将一个矩阵变换成对角矩阵,因此可以在 \(\mathcal{O}(n ^ 3)\) 求解。

实现

以下为 【模板】行列式求值 代码,通过辗转相减法实现了求值取模 \(p\) 的答案。

#include <cstdio>
#include <algorithm>

using i64 = long long ;

const int N = 605 ;

int n, mod;
i64 a[N][N];
i64 ans = 1;

void Gauss(){
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            while (a[i][i]) {
                int t = a[j][i] / a[i][i] ;
                
                for (int k = 1; k <= n; k++) {
                    a[j][k] = (a[j][k] - t * a[i][k] % mod + mod) % mod;
                }

                ans *= -1;//行列式的性质,交换两行,需要改变结果的符号
                std::swap(a[i], a[j]);
            }

            ans *= - 1;
            std::swap(a[i], a[j]);
        }
    }
}

int main(){
    scanf("%d %d", &n, &mod);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%lld", &a[i][j]);
        }
    }

    Gauss();

    for (int i = 1; i <= n; i++) {
        ans = (ans * a[i][i] + mod) % mod;
    }

    printf("%lld",(ans + mod) % mod);
    return 0;
}

矩阵求逆

矩阵求逆就是通过高斯消元将此矩阵消成单位矩阵,而右方可以记录矩阵的初等变换的积,最后右方就是矩阵的逆。

实现

以下为 【模板】矩阵求逆 的代码。

#include <cstdio>
#include <cstring>
#include <algorithm>

using u32 = unsigned int;
using i64 = long long ;
using u64 = unsigned long long ;

const int mod = 1e9 + 7 ;
const int N = 405 ;

int n;
int mat[N][N << 1];

i64 qpow(i64 a, i64 b = mod - 2) {
    i64 ans = 1;
    
    for (; b; b >>= 1, a = a * a % mod) {
        if (b & 1) {
            ans = ans * a % mod;
        }
    }

    return ans;
}

int Gauss(){
    for (int i = 1; i <= n; i++) {
        int p = 0;
        for (int j = 1; j <= n; j++) {
            if (j < i && mat[j][j]) {
                continue;
            }
            if (mat[j][i]) {
                p = j;
                break;
            }
        }

        if (!p) {
            return -1;//如果方程组无解,自然就没有
        }
        std::swap(mat[i], mat[p]);

        i64 inv = qpow(mat[i][i]);
        for (int j = 1; j <= n; j++) {
            if (i == j) {
                continue;
            }

            i64 val = mat[j][i] * inv % mod;
            for (int k = 1; k <= (n << 1); k++) {
                mat[j][k] = (mat[j][k] - val * mat[i][k] % mod + mod) % mod;
            }
        }

        for (int j = 1; j <= (n << 1); j++) {
            mat[i][j] = (mat[i][j] * inv) % mod;
        }
    }

    return 1;
}

int main(){
    scanf("%d", &n);

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &mat[i][j]);
            mat[i][n + i] = 1;
        }
    }

    if (Gauss() == -1) {
        printf("No Solution");
        return 0;
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            printf("%d ", mat[i][n + j]);
        }
        puts("");
    }
    return 0;
}

求解模意义下的方程组

很简单,将除法改成逆元就行了。

特殊的,二进制下可以用异或操作代替逆元。

标签:mat,int,矩阵,long,学习,笔记,using,对角,高斯消
From: https://www.cnblogs.com/zdrj/p/18224845

相关文章

  • 行列式 学习笔记
    引入行列式是方阵的一个运算,对于方阵\(A\),它的行列式记作\(\text{det}A\)也记作\(|A|\)。定义全排列定义记\(\pi(p_1,p_2,\cdots,p_n)\)是排列\(p_1,p_2,\cdots,p_n\)的逆序对数量。\[\text{det}A=\left[ \begin{array}{} a_{1,1}&a_{1,2}&\cdots&a_......
  • 【二】从小白开始使用Python一步一步搭建一个深度学习UI界面【界面设计】
    本来是想使用QtDesigner进行界面控件拖拽的方式进行界面设计的,但是后来觉得这样后面维护更新起来太麻烦了,就还是使用纯代码来写界面吧,这需要一定的想象能力。设计界面pyqt外部工具添加在设置界面搜索“外部工具”,这里我已经添加了两个QTDesigner的外部工具,一个是用于创......
  • 【python深度学习】——大型工程项目管理以及互相导入
    【python深度学习】——大型工程项目管理以及互相导入1.工程项目中常见的文件组织形式2.python中的“包”、“模块”、与__init__.py2.1概念理解2.2\__init__py的使用3.包的导入——相对导入与绝对导入3.1相对导入3.1.1相对导入的语法3.1.2相对......
  • 【风控】可解释机器学习之InterpretML
    【风控】可解释机器学习之InterpretML在金融风控领域,机器学习模型因其强大的预测能力而备受青睐。然而,随着模型复杂性的增加,模型的可解释性逐渐成为一个挑战。监管要求、业务逻辑的透明度以及对模型决策的信任度,都迫切需要我们能够清晰地解释模型的每一个预测。这就是Inter......
  • Java学习-Sentinel 1.8.4 规则持久化到Nacos
    文章目录一、前言二、快速体验1、部署sentinel2、SpringCloud中规则持久化到nacos3、sentinel控制台操作测试三、sentinel-dashboard源码修改1、`pom.xml`中添加依赖2、`application.properties`中添加nacos配置3、nacos配置新增NacosConfig新增NacosConfigUtil4、举......
  • 安装、学习protobuf
    Protobuf是什么?类似于json的一种数据格式,独立于语言,而且是二进制方式,所以比json更快,而且还可以直接存储一些图、树序列化和反序列化持久化(存到磁盘硬盘)领域中,数据存到磁盘叫序列化,从磁盘读取出来叫反序列化网络传输领域中,数据块转字符串叫序列化,对端把字符串解析为数据块......
  • 521源码-免费手游下载-【烽火中原H5】深度体验:横版网页国战手游及WIN学习手工端
    【烽火中原H5】深度体验:横版网页国战手游及WIN学习手工端全面解析,烽火中原H5】横板网页国战手游+WIN学习手工端+语音视频教程+营运后台+CDK授权后台,喜欢国战手游的玩家们,你们期待已久的【烽火中原H5】现已上线!这款游戏以横版网页的形式呈现,为玩家带来沉浸式的国战体验。同时......
  • 2024年网络安全学习指南!详尽路线图,从零基础到黑客高手的进阶之路!
    零基础小白,到就业!入门到入土的网安/黑客学习路线!建议的学习顺序:一、网络安全学习普法(心里有个数,要进去坐几年!)1、了解并介绍《网络安全法》2、《全国人大常委会关于维护互联网安全的决定》3、《中华人民共和国计算机信息系统安全保护条例(2011年修正)》4、《中华人民共......
  • 【Java笔记】第八章:面向对象的三大特性[封装、继承、多态]
    一、封装1.目前程序存在的问题:程序没有进行数据安全检测,可能出现业务逻辑问题2.private:私有的,被private修饰的内容,只能在本类中使用3.给私有化的属性提供公开的get和set方法(1)set方法:为属性赋值   publicvoidset属性名(数据类型变量名){      ......
  • MIMO-OFDM无线通信技术与matlab实现 阅读笔记与勘误
    第四章OFDM技术1.ZP保持子载波正交性原理:在每个符号前,添加了ZP(zeropad/zeroprefix)。收到的信号为多径信号,多径分量的最大延迟τ<ZP宽度。在收到信号后,假定定时准确,也就是图中FFT窗所示。将下一个符号的ZP部分搬移到当前符号起始位置,也就是将图中长竖线之后的部分搬移到F......