想了想还是单开了一篇,数学王子值得!

专栏指路:《再来一遍一定记住的算法(那些你可能忘记了的算法)》

前置知识:

矩阵:数的集合,一般是方程的系数。

题面:

洛谷P3389 【模板】高斯消元法

说了那么多,其实就是想你解方程组。

1.模拟流程

假设我们有方程:

\left\{\begin{matrix} 2x+y-z=8\\ -3x-y+2z=-11\\ -2x+y+2z=-3 \end{matrix}\right.

step 1:写出增广矩阵

\begin{pmatrix} 2 & 1 & -1 & 8\\ -3 & -1 & 2 & -11\\ -2 & 1 & 2 & -3 \end{pmatrix}

(就是把所有数字按照位置写下来啦!)

step 2:定义 r 为将要处理的行,遍历每一列,

再遍历 r 以下的行,找到第一个非零元素

代码这么写:

int r = 1;for (int c = 1;c <= n; c++) {  for (int i = r + 1; i <= n; i++) {if ( fabs(a[i][c]) > eps ) {

(fabs 就是专属浮点数的绝对值,eps 是浮点数比较的精度阈值,一般为极小值,eps 约等于 0)

接下来我们要做的就是用 a[i] 行乘以一个倍数,去和 a[r] 行相减,相减完 a[r][c] 就为 0。

double bs = a[r][c] / a[i][c];for (int j = 1; j <= n + 1; j++) {a[r][j] = a[r][j] - a[i][j] * bs;}

比如这个:\begin{pmatrix} 2 & 1 & -1 & 8\\ -3 & -1 & 2 & -11\\ -2 & 1 & 2 & -3 \end{pmatrix}

第一次循环的 a[i][c] = -3,a[r][c] = 2,bs = -\frac{2}{3}

整行相减完就变成这样:

\begin{pmatrix} 0 & \frac{1}{3} & -\frac{7}{3} & \frac{2}{3}\\ -3 & -1 & 2 & -11\\ -2 & 1 & 2 & -3 \end{pmatrix}

然后交换 i 行和 r 行:

swap(a[r], a[i]);

\begin{pmatrix} -3 & -1 & 2 & -11\\0 & \frac{1}{3} & -\frac{7}{3} & \frac{2}{3} \\ -2 & 1 & 2 & -3 \end{pmatrix}

继续寻找 c 列下一个不为 0 的数。

重复以上操作,直到 c 列只有 r 行是不为 0 的

然后 r++,c++,再次循环。

(顺便一提,每一行第一个不为 0 的数也叫主元,其他的叫自由元

   所以最后的 r - 1 也是主元的个数)

这段的代码:

int r = 1;for (int c = 1;c <= n; c++) {  for (int i = r + 1; i <= n; i++) {while ( fabs(a[i][c]) > eps ) { double bs = a[r][c] / a[i][c];for (int j = 1; j <= n + 1; j++) {a[r][j] = a[r][j] - a[i][j] * bs;}swap(a[r], a[i]);}}if (fabs(a[r][c]) > eps) {r++;}}

我懒得全部写了,反正最后消完是这样的:

\begin{pmatrix} 1 & 0.5 & -0.5 & 4\\ 0 & 1 & 0.5 & 2.5\\ 0 & 0 & 1 & -1 \end{pmatrix}

呵呵,最下面那行可以直接求出一个变量的值,再反带回去消元就好。

代码:

for (int i = n; i >= 1; i--) {for (int j = i + 1; j <= n; j++) {a[i][n + 1] -= x[j] * a[i][j];}x[i] = a[i][n + 1] / a[i][i];if ( fabs(x[i]) < eps ) {x[i] = fabs(x[i]);  // 处理浮点误差:将接近 0的值设为 0(避免输出 -0.00)}}

2.洛谷 P3389 整体代码

难点都讲完了,还有个判多解和无解的,我写代码注释里了:

无解:

\begin{pmatrix} 1 & 0.5 & -0.5 & 4\\ 0 & 1 & 0.5 & 2.5\\ 0 & 0 & 0 & -1 \end{pmatrix}

消完长这样,有行系数全为 0,但最后的常数不为 0。

多解:

\begin{pmatrix} 1 & 0.5 & -0.5 & 4\\ 0 & 1 & 0.5 & 2.5\\ 0 & 0 & 0 & 0 \end{pmatrix}

消完长这样,有行数全为 0。

就相当于那行不存在了,整个就是个不定方程组

代码:

#include<bits/stdc++.h>
using namespace std;const double eps = 1e-8;
int n;
double a[110][110], x[110];void gauss() {int r = 1;for (int c = 1;c <= n; c++) {  for (int i = r + 1; i <= n; i++) {while ( fabs(a[i][c]) > eps ) { double bs = a[r][c] / a[i][c];for (int j = 1; j <= n + 1; j++) {a[r][j] = a[r][j] - a[i][j] * bs;}swap(a[r], a[i]);}}if (fabs(a[r][c]) > eps) {r++;}}//此时找到了 r-1 个主元 //无解 for (int i = r; i <= n; i++) if( fabs(a[i][n + 1]) > eps ) {  cout << "No Solution" << "\n";return ;}//多解 if (r <= n) {cout << "No Solution" << "\n";return ;}for (int i = n; i >= 1; i--) {for (int j = i + 1; j <= n; j++) {a[i][n + 1] -= x[j] * a[i][j];}x[i] = a[i][n + 1] / a[i][i];if ( fabs(x[i]) < eps ) {x[i] = fabs(x[i]);  // 处理浮点误差:将接近 0的值设为 0(避免输出 -0.00)}}for (int i = 1; i <= n; i++) {cout << fixed << setprecision(2) << x[i] << "\n";}
}int main() {ios::sync_with_stdio(false);cin.tie(0);cin >> n;for (int i = 1; i <= n; i++) {for (int j = 1; j <= n + 1; j++) {cin >> a[i][j];}}gauss();return 0;
}

3.行列式

如果你只是想知道怎么解方程,那你可以退出了。

现在我们来讲讲高斯消元的其他妙用——

求行列式

前置知识:

逆序对定义:自行百度

知道求和符号 \sum​ 是啥

 行列式:矩阵的值。例:矩阵 A​ 的行列式写作 |A|​ 或 det(A)​。

像这个矩阵:

A=\begin{vmatrix} a_{11} & a_{12} & ... & a_{1n}\\ a_{21} & a_{22} & ... & a_{2n}\\ ... & ... & ... & ...\\ a_{n1} & a_{n2} & ... & a_{nn} \end{vmatrix}

它的行列式 D​ 的公式长这样:

D=\sum_{k}^{}(-1)^ka_{1k_1}a_{2k_2}...a_{nk_n}

别慌,我来解释下:

其中 k1,k2,...,kn​ 是将序列 1,2,...,n​ 的元素交换 k​ 次得到的一个序列。

(由于每次交换都会产生一个逆序对,所以也可以理解为 k​ 是序列 k1,k2,...,kn​ 的逆序对数

举个例子,这是一个 3*3 的矩阵:

A=\begin{vmatrix} a_{11} & a_{12} & a_{13}\\ a_{21} & a_{22} & a_{23}\\ a_{31} & a_{32} & a_{33} \end{vmatrix}

我们想算它的行列式,那首先要写出 1,2,3​ 的全排列:

1,2,3​ :逆序对数 k=0​,前面的系数为 (-1)^k=(-1)^0=1

1,3,2​ :逆序对数 k=1​,前面的系数为 (-1)^k=(-1)^1=-1

2,1,3​ :逆序对数 k=1​,前面的系数为 (-1)^k=(-1)^1=-1

2,3,1​ :逆序对数 k=2​,前面的系数为 (-1)^k=(-1)^2=1

3,1,2​ :逆序对数 k=2​,前面的系数为 (-1)^k=(-1)^2=1

3,2,1​ :逆序对数 k=3​,前面的系数为 (-1)^k=(-1)^3=-1

把系数代入 D=\sum_{k}^{}(-1)^ka_{1k_1}a_{2k_2}...a_{nk_n}​:

D=a_{11}a_{22}a_{33}\,-\,a_{11}a_{23}a_{32}\,-\,a_{12}a_{21}a_{33}\,+\,a_{12}a_{23}a_{31}\,+\,a_{13}a_{21}a_{32}\,-\,a_{13}a_{22}a_{31}

这就是三阶行列式

还有,有些教程会把行列式写成这样:

det(A)=\sum_{\sigma \in S_n}^{}sgn(\sigma )a_{1\sigma (1)}a_{2\sigma (2)}...a_{n\sigma (n)}

其中 S_n 是n个元素的对称群(所有排列的集合),

\sigma 是群里的元素(单个排列),

sign(\sigma ) 是排列 \sigma 的系数(偶排列为 1,奇排列为 -1,奇偶排列定义和前文逆序对个数一样)。

讲了这么多,高斯消元到底能起到什么作用呢??

4.理论支持

前面说过:

矩阵:数的集合,一般是方程的系数。

所以方程的一些操作放在矩阵上也是合理的,just like:

方程组操作矩阵操作目的
方程交换位置行交换简化消元顺序
方程乘以非零常数行缩放归一化主元(把主元变成 1)
方程相减消元行替换化为行阶梯型/简化行阶梯型

所以之前的变换套在矩阵上也是合理的,高斯消元后的矩阵变成了一个上三角矩阵

\begin{pmatrix} 1 & 0.5 & -0.5 & 4\\ 0 & 1 & 0.5 & 2.5\\ 0 & 0 & 1 & -1 \end{pmatrix}

最重要的一点:(上)三角矩阵的行列式等于主对角线元素的乘积!

证明下:

行列式:det(A)=\sum_{\sigma \in S_n}^{}sgn(\sigma )a_{1\sigma (1)}a_{2\sigma (2)}...a_{n\sigma (n)}

对于上三角矩阵,若存在 i>\sigma (i)(位于主对角线下方),则 a_{i\sigma (i)} = 0

要使乘积 a_{1\sigma (1)}a_{2\sigma (2)}...a_{n\sigma (n)},必须满足 \sigma (i)\geq i 对所有 i 成立。

由于 \sigma 是排列,唯一满足条件的排列是恒等排列(即 \sigma (i)= i)。

因此,行列式中唯一非零的项,是主对角线元素的乘积 a_{11}a_{22}...a_{nn},且其系数为 1

所以,高斯消元能快速求矩阵的行列式

EXTRA.大整数代码实现

放一个大整数的高斯消元求行列式,想要浮点的按照上面求解方程组的改下就好。

还有些细节写代码注释里了:

void gauss() {//主要做的事情就是辗转相减两行,直到该列除特殊位外都为 0 int r = 1; ans = 1; //r记录特殊位的行(一般情况下 r=c for (int c = 1; c <= n; c++) { //枚举列 for (int i = r + 1; i <= n; i++) { //特殊位下面的行 while (K[i][c]) {    //这里可不能改 if __int128 bs = K[r][c] / K[i][c]; //用i行的消r行 for (int j = 1; j <= n; j++) K[r][j] -= K[i][j] * bs;swap(K[r], K[i]); //交换 ans *= -1;//不严格证明下://设 i行值为 a,r行值为 b//第一步操作:i:a+b r:b//第二步操作:i:a+b r:b-(a+b)//第三步操作:i:a+b+(-a) r:-a//将 r的 -a的符号提出来,整个行列式就乘了 -1 }}if (K[r][c] != 0) {r++; //判断多解用的,一般都执行 }}for (int i = 1; i <= n; i++) ans *= K[i][i];//高斯消元完整个矩阵就变成上三角矩阵(主对角行列式),直接乘对角即可 qwrite(ans);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95226.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95226.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/95226.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GEM5学习(3):如何快速创建一个组件

通过一个图并行计算的测试用例&#xff0c;来学习如何快速构建一个目标组件 其核心思想是通过继承现有组件再拓展自定义参数 创建脚本 如何创建脚本&#xff0c;具体还可以看官方说明&#xff1a;gem5: Adding cache to configuration script mkdir configs/tutorial/part1/…

数据血缘中的图数据库如何选择

Neo4j 和 ArangoDB 都是非常优秀的图数据库&#xff0c;但它们的设计哲学、核心架构和适用场景有显著的区别。 简单来说&#xff0c;核心区别在于&#xff1a; Neo4j 是原生图数据库&#xff0c;专为处理图数据和图查询而设计和优化。ArangoDB 是多模型数据库&#xff0c;同时支…

第27章学习笔记|学无止境:从“会用命令”到“会做工具”的进阶路线

第27章学习笔记|学无止境:从“会用命令”到“会做工具”的进阶路线 你已经能用 PowerShell 解决很多日常问题了。接下来最重要的,就是把零散命令升级为可复用的工具,并在真实场景中不断打磨。 一、为什么下一步是“工具化(Toolmaking)” 当任务开始“重复、多人用、可移…

C++编程语言:标准库:第37章——正则表达式(Bjarne Stroustrup)

第 37章 正则表达式(Regular Expressions) 目录 37.1 正则表达式(规范表达式)(Regular Expressions) 37.1.1 正则表达式相关符号(Regular Express Notation) 37.2 regex 37.2.1 匹配结果(Match Results) 37.2.2 格式化(Formatting) 37.3 正则表达式函数 37.3.1 …

sciml包scixgboost函数发布,轻松完成机器学习xgboost分析

Xgboost是Boosting算法的其中一种&#xff0c;Boosting算法的思想是将许多弱分类器集成在一起&#xff0c;形成一个强分类器。因为Xgboost是一种提升树模型&#xff0c;所以它是将许多树模型集成在一起&#xff0c;形成一个很强的分类器。 我目前整合了多个R包&#xff0c;编写…

Ubuntu中配置JMmeter工具

1、检查是否已安装Java 环境java -version若未安装&#xff0c;执行以下命令安装 OpenJDKsudo apt update sudo apt install openjdk-11-jdk # 或 openjdk-17-jdk2、用wget直接下载JMeter压缩包wget https://dlcdn.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz将下载的…

LeetCode 925.长按键入

你的朋友正在使用键盘输入他的名字 name。偶尔&#xff0c;在键入字符 c 时&#xff0c;按键可能会被长按&#xff0c;而字符可能被输入 1 次或多次。 你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字&#xff08;其中一些字符可能被长按&#xff09;&#…

9.3 模拟

lc190 颠倒二进制ret (ret << 1) (n >> i & 1);class Solution { public:uint32_t reverseBits(uint32_t n) {uint32_t ret 0;for (int i 0; i < 32; i)ret (ret << 1) (n >> i & 1);return ret;} };lc14 flag checkclass Solution {…

esp32小智ai对话机器人

ESP-IDF 环境搭建与问题解决 下载与安装 ESP-IDF 官方下载地址&#xff1a;https://dl.espressif.com/dl/esp-idf建议使用稳定版本&#xff0c;避免开发版可能存在的兼容性问题 中文编码问题解决方案 $env:PYTHONIOENCODING "utf-8" $env:PYTHONUTF8 "1&q…

11.类与对象

目录 1. 创建类与对象示例 1.1 __init__ 初始化器: 1.2 __new__构造器 1.3 什么时候需要重写 __new__? 1.4 init和new对比 2. 属性 2.1 实例属性 2.2 类属性 3. 作用域命名约定 3.1 非公共属性 3.2 公共属性 3.3 名称修饰 3.4 一眼看懂三种“可见性” 4. 方法 …

【js】Promise.try VS try-catch

前言JavaScript 正为 Promise 添加一个新的方法&#xff0c;使得处理异步函数更加清晰和安全。Promise.try 允许将任何函数包装在 Promise 中&#xff0c;无论它是否异步。使用 Promise.try 可避免传统 try/catch 结构与 Promise 链的混合使用&#xff0c;代码更简洁。try-catc…

MySQL-表的约束(上)

表的约束在 MySQL 中&#xff0c;表的约束&#xff08;Constraints&#xff09;用于确保数据库中数据的完整性和一致性。它们定义了对表中数据的规则和限制&#xff0c;防止无效或不一致的数据被插入、更新或删除。常见的 MySQL 表约束包括主键约束&#xff08;PRIMARY KEY&…

Frida + FART 联手:解锁更强大的 Android 脱壳新姿势

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ Frida FART 联手能带来什么提升&#xff1f; 增强 FART 的脱壳能力&#xff1a;解决对抗 FART 的壳、动态加载的 dex 的 dump 和修复&#xff1b; 控制 FART…

TLS/SSL(传输层安全协议)

文章目录一、核心概念二、为什么需要 TLS/SSL&#xff1f;三、工作原理与详细流程握手步骤详解&#xff1a;1.ClientHello & ServerHello&#xff1a;2.服务器认证 (Certificate, ServerKeyExchange)&#xff1a;3.客户端响应 (ClientKeyExchange, Finished)&#xff1a;4.…

什么是 AWS 和 GCE ?

AWS 和 GCE 是两种不同厂商提供的云计算服务&#xff0c;主要区别在于提供商和产品定位。AWS全称&#xff1a;Amazon Web Services提供商&#xff1a;亚马逊 (Amazon)简介&#xff1a;全球最大的云计算平台之一&#xff0c;提供完整的云服务&#xff0c;包括&#xff1a; 计算&…

水电站电动机绝缘安全 “不掉线”!在线监测方案筑牢发电保障

对水电站而言&#xff0c;消防水泵、深井水泵等辅助电动机是安全运行的 “关键配角”—— 它们常年处于备用状态&#xff0c;又受潮湿环境影响&#xff0c;绝缘值降低易引发烧毁故障&#xff0c;而传统定期检测难以及时捕捉绝缘劣化趋势&#xff0c;一旦启动时出问题&#xff0…

【Datawhale之Happy-LLM】3种常见的decoder-only模型——Github最火大模型原理与实践教程task07

Task07&#xff1a;第三章 预训练语言模型PLM &#xff08;这是笔者自己的学习记录&#xff0c;仅供参考&#xff0c;原始学习链接&#xff0c;愿 LLM 越来越好❤&#xff09; 本篇介绍3种很典的decoder-only的PLM&#xff08;GPT、LlaMA、GLM&#xff09;。目前火&#x1f52…

【卷积神经网络】卷积神经网络的三大核心优势:稀疏交互、参数共享与等变表示

1. 引言 卷积神经网络(CNN)之所以在计算机视觉、语音识别等领域取得突破性进展,并非偶然。相比传统的全连接神经网络,CNN通过三个重要的思想来帮助改进机器学习系统:稀疏交互(sparse interactions)、参数共享(parameter sharing)、等变表示(equivariant representations)。…

网络共享协议

网络共享协议是用于在计算机网络中实现资源共享和数据传输的规则或标准。常见的共享协议包括文件共享、打印机共享、互联网连接共享等。SMB&#xff08;Server Message Block 服务器消息块&#xff09;SMB是一种网络共享协议&#xff0c;主要用于局域网中实现不同设备之间的文件…

MD5加密算法详解与实现

MD5加密算法详解与实现 什么是MD5加密&#xff1f; MD5&#xff08;Message-Digest Algorithm 5&#xff09;是一种广泛使用的密码散列函数&#xff0c;可以产生一个128位&#xff08;16字节&#xff09;的哈希值&#xff0c;通常用32位的十六进制数表示。MD5由Ronald Rivest在…