高斯消元学习笔记
PigeonTree · · 算法·理论
对于如下一方程组:
可以使用加减消元法,易得出解:
我们将方程组扩展一下,变成一个
称如上方程组为线性方程组,采用同样的消元方法,称为高斯消元。
高斯消元
以开篇的方程组为例。
我们将所有的系数提出来,得到如下系数矩阵:
将右侧的常数提出来,得到常数矩阵:
合并一下,就得到了增广矩阵:
这就是我们所要操作的矩阵。
幻想一下,如果现在我们能得到:
的方程,我们是不是就能直接求出解,对吧:
转换为矩阵的话,就是如下的样子:
替换一下:
这就是我们最终所期望得到的矩阵。如果元数变大,那么整体的
特别的,若最终矩阵存在系数全为
比如,最终矩阵如下:
这意味着对于任意值的
综上,我们要我们的增广矩阵向简化阶梯形矩阵靠拢,接下来就要用到高斯消元所需要的三类操作了。
如果是一个正常方程组,使用加减消元法总需要拿一个方程的若干倍去减另一个方程,那么这在高斯消元里对应的操作就是:将其中一行的若干倍(不为
在中间,我们会有一个将一个方程变为原来若干倍的操作,对应在高斯消元里就是:一行所有数乘以一个非零数字。
还有一个操作就是:交换某两行的数字。
我们就需要这三个操作来得到最终的矩阵,当然这三个操作也成初等行变换。
这里我们就不举例了,读者可以自行下去推推。
接下来考虑程序实现。
首先,我们每组操作的目的是确定一个元,我们第
先看看如下方程组:
如果我们选择方程
所以我们在每组操作里,首先要做的是:保证
接下来,我们就要开始消元了。
对于第
当
具体代码实现如下:
:::success[代码实现]
时间复杂度为
const double EPS=1e-9;//精度是个好问题
int gauss() {
int rank = 0;
for (int col = 1; col <= n; col++) {
int ma = rank + 1;
for (int i = rank + 1; i <= n; i++) {
if (fabs(a[i][col]) > fabs(a[ma][col]))
ma = i;
}
if (fabs(a[ma][col]) < EPS) continue;
for (int j = col; j <= n + 1; j++)
swap(a[rank + 1][j], a[ma][j]);
for (int i = 1; i <= n; i++) {
if (i == rank + 1) continue;
double rate = a[i][col] / a[rank + 1][col];
if (fabs(rate) < EPS) continue;
for (int j = col; j <= n + 1; j++)
a[i][j] -= a[rank + 1][j] * rate;
}
rank++;
}
for (int i = rank + 1; i <= n; i++) {
bool all_zero = true;
for (int j = 1; j <= n; j++) {
if (fabs(a[i][j]) > EPS) {
all_zero = false;
break;
}
}
if (all_zero && fabs(a[i][n + 1]) > EPS)
return -1;//无解
}
if (rank < n) return 1;//无数解
for (int i = 1; i <= n; i++) {
if (fabs(a[i][i]) < EPS) continue;
for (int j = n + 1; j >= i; j--)
a[i][j] /= a[i][i];
}
return 0;//唯一解
}
:::
习题
:::info[「SDOI2006」线性方程组] 模板题。借用此题熟悉高斯消元。 :::
异或方程组
如果
我们需要求解,同样可以采用高斯消元。
已知,异或运算可以看做不进位的加法。我们仍然可以写出增广矩阵,在执行高斯消元的过程中,把加减换为异或,且不用执行乘除法。
同上,我们可以写出过程:
-
对于第
i 行,保证A_{i,i} 为1 ,找到A_{j,i} 也为1 的一行j ,交换两行。 -
将第
i 列除A_{i,i} 外,全部变为0 ,即让A_{j,i} 为1 的一行整行与第i 行进行异或。
若最后存在一行系数全为
在 int 和 long long 来存储,否则就可以使用 bitset。读者可以自行下去学习 bitset。
习题
:::info[开关问题]
注意到每个开关只有开(
对于系数,如果一个开关
令第
这样我们易列出:
高斯消元即可。 :::
线性空间
因为我们老师说这和高斯消元有关所以也讲了,给一位初一生干懵了。
:::warning[如果你不懂向量的话请看这里]
向量(Vector),顾名思义就是有方向的量,一个
与向量对应的就是数量(标量),标量只有大小,没有方向。我们这里所讨论的标量都是实数。
向量也有运算,但在这里我们只用向量加法(
在一个非空向量集合
对于一组向量
则称这组向量线性相关;否则称线性无关。
若线性空间
- 线性无关;
-
则称这组向量为
举个例子。
考虑二维平面上的所有向量组成的线性空间
发现所有的向量
其实,我们可以把矩阵的每一行看成一个
习题
::::info[[JLOI2015] 装备购买]
转换一下题意,易得出我们所需要的向量应该是线性无关的,所以建立矩阵,进行高斯消元。
对于要购买尽可能多的装备,我们可以使用贪心。在选择行时,优先选择费用较少的。
贪心正确性证明如下:
:::info[证明]{open}
把所有的
若
证明:假设最优基
与
-
- 假设前
k-1 步贪心选择的向量必在某个最优基中,则由引理,第k 步选择的向量也必在某个最优基中。
故贪心每次选的向量都能扩展成一个最优基,最终得到的就是最优基。贪心正确。
::: ::::
后记
在写完全文初稿之后,使用过 AI 查错,但保证个人贡献大于 AI 贡献。
全文 6.9k+ 字,完。