首页 > 编程语言 >算法学习笔记(39)——高斯消元

算法学习笔记(39)——高斯消元

时间:2022-12-10 09:45:05浏览次数:69  
标签:39 int 线性方程组 ++ 算法 col 高斯消 row

高斯消元

高斯消元解线性方程组

  • 通过初等行变换把增广矩阵化为阶梯型矩阵,并回代得到方程的解
  • 适用于求解 包含 \(n\) 个方程,\(n\) 个未知数的多元线性方程组

前置知识:初等行(列)变换

  1. 把某一行乘一个非00的数 (方程的两边同时乘上一个非00数不改变方程的解)
  2. 交换某两行 (交换两个方程的位置)
  3. 把某行的若干倍加到另一行上去 (把一个方程的若干倍加到另一个方程上去)

算法步骤:

枚举每一列c
1. 找到当前列绝对值最大的一行
2. 用初等行变换(2) 把这一行换到最上面(未确定阶梯型的行,并不是第一行)
3. 用初等行变换(1) 将该行的第一个系数变成 1 (其余所有的系数依次跟着变化)
4. 用初等行变换(3) 将下面所有行的当前列的值变成 0

时间复杂度:\(O(N^3)\)

题目链接:AcWing 883. 高斯消元解线性方程组

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 110;
const double eps = 1e-6;

int n;
double a[N][N];

int gauss()
{
    int col, row;
    // 枚举每一列
    for (col = 0, row = 0; col < n; col ++ ) {
        // 1. 找到当前这一列绝对值最大的系数所在的行号
        int maxRow = row;
        for (int i = row; i < n; i ++ )
            if (fabs(a[i][col]) > fabs(a[maxRow][col]))
                maxRow = i;
        
        // 如果这一列最大系数的绝对值是0,则该列全为0,所以无需计算,继续下一列,而行数仍然不变
        if (abs(a[maxRow][col]) < eps) continue;
        
        // 2. 将该行换到所有未确定的行的最上面(第row行),由于是上三角矩阵形式,所以从第一个非零的系数开始交换
        for (int i = col; i < n + 1; i ++ ) swap(a[maxRow][i], a[row][i]);
        
        // 3. 将最上行的第一个系数化为1,即行内每个系数除以第一个系数
        for (int i = n; i >= col; i -- ) a[row][i] /= a[row][col];
        
        // 4. 将下面所有行的第col列化为0(消元)
        for (int i = row + 1; i < n; i ++ ) 
            // 判断如果非零再进行操作
            if (fabs(a[i][col]) > eps)
                for (int j = n; j >= col; j -- )
                    a[i][j] -= a[row][j] * a[i][col];
        
        // 换上去并固定了一行系数,继续处理下一行
        row ++;
    }
    
    // 上三角阶梯形矩阵行数小于n,由克拉默法则,系数矩阵行列式等于0,则方程组无解或有无穷多解
    if (row < n) {
        // 如果出现了左边 = 0, 右边 != 0 的情况,即无解
        // row < n,则第row行至第n-1行都是0,所以从row行开始判断
        for (int i = row; i < n; i ++ )
            // a[i][n]即位增广矩阵第i行第n列的值,
            if (fabs(a[i][n]) > eps)
                return 2;
        // 否则有自由变量,有无穷多解
        return 1;
    }
    
    // 有唯一解,从下往上回代求解
    for (int i = n - 1; i >= 0; i -- )      // 从下往上枚举每一行
        for (int j = i + 1; j < n; j ++ )   // 每次用当前行之下的所有行来计算x
             a[i][n] -= a[j][n] * a[i][j];  // 只需要对方程右边的数进行处理即可得到解
             
    return 0;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) 
        for (int j = 0; j < n + 1; j ++ )
            cin >> a[i][j];
        
    int t = gauss();
    
    // 无解
    if (t == 2)      puts("No solution");
    // 有无穷多解
    else if (t == 1) puts("Infinite group solutions");
    // 有唯一解
    else
        for (int i = 0; i < n; i ++ ) {
            if (fabs(a[i][n]) < eps) a[i][n] = 0;  // 去掉输出 -0.00 的情况
            printf("%.2lf\n", a[i][n]);
        }
    
    return 0;
}

高斯消元解异或线性方程组

异或运算有一个别名"不进位的加法",可以利用高斯消元的方法求解异或线性方程组。

算法思想:

  1. 消成上三角矩阵
    • 枚举列
    • 找非零行
    • 交换
    • 下面消零
  2. 判断
    • 完美:唯一解
    • 有矛盾:无解
    • 无矛盾:无穷解

时间复杂度:\(O(N^3)\)

题目链接:AcWing 884. 高斯消元解异或线性方程组

#include <iostream>

using namespace std;

const int N = 110;

int n;
int a[N][N]; // 系数矩阵

int gauss()
{
    int c, r;
    // 枚举每一列
    for (c = r = 0; c < n; c ++ ) {
        // 找非零行
        int t = r;
        for (int i = r; i < n; i ++ )
            if (a[i][c]) {
                t = i;
                break;  // 找到一个非零行即可
            }
            
        if (!a[t][c]) continue; // 如果找到的这一行是0,则这一列不需要消元,看下一列
        
        // 交换到未确定部分的顶行
        for (int i = c; i <= n; i ++ )
            swap(a[t][i], a[r][i]);
            
        // 对下面每一行进行消元
        for (int i = r + 1; i < n; i ++ )
            if (a[i][c])    // 如果非0则需要消元
                for (int j = n; j >= c; j -- )
                    a[i][j] ^= a[r][j];
        
        // 当前行处理完毕,位置固定,处理下一行
        r ++;
    }
    
    if (r < n) {
        // 左边为0 右边不为0 无解
        for (int i = r; i < n; i ++ )
            if (a[i][n])
                return 2;
        // 无穷多解
        return 1;
    }
    
    // 将方程右边进行回代求解
    for (int i = n - 1; i >= 0; i -- )
        for (int j = i + 1; j < n; j ++ )
            a[i][n] ^= a[i][j] * a[j][n];
            // 上一行等价于 if (a[i][j]) a[i][n] ^= a[j][n];
            // 如果是0 就不用下面的a[j][j] 来^a[i][j]了
            // 如果不是0 才需要用第j行第j列a[j][j]来^第i行第j列a[i][j] 
            // 进而进行整行row[i]^row[j] 间接导致 a[i][n]^a[j][n]
    
    // 有唯一解
    return 0;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n + 1; j ++ )
            cin >> a[i][j];
    
    // 高斯消元解异或线性方程组
    int t = gauss();
    
    // 有唯一解
    if (t == 0) {
        for (int i = 0; i < n; i ++ ) cout << a[i][n] << endl;
    }
    // 有无穷多解
    else if (t == 1) puts("Multiple sets of solutions");
    // 无解
    else puts("No solution");
    
    return 0;
}

标签:39,int,线性方程组,++,算法,col,高斯消,row
From: https://www.cnblogs.com/I-am-Sino/p/16970795.html

相关文章