要说行列式,就得先说一下矩阵(其实是方阵)的秩的定义。

对于一个方阵,其秩的定义就是矩阵里线性无关的极大值。

听这个定义有点难懂,这里解释一下。

其实一个方阵就可以被当做是一个没有右项的方程集。

而一个方阵的秩就可以当做是,这个没有右项的方程集在经过高斯消元后,非 $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$,那么这个方程组就有唯一解。


然后,我们回归正题,说行列式。

行列式是对于一个方阵来讲的,一个方阵有行列式有且仅当这个方程的秩刚好等于方阵的大小(行或者列)。

行列式的定义此处不讲,因为:

  1. 太复杂了
  2. (几乎)没有用处

行列式的计算方法是用的高斯消元。

在高斯消元的思路里,提到了三种“基本行变换”:(以下全部以高斯矩阵的角度去说)

  1. 将一行全部乘上一个非 $0$ 实数 $x$,解不变。
  2. 将一行内的所有元素对应地加上/减去另一行对应的元素乘上一个非 $0$ 实数 $x$,解不变。
  3. 交换两行,解不变。

这三种基本行变换在执行前后,行列式并不是都不变的:

  1. 将一行全部乘上一个非 $0$ 实数 $x$,行列式也会乘上 $x$。
  2. 将一行内的所有元素对应地加上/减去另一行对应的元素乘上一个非 $0$ 实数 $x$,行列式不变。
  3. 交换两行,行列式变号(负数变成正数,反之亦然)。

所以我们只用写一下高斯消元,然后处理一下对行列式的值的变化即可。

但这儿还有一点问题,就是行列式怎么求。

求法很好记,如果说当前方阵是个上三角矩阵(设方阵叫 $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$ 数字 $)$ 级别

以上所有过程显然都不会影响行列式,所以做法正确。