• 高斯消元与线性空间


    高斯消元

    考虑如何解下面的方程组:

    $a_{11}x_1+a_{12}x_2+...+a_{1n}x_n=y_1$

    $a_{21}x_1+a_{22}x_2+...+a_{2n}x_n=y_2$

    ...

    $a_{n1}x_1+a_{n2}x_2+...+a_{nn}x_n=y_n$

    求满足条件的$x_1,x_2...x_m$。

    把系数抽象成矩阵:

    a11 a12 ... a1n

    a21 a22 ... a2n

    ...

    an1 an1 ... ann

    再将其变为增广矩阵:

    a11 a12 ... a1n b1

    a21 a22 ... a2n b2

    ...

    an1 an1 ... ann b2

    高斯消元即在增广矩阵上通过初等行变换而消成对角矩阵。

    初等行变换:

    1、交换两行

    2、将某行同时乘上某个数

    3、将某行的几倍加到另一行上

    其实就是在做加减消元的过程。

    具体来讲,我们枚举每个要消的未知数$x_i$。然后留下$a_{ii}$,其余行第i列全通过初等行变换变为0。

    这要求之前的数全为0。我们通常找到最大的$a_{ji}$和$a_{ii}$换来保持精度。

    最后的答案即为$frac{B[i]}{A[i][i]}$。

    一个例子(从网上借鉴的):

    模板:

    void Gauss() {
        for (int i = 1; i <= n; i++) {
            int mx = i;
            for (int j = i + 1; j <= n; j++) {
                if (A[j][i] > A[mx][i]) {
                    mx = j;
                }
            }
            for (int j = i; j <= n; j++) {
                swap(A[i][j], A[mx][j]);
            }
            swap(B[i], B[mx]);
            if (!A[i][i]) continue;
            for (int j = 1; j <= n; j++) {
                if (i == j) continue;
                double Rate = A[j][i] / A[i][i];
                for (int k = i; k <= n; k++) {
                    A[j][k] -= A[i][k] * Rate;
                }
                B[j] -= B[i] * Rate;
            }
        }
    }

    消元过程中可能出现某一行全为0但b不为0,这种情况误解。如果某一行全为0而b也为0则有无数解。注意先判误解再判无数解。

    若系数不全为0的行有k个,则主元有k个,自由元有n-k个(存在解得情况下)。

    不过还有可能出现种情况,要特判。

    这种情况可以这么写:

    有对应的题目:[SDOI2006]线性方程组

    #include <bits/stdc++.h>
    using namespace std;
    const double eps = 1e-8;
    const int N = 110;
    int n;
    double a[N][N], b[N];
    int sgn(double x) {
        if (fabs(x) < eps) return 0;
        if (x > 0) return 1;
        return -1;
    }
    void deburg() {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) printf("%.2lf ", a[i][j]);
            printf("%.2lf
    ", b[i]);
        } printf("
    ");
    }
    int Gauss() {
        int now = 1;
        for (int i = 1; i <= n; i++) {
            int mx = now;
            for (int j = now + 1; j <= n; j++) {
                if (fabs(a[j][i]) > fabs(a[mx][i])) {
                    mx = j;
                }
            }
            for (int k = 1; k <= n; k++) swap(a[now][k], a[mx][k]);
            swap(b[now], b[mx]);
            if (fabs(a[now][i]) < eps) {
                continue;
            }
            double div = a[now][i];
            for (int j = i; j <= n; j++) a[now][j] /= div;
            b[now] /= div;
            for (int j = 1; j <= n; j++) {
                if (now == j) continue;
                double base = a[j][i] / a[now][i];
                for (int k = i; k <= n; k++) a[j][k] -= a[now][k] * base;
                b[j] -= b[now] * base;
            }
            now++;
        }
        if (now <= n) {
            while (now <= n) {
                if (b[now++] != 0) return -1;
            }
            return 0;
        }
        return 1;
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                scanf("%lf", &a[i][j]);
            }
            scanf("%lf", &b[i]);
        }
        int ans = Gauss();
        if (ans <= 0) {
            printf("%d", ans);
        } else {
            for (int i = 1; i <= n; i++) {
                printf("x%d=%.2lf
    ", i, b[i]);
            }
        }
        return 0;
    }

    这篇题解讲得蛮清楚的。

    习题:

    [JSOI2008]球形空间产生器(模板)

    线性空间

    定义:关于向量加法与数乘封闭的集合

    如果向量$b$,能被向量$a_1, a_2, dots, a_n$通过数乘表示出来即存在实数$x_1, x_2,dots, x_n$使得$b=a_1x_1, a_2x_2, dots, a_nx_n$。

    所有$b$构成一个线性空间,$a_1, a_2, dots, a_n$称为生成子集。

    给定若干向量$a_1, a_2, dots, a_n$若存在$a_i$能被其它表示出来则称其线性相关,若不行则称其线性无关。

    线性无关的向量组称为构成的线性空间的基底(简称基)。

    这个基底的向量的数量称为维数。

    对于一个$n*m$的矩阵其$n$个行向量构成的线性空间的维数称为其行秩,同样我们可以定义列秩。

    对于一个矩阵其行秩与列秩相等,统称为矩阵的秩。

    对这个矩阵高斯消元求出主元的个数即为矩阵的秩。

    例题:[JLOI2015]装备购买

    有先选费用小的高斯消元即可。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const long double eps = 1e-5;
    struct node{
        long double b[510];
        int c;
    }a[510];
    int n, m;
    int ans;
    void Gauss() {
        int now = 1;
        for (int i = 1; i <= m; i++) {
            int cha = -1;
            for (int j = now; j <= n; j++) {
                if (fabs(a[j].b[i]) > eps && (cha == -1 || a[cha].c > a[j].c)) {
                    cha = j;
                }
            }
            if (cha == -1) continue;
            for (int j = 1; j <= m; j++) swap(a[now].b[j], a[cha].b[j]);
            swap(a[now].c, a[cha].c);
            for (int j = 1; j <= n; j++) {
                if (j == now) continue;
                double base = a[j].b[i] / a[now].b[i];
                for (int k = i; k <= m; k++) {
                    a[j].b[k] -= a[now].b[k] * base;
                }
            }
            ans += a[now].c;
            now++;
            if (now > n) break;
        }
        printf("%d %d", now - 1, ans);
    }
    int main() {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                scanf("%Lf", &a[i].b[j]);
            }
        }
        for (int i = 1; i <= n; i++) scanf("%d", &a[i].c);
        Gauss();
        return 0;
    }
  • 相关阅读:
    C++模板元编程(二)
    C++模板元编程(一)
    interpret_cast
    Bresenham算法
    Windows中编译Lua源码
    MathJax基础教程与快速参考
    散列表
    Bash脚本实例
    Bash基础
    【iOS】重读《精通Objective-C》(一)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13053504.html
Copyright © 2020-2023  润新知