zoukankan      html  css  js  c++  java
  • 行列式学习笔记

    updata on 2020.3.29

    修了一些炸了的公式,原本在洛谷博客上是好的,搬过来成这样了
    然后去掉了定理部分的列表,把一堆行间公式放在列表里是怎么想的。。。
    然而#4和#5还是没填

    on 2020.11.21

    这篇后面两个内容耽误太久了,现在并不准备把它写完了,置顶也去掉了
    发现那个奇怪的高斯消元方法好像是假的,删掉了它


    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一堆定理

    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.2.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以前的值}\)

    2.2.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}}\),分别对应等号右边的两个行列式

    2.2.4交换行列式两行,行列式符号改变

    设把\(i,j\)两行交换
    则改变的只有\(sgn(j_1j_2\dots j_n)\),根据我们在排列中的定理,做一次对换奇偶性改变,则它的正负就改变,所以变号

    2.2.5如果行列式中两行成比例,行列式等于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

    2.2.6把一行的某个倍数加到另外一行,值不变

    把第\(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
    这也是后面要常用的一个定理

    2.2.7终于没了

    然后又因为定理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;
    }
    

    4.题目

    代填

    5.OI以外的简单应用

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

  • 相关阅读:
    Cookie和Session
    Servlet中关于中文乱码
    单例模式
    document对象-操作元素的文档结构
    JS实现简单的多选选项的全选反选按钮
    js实现表单验证
    servlet
    http(Hyper Text Transfer Protocol)
    Vue之webpack打包
    Flink用户画像系统之实时品牌爱好
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12446673.html
Copyright © 2011-2022 走看看