行列式学习笔记

試著忘記壹切 提交于 2020-03-09 08:57:36

然而这东西在OI里好像并没有什么用
这上面的题不多,洛谷里连标签都没有

1.排列和逆序对

1.1排列

将n个数按任意顺序排序,得到长度为n的排列
显然有\(n!\)种不同排列
在一个排列种,对换其中两数,其他数不动,的得到另一个排列,叫做对换

1.2逆序对

对于排列\(a_1,a_2,\dots a_n\)\((i,j),i<j,\text{且}a_i>q_j\)称为一个逆序对


好了可以认为以上都是废话

1.3

奇排列:逆序对数量是奇数
偶排列:逆序对数量是偶数
一些定理:

  • 对换改变排列的奇偶性
    设对换\(a_i,a_j(i\leq j)\)两个元素
    考虑相邻的两个元素对换,逆序对数量\(+1\text{或}-1\),改变排序奇偶性
    我们对换\(a_i,a_j\)时每次都只对换相邻的两个元素
    \(a_i,a_{i+1},\dots,a_j\)
    \(\rightarrow a_{i+1},a_i,a_{i+2},\dots ,a_j\)
    \(\rightarrow a_{i+1},a_{i+2},a_i,a_{i+3},\dots,a_j\)
    \(\dots\)
    \(\rightarrow a_{i+1},a_{i+2},\dots a_j,a_i\)
    然后再用相同的方法把\(a_j\)向左移动
    移动\(i\)时移动\(j-i\)次,移动\(j\)\(j-i-1\)
    所以共移动\(2(j-i)-1\)次,奇数
    改变奇数次奇偶性,所以奇偶性肯定被改变
  • \(n>1\)的排列中,奇偶排列各占一半
    尝试将奇偶排列一一对应
    \(a_1,a_2,\dots,a_n\)为奇排列
    \(a_2,a_1,a_3,\dots,a_n\)为偶排列
    所以建立起这种一一对应的关系,就可以证明奇偶排列数量相等
  • 任意排列可以经给一系列对换变成自然排列,所作对换次数的奇偶性与这个排列的奇偶性相同
    构造一种对换顺序,将1换到\(a_1\)上,2交换到\(a_2\)上,\(\dots\)
    对换次数那个就不证了严格怎么证不大会
     
    然而这些定理行列式种用到也不多

    2.行列式

    2.1定义

    终于进入正题
    n阶行列式由\(n^2\)个数通过下式确定一个数

\[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ a_{2,1}&a_{2,2}&\dots&a_{2,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
\[=\sum_{j_1j_2\dots j_n}{sgn(j_1j_2\dots j_n)}a_{1,j_1}a_{2,j_2}\dots a_{n,j_n}\]
当然看到这个式子很是懵
叙述一遍:枚举\(1\)\(n\)的全排列(\(j_1,j_2,\dots,j_n\)),对于每一种排列,求\(a_{1,j_1}a_{2,j_2}\dots a_{n,j_n}\)再乘: $sgn(j_1j_2\dots j_n)=
\begin{cases}
1&j_1j_2\dots j_n\text{是偶排列}\
-1&j_1j_2\dots j_n\text{是奇排列}\
\end{cases}
$
然后还是很懵
其实就是每行取一个数,每列取一个,乘起来,再乘那个\(sgn\)
可以自己找个例子试试
要注意矩阵和行列式是不同的,行列式就是一个数,矩阵是数表
显然,按照定义直接求行列式的值是\(O(n!n)\)

2.2一堆定理

  1. 行列互换,值不变
    比如
    \[ \left |\begin{array}{cccc} 1&2&3 \\ 4&5&6 \\ 7&8&9 \\ \end{array}\right| \]
    变成:
    \[ \left |\begin{array}{cccc} 1&4&7 \\ 2&5&8 \\ 3&6&9 \\ \end{array}\right| \]
    在取数的时候,\(\sum\)中的每一项都是只在每行取一个数,每列取一个
    所以无论从行还是列去看,都是取了所有数
    然后这个有一种“对称的”感觉
    感性理解

  2. 用一个数去乘某个行列式等于用这个数乘此行列式的某一行
    比如这个数是\(k\)乘在第\(i\)行里
    则:
    \[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
    在那个\(\sum\)中的每一项肯定都会乘一个第\(i\)行的数,所以把这个第\(i\)行的数中的\(k\)提出了,就是\(k\times \text{这个行列式乘k以前的值}\)

  3. 如果行列式中某一行是两组数之和,则这个行列式等于分别以这两组数为改行,其余行与这个行列式相等的两个行列式之和
    即:
    $$
    \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    b_{i,1}+c{i,1}&b_{i,2}+c{i,2}&\dots&b_{i,n}+c{i,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
    =
    \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    b_{i,1}&b_{i,2}&\dots&b_{i,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
  • \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    c_{i,1}&c_{i,2}&\dots&c_{i,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
    $$
    在等号左边的行列式等于\(\sum{\dots\times (b_{i,j}+c_{i,j})}\)简写了
    把那个\(\dots\)乘进去,就是\(\sum{\dots\times b_{i,j}}\)\(\sum{\dots\times c_{i,j}}\),分别对应等号右边的两个行列式
  1. 交换行列式两行,行列式符号改变
    设把\(i,j\)两行交换
    则改变的只有\(sgn(j_1j_2\dots j_n)\),根据我们在排列中的定理,做一次对换奇偶性改变,则它的正负就改变,所以变号

  2. 如果行列式中两行成比例,行列式等于0
    考虑下面这个行列式:
    \[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
    首先由定理2,把它变成:
    \[k\times \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&\dots&a_{1,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{i,1}&a_{i,2}&\dots&a_{i,n} \\ \dots&\dots&\dots&\dots\\ a_{n,1}&a_{n,2}&\dots&a_{n,n} \\ \end{array}\right| \]
    再由定理4,交换这里相同的两行,行列式形式不变,值就不变,但它的值又应该变为相反数
    相反数等于本身,所以必然是0

  3. 把一行的某个倍数加到另外一行,值不变
    把第\(i\)行的\(k\)倍加到第\(j\)行:
    $$
    \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    a_{i,1}&a_{i,2}&\dots&a_{i,n} \
    \dots&\dots&\dots&\dots\
    ka_{i,1}+a_{j,1}&ka_{i,2}+a_{j,2}&\dots&ka_{i,n}+a_{j,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
    =
    \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    a_{i,1}&a_{i,2}&\dots&a_{i,n} \
    \dots&\dots&\dots&\dots\
    a_{j,1}&a_{j,2}&\dots&a_{j,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
  • \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    a_{i,1}&a_{i,2}&\dots&a_{i,n} \
    \dots&\dots&\dots&\dots\
    ka_{i,1}&ka_{i,2}&\dots&ka_{i,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
    \[ \]
    =
    \left |\begin{array}{cccc}
    a_{1,1}&a_{1,2}&\dots&a_{1,n} \
    \dots&\dots&\dots&\dots\
    a_{i,1}&a_{i,2}&\dots&a_{i,n} \
    \dots&\dots&\dots&\dots\
    a_{j,1}&a_{j,2}&\dots&a_{j,n} \
    \dots&\dots&\dots&\dots\
    a_{n,1}&a_{n,2}&\dots&a_{n,n} \
    \end{array}\right|
    $$
    用到了定理5和定理3
    这也是后面要常用的一个定理
  1. 终于没了

然后又因为定理1,下面几个定理对行的操作也都可以到列上

3如何更快的求值

3.1考虑一种特殊行列式

\[ \left |\begin{array}{cccc} a_{1,1}&a_{1,2}&a_{1,3}&\dots&a_{1,n} \\ 0&a_{2,2}&a_{2,3}&\dots&a_{2,n} \\ 0&0&a{3,3}&\dots&a{3,n}\\ \vdots&\vdots&\vdots&\ddots&\vdots \\ 0&0&0&\dots&a_{n,n} \\ \end{array}\right| \]
显然它的值是\(\prod_{i=1}^{n}a_{i,i}\),因为求行列式的那个\(\sum\)里其他项都会乘个0

3.2高斯消元

这个东西还可以用来解n元一次方程
有了这个,就可以进入高斯消元了
考虑把一个普通行列式变成上面那种形式
一列一列的消
考虑第一列,对于第\(i\)行(当然\(i\)不等于1),让它每个元素\(a_{i,j}\)减去\(\dfrac{a_{i,1}}{a_{1,1}}\times a_{1,j}\)(定理6)
这样就让\(2\)\(n\)行的第一列的元素变成了0
然后用第2行的\(a_{2,2}\)\(a_{2,n}\)\(n-1\)个元素,将\(a_{3,2}\)\(a_{n,2}\)(也就是第二列)消成0
同理,用第3行消第3列,第4行消第4列,\(\dots\)
当然不用考虑在消后面的元素的时候把前面已经消成0的破坏
因为如果在用第\(i\)行消第\(i\)列,\(a_{i,1}\)\(a_{i,i-1}\)和他们之间的每一个数肯定都已经消成了0,所以用他们的任意倍去减下面的\(a_{j,h}(1\leq h\leq i-1\),它已经被消成0了)还是0
所以可以写出代码
因为没有找到题,本文所有代码未验证,可能有错

double gauss(){
    double x=1;
    for(reg int i=1;i<=n;i++){
        for(reg int j=i;j<=n;j++){//找一个不为0的项 
            if(std::abs(a[j][i])>1e-8) {
                if(j==i) break;
                std::swap(a[i],a[j]);
                x=-x;//根据定理5,交换两行行列式变好 
                break;
            }
        } 
        if(std::fabs(a[i][i])<=1e-8) return 0;//没有不为0的项了,行列式值为0 
        for(reg int j=i+1;j<=n;j++){
            double k=a[j][i]/a[i][i];
            for(reg int h=1;h<=n;h++)
                a[j][h]-=a[i][h]*k;
        }
    }
    for(reg int i=1;i<=n;i++) x*=a[i][i];
    return x;
}

然而,这样可能精度会出问题
比如我们被给定的行列式都由正数组成,然而我们一直在用实数来算
所以我们基本不会用上面那种。。。

3.3精度更高的高斯消元

可以发现,整个高斯消元中,产生最大精度误差的是这一句:

double k=a[j][i]/a[i][i];  

考虑如何减小
比如我们由两个数,\(a=10^5,b=10^{-5}\)
\(\dfrac{k}{10^5}\)\(\dfrac{k}{10^{-5}}\)谁精度高呢?这里\(k\)是c++浮点数
当然是第一种,因为\(\dfrac{k}{10^{-5}}=k\times 10^5\),所以,更多的数位会去用来表示整数部分,表示小数部分的少了,就没有第一种误差小
所以我们每次可以选一个最大的a[i][i]来做除数
具体实现在上一个代码改一改就好了
因为在数学上一个矩阵或是什么中的最大数叫主元,所以整个方法叫主元高斯消元

double gauss(){
    double x=1;
    for(reg int i=1;i<=n;i++){
        int maxline=i;//最大数的行号 
        for(reg int j=i+1;j<=n;j++)
            if(std::abs(a[maxline][i])<std::fabs(a[j][i])) maxline=j;
        if(maxline!=i) std::swap(a[maxline],a[i]),x=-x;
        for(reg int j=i+1;j<=n;j++){
            double k=a[j][i]/a[i][i];
            for(reg int h=1;h<=n;h++)
                a[j][h]-=k*a[i][h];
        }
    }
    for(reg int i=1;i<=n;i++) x*=a[i][i];
    return x;
}

3.4辗转相消法

又是然而,主元高斯消元减少了误差,但没完全避免
所以我们能不能在过程中不用实数运算来避免误差呢?
用一种类似于辗转相除的方法
当然也要用定理6
还是一列一列消元,不断用数大的行减去数小的行,直到减成0
然后可以像求\(\gcd\)的时候一样把减改成模
然后这种方法的复杂度是\(O(n^2(n+\log n))\)
因为求\(n\)个数的\(\gcd\)的最大公因数实际上是\(O(n+\log n)\)
直接看代码吧,我实在想不出怎么描述了.....
很像辗转相除的
但是它没有用double的运算,所以它的常数和前面两种差别不大

int gauss(){
    int x=1;
    for(reg int i=1;i<=n;i++){
        for(reg int j=i+1;j<=n;j++){
            while(a[j][i]!=0){
                int k=a[i][i]/a[j][i];
                for(reg int h=1;h<=n;h++) a[i][h]-=a[j][h]*k;
                for(reg int h=1;h<=n;h++) std::swap(a[i][h],a[j][h]);
                x=-x;
            }
        }
    }
    for(reg int i=1;i<=n;i++) x*=a[i][i];
    return x;
}

3.5玄学方法

比如说我取一个极大的模数
行列式中每个数对它求逆元
然后除法的时候直接乘逆元
但是这种方法局限太大,模数要大于运算过程和结果的所有数(让他又能求逆元,又能不被取模后的结果影响)
一般题目中对结果和运算过程数没有约定,不好去取模数。还是别用了。。。

4.题目

代填

5.OI以外的简单应用

代填
 
 
打个笔记latex快累死我了.....

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!