所谓高斯消元,就是一种解线性方程组的算法。
学过线性代数的同学都知道,线性方程组本质就是一个向量X1左乘一个系数矩阵A得到另一个向量X2,我们要求解的就是所有未知数构成的向量X1。
设一个n元一次方程组,我们把所有未知数的系数以及等号右边的常数在保持相对位置不变的情况下组成一个n行n+1列的矩阵,
a11 a12 a13 ...... a1n b1
a21 a22 a23 ...... a2n b2
...
...
an1 an2 an3 ...... ann bn
然后我们通过矩阵的初等变换,每个方程选择一个未知数的系数(我们称为主元),然后把其余方程同一个未知数的系数变为0,最终将该矩阵的未知数系数部分变成每行只有一个非零的数,每列只有一个非零的数的鬼畜矩阵,这样很显然每个未知数最终的值都可以直接求出来啦。
当然这里有两个问题
一是精度误差。众所周知浮点数会有精度误差的,精度要依题而定。精度过小会导致较小的系数误以为0,而精度过大则可能导致本该为0的误判为不为0,故而精度要有把握,一般在1e-4~1e-10
二是主元的选择,我们要想办法避免或者减少精度误差,我们可以选择一个方程里系数最大的那个作为主元,这样的精度误差可以减少,因为在浮点数除法运算时,一个较大的数除以一个较小的数的误差是个玄学大。
枚举行,先扫一遍得到该行系数最大值,再枚举剩下(n-1)行,然后对每行n个数进行运算,故而高斯消元的时间复杂度就是O(n3)
最后这个方程组如果是在模2的意义下的话,也就是说方程未知数的系数以及等号右边的数不是0就是1的话,我们可以用bitset来加快对每行的运算操作从而时间复杂度降为而O(n2)。
1 #include <algorithm>
2 #include <cstring>
3 #include <cstdlib>
4 #include <cstdio>
5 #include <iostream>
6 #include <cmath>
7 #include <ctime>
8 #include <queue>
9 #define MIN(a,b) (((a)<(b)?(a):(b)))
10 #define MAX(a,b) (((a)>(b)?(a):(b)))
11 #define ABS(a) (((a)>0?(a):-(a)))
12 #define debug(a) printf("a=%d
",a)
13 #define OK cout<<"QAQ"<<endl
14 #define fo(i,a,b) for (int i=(a);i<=(b);++i)
15 #define fod(i,a,b) for (int i=(a);i>=(b);--i)
16 #define rep(i,a,b) for (int i=(a);i<(b);++i)
17 #define red(i,a,b) for (int i=(a);i>(b);--i)
18 #define N 106
19 typedef long long LL;
20 using namespace std;
21 int n, w[N]; //w数组记录第i行最大系数的位置
22 double v[N], a[N][N + 1]; //v数组记录xi的解,a数组为相应矩阵
23 void readint(int& x) {
24 x = 0;
25 char c;
26 int w = 1;
27 for (c = getchar(); c<'0' || c>'9'; c = getchar())
28 if (c == '-') w = -1;
29 for (; c >= '0' && c <= '9'; c = getchar())
30 x = (x << 3) + (x << 1) + c - '0';
31 x *= w;
32 }
33 void readlong(long long& x) {
34 x = 0;
35 char c;
36 long long w = 1;
37 for (c = getchar(); c<'0' || c>'9'; c = getchar())
38 if (c == '-') w = -1;
39 for (; c >= '0' && c <= '9'; c = getchar())
40 x = (x << 3) + (x << 1) + c - '0';
41 x *= w;
42 }
43 int read() {
44 int x = 0;
45 char c;
46 int w = 1;
47 for (c = getchar(); c<'0' || c>'9'; c = getchar())
48 if (c == '-') w = -1;
49 for (; c >= '0' && c <= '9'; c = getchar())
50 x = (x << 3) + (x << 1) + c - '0';
51 x *= w;
52 return x;
53 }
54 bool gauss() {
55 int p; //p为最大系数的位置
56 double mx = 0; //mx为最大系数
57 double eps = 1e-6; //eps为精度控制
58 fo(i, 1, n) {
59 p = 0;
60 mx = 0;
61 fo(j, 1, n) if (fabs(a[i][j]) - eps > mx) { mx = fabs(a[i][j]); p = j; } //fabs函数是浮点数的绝对值函数
62 if (p == 0) return 0; //这个情况下,该行未知数系数全为0,若等号右边的常数不为0,则该方程组无解,若为0,则有无数组解
63 fo(j, 1, n)
64 if (i != j) { //枚举其他行进行运算
65 double qwq = a[j][p] / a[i][p]; //主元选最大的可以在作商时相对地减少精度误差
66 fo(k, 1, n + 1) a[j][k] -= qwq * a[i][k]; //注意这里是n+1
67 }
68 w[i] = p;
69 }
70 fo(i, 1, n) v[w[i]] = a[i][n + 1] / a[i][w[i]];
71 return 1;
72 }
73 int main() { // by Lanly
74 readint(n);
75 fo(i, 1, n) fo(j, 1, n + 1) a[i][j] = read();
76 //fo(i, 1, n) fo(j, 1, n + 1) printf("%.2lf%c", a[i][j], j == n + 1 ? '
' : ' ');
77 if (gauss()) fo(i, 1, n) printf("%.2lf
", v[i]);
78 else puts("No Solution");
79 return 0;
80 }