行列式
对于一个n×nn \times nn×n的矩阵A,其行列式的可以记作det(A)det(A)det(A)或者∣A∣|A|∣A∣。
行列式可以看作是一个函数,将一个矩阵映射到一个标量上
对于方阵A来说,可以将它看成n个行向量的组合,也可以看成n个列向量的组合,不管是哪一种,行列式的绝对值都等于这n个向量所张成的n维立体的超体积。换句话说,对于方阵来说,行、列向量的区分不改变行列式。
定义行列式方式有三种:
全排列方法定义
定义为:
det(A)=∣A∣=∣a11a12...a1na21a22...a2n............an1an2...ann∣=∑p(−1)τ(p)∏i=1nai,pidet(A) = |A| =
\begin{vmatrix}
a_{11} & a_{12} &... & a_{1n} \\
a_{21} & a_{22} & ... & a_{2n} \\
... & ... & ... & ...\\
a_{n1} & a_{n2} &... & a_{nn} \\
\end{vmatrix} \\
=\sum_p(-1)^{\tau(p)}\prod_{i=1}^na_{i,p_i}
det(A)=∣A∣=a11a21...an1a12a22...an2............a1na2n...ann=p∑(−1)τ(p)i=1∏nai,pi
其中ppp从1到n的所有数的一个排列,τ(p)\tau(p)τ(p)表示该序列中逆序对个数
手动计算较低阶的行列式可以采用这种方法,1到n进行全排列,一共有n!n!n!中排列,每个排列需要计算n-1次乘法,复杂度为n!×(n−1)n!\times(n-1)n!×(n−1)
归纳方法定义
这种方法只是描述了行列式的一种代数性质,时间复杂度也为阶乘量级,不适合用于计算。
即定义代数余子式和余子式,进而定义行展开和列展开
对于 n 阶行列式detA\det AdetA,某一元素 aija_{ij}aij 的余子式 MijM_{ij}Mij 指的是该行列式中,划去 aija_{ij}aij 所在的行和列后,余下的 n-1 阶子式。
det(A)=a11A11+a12A12+...+a1nA1n=(−1)1+1a11M11+(−1)1+2a12M12+...+(−1)1+na1nM1n
\begin{align}
det(A) &= a_{11}A_{11} +a_{12}A_{12} + ...+a_{1n}A_{1n} \\
&=(-1)^{1+1}a_{11}M_{11} +(-1)^{1+2}a_{12}M_{12} + ...+(-1)^{1+n}a_{1n}M_{1n}
\end{align}
det(A)=a11A11+a12A12+...+a1nA1n=(−1)1+1a11M11+(−1)1+2a12M12+...+(−1)1+na1nM1n
代数方法定义
代数方法定义是说,满足了某些性质的运算只能是行列式。
利用行列式有关初等变换的性质,可以方便手动计算更高阶的行列式。后文的「高斯消元」方法计算行列式,也用到了这个性质,时间复杂度为 O(n3)O(n^3)O(n3)。
对于一个 n 阶矩阵 A 的运算,如果满足以下三个性质,称为行列式:
- 把一个行列式的某一行或某一列的所有元素同时乘以一个数 k,等于用 k 乘这个行列式。
- 交换一个行列式的两行或两列,行列式改变符号。
- 把行列式的某一行或某一列的元素乘以同一数后加到另一行或另一列的对应元素上,行列式不变。
计算方法
定义法
如上定义时的计算方法,复杂度都是阶乘级别的,对于较大行列式无法计算。
高斯消元法
高斯消元法(Gauss–Jordan elimination)是求解线性方程组的经典算法。基本思想就是将未知量使用其他未知量进行替代以达到消去未知量的目的,进而求解出答案。
进行行列式计算的时候可以借用这种思想,结合行列式的初等变化特性,尽可能消除行列式中非零元素的个数,以实现三角行列式,进而再使用上面提到的定义法中的公式。对于三角矩阵而言,根据行列式定义,需要从行列式中取出n个不同行不同列的元素,当第一行时只有a11a_{11}a11是非零的,第二行的时候,由于a21a_{21}a21不符合要求,a22a_{22}a22以后又全是0,只能选择a22a_{22}a22,以此类推,只需要计算一个排列即可,也就是行列式对角线上的元素相乘。
初等矩阵相乘的行列式等于初等矩阵行列式相乘
注意初等变化对于行列式值的影响以及对正负号的影响。
#include <iostream>
#include <vector>
using namespace std;
int main(){
const double EPS = 1E-9;
int n;
vector<vector<double> > a(n, vector<double>(n));
for(int i=0;i<n;++i){
a[i][i] = i+1;
}
double det = 1;
//进行初等变换,化简为三角矩阵
for (int i = 0; i < n; ++i) {
int k = i; //标记该列中最大的元素行坐标
for (int j = i + 1; j < n; ++j)
if (abs(a[j][i]) > abs(a[k][i])) k = j;
if (abs(a[k][i]) < EPS) {
det = 0; //列最大值的绝对值为0直接返回0
break;
}
swap(a[i], a[k]); //交换行
if (i != k) det = -det;
det *= a[i][i];
for (int j = i + 1; j < n; ++j) a[i][j] /= a[i][i]; //除以a[i][i],对角线元素化为1,
for (int j = 0; j < n; ++j)
if (j != i && abs(a[j][i]) > EPS) //对于第j列中,不为0的行,进行行加减化为0
for (int k = i + 1; k < n; ++k) a[j][k] -= a[i][k] * a[j][i];
}
cout << det;
}
时间复杂度O(n3)O(n^3)O(n3)
线性方程组计算
关于线性方程组的数值解法一般有两类:
- 直接法:经过有限步运算,求得方程组的精确解(如果不考虑计算过程中的舍入误差)
- 迭代法:用某种极限过程去逼近线性方程组的精确解,程序设计比较简单,但存在收敛性和收敛速度等问题。
直接计算法包括:高斯消元法,克莱姆法则法(基本就是行列式定义法,阶乘复杂度)
迭代法包括:牛顿迭代法,又称为牛顿求根法。