要说行列式,就得先说一下矩阵(其实是方阵)的秩的定义。
对于一个方阵,其秩的定义就是矩阵里线性无关的极大值。
听这个定义有点难懂,这里解释一下。
其实一个方阵就可以被当做是一个没有右项的方程集。
而一个方阵的秩就可以当做是,这个没有右项的方程集在经过高斯消元后,非 $0$ 行的数量。
有了这个定义,矩阵的秩还可以被当做是,这个没有右项的方程集中,不会被其他方程替代掉的方程数量:
解释一下,如果方程长这样:
$ \begin{cases} -x_1+2x_2+5x_3=11 \ 2x_1=8 \ x_1+2x_2+5x_3=19 \end{cases} $
那么,其实第三个方程式可以通过前两个方程式推出来,也就称作第三个方程式可以被前两个方程式替代掉。
知道了“替代”的定义后,就知道了一件事,就是上面说的“会被其他方程替代掉的方程”,这些方程可以直接从方程组里去掉,而不影响最终的解。
所以,我们就可以对奥数里的一个点做一个更简洁的解释:
在奥数里,讲了解 $n$ 元一次方程(组)的方法。
同时还说了,在这个 $n$ 元一次方程组内,如果“有用”的方程数量,刚好等于未知数的数量,或者大于未知数的数量,那么这个方程组就有唯一解。
这一点可以被更简洁地解释为:
这个 $n$ 元一次方程组,转化为“高斯矩阵”,并删除最后一列后(变成 $n \times n$ 的方阵),如果这个方阵的秩刚好为 $n$,那么这个方程组就有唯一解。
然后,我们回归正题,说行列式。
行列式是对于一个方阵来讲的,一个方阵有行列式有且仅当这个方程的秩刚好等于方阵的大小(行或者列)。
行列式的定义此处不讲,因为:
- 太复杂了
- (几乎)没有用处
行列式的计算方法是用的高斯消元。
在高斯消元的思路里,提到了三种“基本行变换”:(以下全部以高斯矩阵的角度去说)
- 将一行全部乘上一个非 $0$ 实数 $x$,解不变。
- 将一行内的所有元素对应地加上/减去另一行对应的元素乘上一个非 $0$ 实数 $x$,解不变。
- 交换两行,解不变。
这三种基本行变换在执行前后,行列式并不是都不变的:
- 将一行全部乘上一个非 $0$ 实数 $x$,行列式也会乘上 $x$。
- 将一行内的所有元素对应地加上/减去另一行对应的元素乘上一个非 $0$ 实数 $x$,行列式不变。
- 交换两行,行列式变号(负数变成正数,反之亦然)。
所以我们只用写一下高斯消元,然后处理一下对行列式的值的变化即可。
但这儿还有一点问题,就是行列式怎么求。
求法很好记,如果说当前方阵是个上三角矩阵(设方阵叫 $a$,那么 $a$ 是个上三角矩阵有且仅当对于所有的 $(i,j)$,如果满足 $i>j$,那么 $a_{i,j}$ 一定要等于 $0$,当然其他方格不做要求),那么这个方阵的行列式就是这个方阵中正对角线上的元素的乘积。
进而就可以写出行列式的模板了。
此外,行列式还满足一个性质。
对于一个方阵 $A$,如果它的构造如下:
$ A= \begin{bmatrix} B & C \ 0 & D \end{bmatrix} $
其中 $B$、$D$ 皆为方阵,$C$ 不做限制。
即,如果把 $A$ 划分成这样:
满足绿色部分内全部都是 $0$,红色部分 $B$ 和蓝色部分 $D$ 都是方阵。
那么,设 $\det A$ 为矩阵 $A$ 的行列式值,那么 $\det A$ 一定等于 $(\det B) \times (\det D)$。
行列式的求法有了,但有人就有个问题了,如果模数不是质数,那么怎么求行列式呢?接下来说一下做法。
首先,模数不是质数,就说明数可能没有逆元,所以以下两种操作无法进行:
- 将同一行同时乘上一个非 $0$ 整数(在乘的同时,行列式的值也需要除上相同的数,涉及逆元,证毕)
- 将同一行同时除以一个非 $0$ 整数(除的时候就涉及逆元了,证毕)
其次,我们考虑做法。
在高斯-约旦消元法里,要求把 $1 \sim n-1$ 行(除了 $now$ 行)的第 $j$ 列全部变成 $0$。
但由于模数不是质数,就无法用以前的思路去做到上述点了。
但也不是不能做到,这里就说一下。
记得“更相减损术”(又称“辗转相减法”)没?更相减损术是解决 $\gcd(a,b)$ 的一个利器。
更相减损术就是说 $\gcd(a,b)=\gcd(b,a-b)$。
更相减损术还说了,用递归实现上述公式,递归到最后,$a$、$b$ 两个数之一一定会成为 $0$。
但“辗转相除法”比“更相减损术”要快,代码也更短,所以容易被忘掉,但这里成了推导过程中必须的一步。
具体地,用上面说的更相减损术的方法,我们就可以设计出一个算法来了:
我们循环每个比 $now$ 大的 $i$(此处不需要循环每个不等于 $now$ 的 $i$,因为如果这么循环,是无法实现的,实现起来是错的;而且行列式只要求我们消成上三角矩阵即可,不用消成严格对角矩阵),然后我们只关注第 $now$ 行和第 $i$ 行。
我们不断比较这两行的主元(第 $j$ 列上的值),将主元更小的放到第 $now$ 行,主元更大的放到第 $i$ 行。
然后,将第 $i$ 行全部对应地减去第 $now$ 行。
重复以上过程直到第 $i$ 行第 $j$ 列消成了 $0$。
为了防止出现特殊情况,每次循环时也顺便判断一下第 $now$ 行第 $j$ 列是否是 $0$。
但这个算法复杂度很大,所以我们需要优化。
记得上面还提到了“辗转相除法”,这里就要用这个来优化,变成:
我们还是循环每个比 $now$ 大的 $i$,然后我们只关注第 $now$ 行和第 $i$ 行。
我们不断比较这两行的主元(第 $j$ 列上的值),将主元更小的放到第 $now$ 行,主元更大的放到第 $i$ 行。
然后,将第 $i$ 行全部对应地减去第 $now$ 行
乘上 $\left\lfloor \dfrac{第i行的主元}{第now行的主元} \right\rfloor$ 。重复以上过程直到第 $i$ 行第 $j$ 列消成了 $0$。
为了防止出现特殊情况,每次循环时也顺便判断一下第 $now$ 行第 $j$ 列是否是 $0$。
用辗转相除法就可以把循环次数优化到 $O(~\log$ 数字 $)$ 级别
以上所有过程显然都不会影响行列式,所以做法正确。