整理日期按学习顺序编排,不代表原始发表时间。实际整理于 2026-10-23。
难度为阅读建议,可从 ACM 学习索引 按阶段阅读。
当我们枚举到第 i 个变量,且前 i 个变量都不是自由元时,如果第 i∼n 行中这个变量的系数均为 0,那么第 i 个变量是自由元。
⎣⎢⎢⎢⎢⎢⎡a1,10000a1,2a2,2000a1,3a2,3000a1,4a2,4a3,4a4,40a1,5a2,5a3,5a4,5a5,5∣∣∣∣∣∣∣∣∣∣∣c1c2c3c4c5⎦⎥⎥⎥⎥⎥⎤
上面的例子中,x3 即为自由元。可以发现,当 x4,x5 被确定之后,第三行的方程可以记为:
0⋅x3=c3−x4⋅a3,4−x5⋅a3,5=C
若 C=0,则 x3 可以在 R 中任取。
若 C=0,则 x3 一定无解,方程组也一定无解。
另外,存在一个自由元,其值可以任取,不代表方程组有无穷组解。因为若有其它变量是无解的,则方程组无解。
出现自由元这种情况,说明有些方程之间是线性相关的,所以加减消元之后就消没了。
解决 n=m 的情况
在枚举变量的过程当中,会发现有些变量是自由元。这时我们应当跳过这一列,并且保持行不变,处理下一个变元。
因此我们用指针 row 和 col 表示当前所在的行和列,最后 row 的位置即为非自由元的个数。
由于遇到所有自由元时,我们都保持行不变,所以可以理解为:我们把所有自由元都挪到了最后面没有被 row 遍历到的几行当中去。
这可以结合上面 5×5 的例子进行理解。
在遇到自由元 x3 后,跳过第 3 列,保持 row 不变。所以第 3 行保留的主元是 x4,同理第 4 行保留的主元是 x5.
所以第 5 行即代表第 x3,第 5 行的方程为:0×x3=c5.
代码
下面代码会将所有自由元保留到最后的连续几行。
因此,要判断解的情况,只需要去判断第 row+1∼n 行的系数 c 是否为 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
| //高斯消元法,返回-1为无解,0为无穷解,1为唯一解 double A[MAXN][MAXN], ans[MAXN]; int Gauss() { int row = 1, max_row; for(int col = 1; col <= m; col++) { max_row = row; for(int i = row + 1; i <= n; i++) { //提高精度 找到一列中值最大的 if(fabs(A[i][col]) > fabs(A[max_row][col])) max_row = i; } if(fabs(A[max_row][col]) < eps) continue; //如果col是自由元,那么跳过这一列,但行不变 if(max_row != row) { //swap(当前行,最大值所在的行) for(int i = 1; i <= m + 1; i++) swap(A[row][i], A[max_row][i]); } for(int i = row + 1; i <= n; i++) { double t = A[i][col] / A[row][col]; for(int j = 1; j <= m + 1; j++) { A[i][j] -= A[row][j] * t; } } row++; } row--; if(row < n) { for(int i = row + 1; i <= n; i++) { //如果左边所有系数都是0,等号右边不是0,则无解 if(abs(A[i][m + 1]) > eps) return -1; } //否则有无数解 return 0; } for(int i = m; i >= 1; i--) { //回代 for(int j = i + 1; j <= m; j++) A[i][m + 1] -= A[i][j] * ans[j]; ans[i] = A[i][m + 1] / A[i][i]; } return 1; }
|
高斯消元 Java 高精度版 HDU 2449 Gauss Elimination-CSDN博客