我也不知道为什么要学这个,不过线性代数相关的内容确实是计算机科学非常重要的知识。
关于行列式
前置芝士:矩阵和高斯消元。
行列式的定义
行列式 (Determinant) 是一个函数定义,取值是一个标量。
对一个 n×n 的矩阵 A(n 阶方阵),其 n 阶行列式写作 det(A) 或者 |A|,定义为:
det(A)=|A|=∑p(−1)τ(p)n∏i=1ai,pi
p 表示一个排列,所有可能的 p 则是 1 到 n 这 n 个数的全排列。τ(p) 表示一个排列 p 的逆序对个数。
行列式可以理解为所有列向量所夹的几何体的有向体积,这样可以结合从几何直观出发理解为线性变换的伸缩因子。
不过,这些都不重要! 在 OI 中,我们最应该了解的,是如何高效地求出一个矩阵的行列式然后去做题。至于其更深层次的数学意义,交给数学家们吧。
关于排列的奇偶性
我们发现 τ(p) 的奇偶性对行列式求值起到了很大的影响,所以我们需要了解排列的奇偶性相关。
- 我们约定:如果 τ(p) 为奇数,则 p 为一个奇排列,否则是一个偶排列;
- 对于一个 n(n≥2) 阶排列的所有排列情况,奇排列与偶排列的情况各 12;
- 对于排列 p 我们交换其中的 2 个元素,其余元素不边,会得到一个新的排列,这种操作叫对换;
- 一次对换会改变排列的奇偶性;
- 一个排列可以通过若干次对换变成一个元素严格递增的自然排列,对换次数的奇偶性与原排列的奇偶性相同。
行列式的性质与定理
行列式有着一些有助于我们做题的性质。
行列式的不变性从体积的几何意义上理解非常直观,但是我们这里不做讨论。
- 交换对应矩阵的 2 行(列),行列式取反;(1)
- 交换 1 行与 1 列(进行一次矩阵转置),行列式不变;(2)
这两个性质的证明,可以考虑结合排列的奇偶性,然后直接重新带入公式观察。
-
行列式的行(列)所有元素等比例变化,则行列式也等比例变化;(3)
-
∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮k×ai,1k×ai,2⋯k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣=k×∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣
-
如果行列式对应矩阵 A 中有一行(列),是对应 2 个矩阵 B,C 中分别的 2 行(列)所有元素之和。那么有 det(A)=det(B)+det(C);(4)
-
∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮bi,1+ci,1bi,2+ci,2⋯bi,n+ci,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣=∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮bi,1bi,2⋯bi,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣+∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ci,1ci,2⋯ci,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣∣
-
如果一个矩阵存在两行(列)成比例则 det(A)=0;(5)
-
∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮k×ai,1k×ai,2⋯k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣=0
-
这个证明可以直接代,但也有巧妙的办法:我们可以通过交换有比例关系的这两行得到新的矩阵 A′ k×ai,1,k×ai,2,⋯,k×ai,n 现在就在上面的位置,一次交换使得 det(A)=−det(A′) ,我们通过 (3) 将 k 的比例变化放到行列式前,也就是 A,A′ 的 k×ai,1k×ai,2⋯k×ai,n 一行(列)变成 ai,1,ai,2⋯,ai,n,得到的新的矩阵分别是 A′′,A′′′ 我们发现 A′′=A′′′ 又有 k×|A′′|=|A|=−k×|A′′′|=−|A′|,一个数等于其相反数,所以有 |A|=0;
-
把一个矩阵的一行(列)的值全部乘一个常数加到另一行(列)上,行列式值不变。(6)
-
∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1+k×ai,1aj,2+k×ai,2⋯aj,2+k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣=∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1aj,2⋯aj,2⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣
-
证明也很简单,根据 (4) 有:
-
∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1+k×ai,1aj,2+k×ai,2⋯aj,2+k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣=∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1aj,2⋯aj,2⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣+∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮k×ai,1k×ai,2⋯k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣
-
∵(5)
-
∴∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1+k×ai,1aj,2+k×ai,2⋯aj,2+k×ai,n⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣=∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮ai,1ai,2⋯ai,n⋮⋮⋱⋮aj,1aj,2⋯aj,2⋮⋮⋱⋮an,1an,2⋯an,n∣∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣
∣∣+0
行列式求值
那么知道了行列式这样一些性质,怎样在 OI 中高效地去求行列式呢。
直接根据定义计算,行列式求值是 Θ(n×n!) 的。显然太高了。
有上面这些性质我们怎么该将问题简化呢。
消元
我们考虑一个情况,当一个矩阵任意一个位置出现 0,其对行列式的影响非常大。
因为我们考虑公式中 n∏i=1ai,pi 一项,一旦选到 0 整个 p 在 ∑p 中就没有贡献了。
我们利用上面的一些性质显然是可以让矩阵不断变化出现 0 的。运用定理 (1),(3),(4) 就可以做到。
显然瞎转化肯定是不行的,我们要让运算次数尽可能少。
如果学习了前置知识。我们知道求解线性方程组的算法高斯消元。其实高斯消元在做增广矩阵行(初等)变换为行最简形时的步骤和我们的转化有异曲同工之妙。
我们现在考虑将矩阵一行(列)消成只有最后一个元素非 0 该怎么做。也就是说:
A=⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣a1,1a1,2a1,3⋯a1,na2,1a2,2a2,3⋯a2,na3,1a3,2a3,3⋯a3,n⋮⋮⋮⋱⋮an,1an,2an,3⋯an,n⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦⇒⎡⎢
⎢
⎢
⎢
⎢
⎢
⎢
⎢⎣a1,1a1,2a1,3⋯a1,na2,1a2,2a2,3⋯a2,na3,1a3,2a3,3⋯a3,n⋮⋮⋮⋱⋮000⋯an,n⎤⎥
⎥
⎥
⎥
⎥
⎥
⎥
⎥⎦
对于 1 到第 n−1 列中的第 i 列,我们只需要让第 i 列整列加上第 n 列的 −an,ian,n 倍就可以在 det(A) 不变的情况下使得整行前 n−1 个元素被消掉。
代数余子式求值
消元有什么用呢,我们引入线性代数中帮助我们求行列式的一个概念——代数余子式。
在一个 n 阶行列式 D 中选定 k 行 k 列可以组成一个 k 阶子行列式 A;
删除在 k 行 k 列后剩下的 n−k 阶行列式称为 A 对应的 n−k 阶余子式 M。
设 A 在 D 中原来的元素行下标有集合 I={i1,i2,…,ik} 列下标有集合 J={j1,j2,…,jk},则有 (−1)(i1+i2+⋯+ik)(j1+j2+⋯+jk)×det(M) 为 n 阶行列式 D 的 k 阶子式 A 的代数余子式。
对于单一元素 ai,j 我们令其代数余子式为 Ai,j ,余子式为 Mi,j 。有 Ai,j=(−1)i+jMi,j。
我们有如下命题:
求值
我们回到刚才最后一行消元后的情况。运用第一个命题我们发现 D 的值只是 an,n×An,n。这个时候如果我们继续对 An,n 做同样的消元呢?
∣∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2a1,3⋯a1,n−1a1,na2,1a2,2a2,3⋯a2,n−1a2,na3,1a3,2a3,3⋯a3,n−1a3,n⋮⋮⋮⋱⋮⋮an−1,1an−1,2an−1,3⋯an−1,n−1an,n−1an,1an,2an,3⋯an,n−1an,n∣∣
∣
∣
∣
∣
∣
∣
∣∣
也就是说如果对于这个 n−1 阶的红色余子式继续做消元使得其变成:
∣∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2a1,3⋯a1,n−1a2,1a2,2a2,3⋯a2,n−1a3,1a3,2a3,3⋯a3,n−1⋮⋮⋮⋱⋮000⋯an−1,n−1∣∣
∣
∣
∣
∣
∣
∣∣
我们发现之前余子式 An,n 又只等于 an−1,n−1×An−1,n−1。
以此类推,如果我们一直:
∣∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2a1,3⋯a1,n−1a1,na2,1a2,2a2,3⋯a2,n−1a2,na3,1a3,2a3,3⋯a3,n−1a3,n⋮⋮⋮⋱⋮⋮an−1,1an−1,2an−1,3⋯an−1,n−1an,n−1an,1an,2an,3⋯an,n−1an,n∣∣
∣
∣
∣
∣
∣
∣
∣∣
按不同颜色一直递归下去做,我们发现最后我们得到
∣∣
∣
∣
∣
∣
∣
∣
∣∣a1,1a1,2a1,3⋯a1,n−1a1,n0a2,2a2,3⋯a2,n−1a2,n00a3,3⋯a3,n−1a3,n⋮⋮⋮⋱⋮⋮000⋯an−1,n−1an,n−1000⋯0an,n∣∣
∣
∣
∣
∣
∣
∣
∣∣
这样的一个下三角行列式。
我们就会发现这样一个矩阵的行列式是其对角线所有元素的乘积,也就是 n∏i=1ai,i。
这样就可以做了。
实现的时候有一些细节:
消元操作是 Θ(n3) 的,辗转相除法是 Θ(logp) 的,因为辗转相除和消元每次必然使得数变小,势能只会减少,所以这个是均摊到 Θ(n2) 的,最终有复杂度 Θ(n2logn+n3)。
代码
Determinant——2021.03.21写在机房
Reference:
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】