行列式求值,从 $n!$ 优化到 $n^3$
前置知识
- \(\sum\) 为累加符号,\(\prod\) 为累乘符号。
- 上三角矩阵指只有对角线及其右上方有数值其余都是 \(0\) 的矩阵。
- 如果一个矩阵的对角线全部为 \(1\) 那么这个矩阵为单位矩阵记作 \(I\)。
- 对于矩阵 \(A_{n,m}\) 和矩阵 \(B_{m,n}\) 满足 \(A_{i,j}=B_{j,i}\) 记作 \(A=B^T\)。
- 如果 \(i,j\in[1,n]\) 满足 \(i<j\) 且 \(p_i>p_j\),那么称 \((p_i,p_j)\) 为一对逆序对。
- 假设 \(p\) 是一个排列,那么 \(\tau(p)\) 为 \(p\) 中逆序对的个数。
定义
行列式 \(A\) 为 \(n\) 阶方阵,那么 \(|A|\) 为该矩阵的行列式,记作 \(\operatorname{det}(A)\)。
a_{1,1} & a_{1,2} &\cdots & a_{1,n} \\
a_{2,1} & a_{2,2} &\cdots & a_{2,n} \\
\cdots & \cdots &\cdots & \cdots \\
a_{n,1} & a_{n,2} &\cdots & a_{n,n} \\
\end{vmatrix}=\sum_{j_1,j_2,\cdots ,j_n}(-1)^{N(j_1,j_2,\cdots ,j_n)}\prod_{i=1}^n a_{i,j_i}\]
几何意义
对于一个 \(n\) 维的向量空间中的 \(n\) 个向量,我们可以构造一个 \(n\) 阶行列式。这个行列式的绝对值等于由这 \(n\) 个向量所构成的平行体的体积。
具体来说,如果我们有 \(n\) 个向量 \(\vec{v_1}, \vec{v_2}, ..., \vec{v_n}\),我们可以将这些向量的坐标构造成一个n阶行列式:
v_{1,1} & v_{1,2} & \cdots & v_{1,n} \\
v_{2,1} & v_{2,2} & \cdots & v_{2,n} \\
\cdots & \cdots & \cdots & \cdots \\
v_{n,1} & v_{n,2} & \cdots & v_{n,n} \\
\end{bmatrix}
\]
其中,\(v_{ij}\) 是向量 \(\vec{v_i}\) 的第 \(j\) 个坐标。这个行列式的绝对值就是由向量 \(\vec{v_1}, \vec{v_2}, ..., \vec{v_n}\) 所形成的平行体的体积。
求解
暴力
首先有一个粗暴的做法单纯是根据定义求解的,虽然无法应用到那时有助于理解定义。
要求解行列式首先需要随机选择一行,为了方便说明不妨取第一行。那么在这一行的第 \(i\) 个元素的贡献为 \((-1)^{1+i}\times 1\times\) 不看这一行和这一列剩余的矩阵。
\]
通过这个操作,我们就将这个行列式降阶了,接下来我们只需要一直进行递归操作直到行列式的阶成为 \(1\) 就好了。所以根据定义我们就可以在 \(O(n!)\) 的时间复杂度内求解出 \(n\) 阶行列式的值了。
优化
假设矩阵 \(A\) 是一个上三角矩阵,那么 \(\operatorname{det}(A)=\prod_{i=1}^{n}a_{i,i}\) 的值,求解的时间复杂度十分优秀为 \(O(n)\),考虑是否可以使用高斯消元进行优化。
排列的性质
- 定义如果 \(\tau(p)\) 为奇数那么 \(p\) 为奇排列,反之即为偶排列。
- 对于一个 \(n(n\geq2)\) 阶排列的所有排列情况,奇排列与偶排列的情况各有 \(\dfrac{1}{2}\cdot n!\) 种。
- 将 \(p\) 中两个不同的元素进行交换得到一个新的排列的过程叫对换操作,进行一次对换操作会改变序列的奇偶性。
矩阵性质
- \(\operatorname{det}(A)=\operatorname{det}(A^T)\),所以说所有的对列成立的性质均对行成立,反之亦然。
带入排列的性质自行观察即可以得到。 - 交换某 \(2\) 行或列,此时的 \(\operatorname{det}(A)\) 需要乘以 \(-1\)。
证明同上。 - 根据上一行进行推论,如果有两行相同那么 \(\operatorname{det}(A)=0\)。
假设 \(s=\operatorname{det}(A)\),不妨设行 \(x\) 与行 \(y\) 相等,那么假设交换 \(x,y\) 则有 \(\operatorname{det}(A)=-s\) 但是矩阵并未变化,所以 \(\operatorname{det}(A)=-\operatorname{det}(A)\) 就得到了 \(\operatorname{det}(A)=0\) 是唯一解了。 - 将 \(A\) 的一行全部乘以 \(k\),那么 \(\operatorname{det}(A)\) 也需要乘以 \(k\)。
考虑从使用定义求解行列式的值的角度进行解释。因为在求值是选择任意行计算的结果都是相同的,所以不妨假设我们刚好选择了全部乘以 \(k\) 的那一行,使用乘法分配律将 \(k\) 提出即可证明。 - 根据上一行进行推论,如果有一行全部为 \(0\) 那么,那么 \(\operatorname{det}(A)=0\)。
假设 \(s=\operatorname{det}(A)\),那么不妨假设 \(x\) 行全部为 \(0\)。将行 \(x\) 全部乘以 \(k\) 得到 \(\operatorname{det}(A)=s\cdot k\),可是矩阵并未变化所以 \(\operatorname{det}(A)=s\),因为 \(k\) 为任意值所以得到 \(\operatorname{det}(A)=0\)。 - 如果行列式对应矩阵 \(A\) 中有一行,是对应 \(2\) 个矩阵 \(B,C\) 中分别的 \(2\) 行所有元素之和,那么有 \(\det(A)=\det(B)+\det(C)\)。
\]
- 将某一行的的 \(k\) 倍加到另外一行,不影响 \(\operatorname{det}(A)\) 的值。
结合前面的一些性质即可证明,十分显然。
高斯消元
考虑到将某一行的的 \(k\) 倍加到另外一行不影响 \(\operatorname{det}(A)\) 的值,可以直接使用高斯消元就可以求解了。同样还是高斯消元,用一个变量记录交换两行引起的符号改变。因为将一行的 \(\) 倍加到另一行上不影响答案,可以采用辗转相除的方式,将其他行的对应位置消成 \(0\)。
因为辗转相除法带一个 \(\log\),所以总的时间复杂度为 \(O(n^2\log W+n^3)\) 也就是 \(O(n^3)\),其中 \(W\) 为矩阵的值域。
AC Code
#define debug
// #define tests
#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
template<typename T=int> inline T read(){T x;cin>>x;return x;}
struct debug_{template<typename T>debug_&operator<<(T x){
#ifdef debug
cout<<x;
#endif
}}o;
const int N=605;
int n,a[N][N],mod;
void solve(){
cin>>n>>mod;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
int flag=1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
while(a[i][i]){
int s=a[j][i]/a[i][i];
for(int k=i;k<=n;k++){
a[j][k]=(a[j][k]-s*a[i][k]+mod)%mod;
}
flag++;
swap(a[i],a[j]);
}
flag++;
swap(a[i],a[j]);
}
}
int ans=1;
for(int i=1;i<=n;i++){
ans=(ans*a[i][i])%mod;
}
cout<<(ans*(flag%2?1:-1)+mod)%mod;
}
signed main(){
#ifdef debug
#else
ios::sync_with_stdio(false),cin.tie(nullptr);
#endif
int T=1;
#ifdef tests
cin>>T;
#endif
while(T--) solve();
return 0;
}
行列式求值,从 $n!$ 优化到 $n^3$的更多相关文章
- 高斯消元与行列式求值 part1
两道模板题,思路与算法却是相当经典. 先说最开始做的行列式求值,题目大致为给一个10*10的行列式,求其值 具体思路(一开始看到题我的思路): 1.暴算,把每种可能组合试一遍,求逆序数,做相应加减运算 ...
- 洛谷P7112 行列式求值
行列式求值 这是一个让你掉头发的模板题 行列式的定义 行列式 (\(\texttt{Determinant}\)) 是一个函数定义,取值是一个标量. 对一个 \(n\times n\) 的矩阵 \(A ...
- U66785 行列式求值
二更:把更多的行列式有关内容加了进来(%%%%%Jelly Goat奆佬) 题目描述 给你一个N(n≤10n\leq 10n≤10)阶行列式,请计算出它的值 输入输出格式 输入格式: 第一行有一个整数 ...
- 基于上三角变换或基于DFS的行(列)展开的n阶行列式求值算法分析及性能评估
进入大一新学期,看完<线性代数>前几节后,笔者有了用计算机实现行列式运算的想法.这样做的目的,一是巩固自己对相关概念的理解,二是通过独立设计算法练手,三是希望通过图表直观地展现涉及的两种算 ...
- C语言求行列式的值
#include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <window ...
- 延迟求值-如何让Lo-Dash再提速x100?
「注释」作者在本文里没有说明这么一个事实: 目前的版本Lo-Dash v2.4.1并没有引入延迟求值的特性,Lo-Dash 3.0.0-pre中部分方法进行了引入,比如filter(),map(),r ...
- 数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值
一.简介 迷宫求解:类似图的DFS.具体的算法思路可以参考书上的50.51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意.大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚, ...
- C/C++ 语言中的表达式求值(原文作者:裘宗燕)
经常可以在一些讨论组里看到下面的提问:“谁知道下面C语句给n赋什么值?”m = 1; n = m+++m++;最近有位不相识的朋友发email给我,问为什么在某个C++系统里,下面表达式打印出两个4, ...
- 求值器本质--eval&apply
最近跟着(How to Write a (Lisp) Interpreter (in Python))使用python实现了一个简易的scheme解释器.不得不说使用python这类动态语言实现不要太 ...
- Herding(hdu4709)三点运用行列式求面积
Herding Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
随机推荐
- Spring6
0x00 环境配置 环境: IDEA >= 2022.1.4 JDK 17 Maven 3.8.6 Spring 6.0.0 JUnit 4.13.2 Log4j2 新建模块 spring001 ...
- 照骗qsnctfwp
题目附件 使用 010 Editor 等工具打开发现 flag.txt 字样 将图片保存至 Kali 使用工具 foremost,通过命令foremost 3.png即可分离处图片所含隐写文件 打开发 ...
- # AssertionError: The `num_classes` (3) in Shared2FCBBoxHead of MMDataParallel does not matches the length of `CLASSES` 80) in CocoDataset
我看很多人都遇到了这个问题,有很多解决了的.我就把这篇博文再完善一下,让大家对mmdetection使用得心应手. mmdetection训练自己的数据集时报错 ️ : # AssertionErro ...
- FPGA技术脚本使用
做fpga 不会脚本,基本跟残废一个概念.以前我觉得做FPGA应该学习什么人工智能,大数据,机器人.现在想起来真是傻逼,做fpga也好,做ic,做逻辑其实基本能力都是一样的. 一个学习tcl脚本,pe ...
- hashchang事件是异步更新的
1.代码 // 此时会触发hashchange location.hash = '/test' window.addEventListener('hashchange', () => { con ...
- opensips开启python支持
操作系统 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作为脚本语言,使用起来很方便,查了下opensips的文档,支持使用py ...
- 进一步释放技术红利,阿里云推出全新内存增强型实例re6,性能提升30%
5月7日,国内最大云计算厂商阿里云宣布推出全新一代内存增强型实例,提供1:14.8超大内存比内存容量,满足内存型数据库如SAP HANA.Redis等应用,充分释放技术红利,帮助线下企业快速上云,完成 ...
- OpenKruise v0.8.0 核心能力解读:管理 Sidecar 容器的利器
简介: OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 项目.它来 ...
- [SVG] JS 动态加载 svg 修改 svg 属性
svg 概念一览: https://javascript.ruanyifeng.com/htmlapi/svg.html加载 svg: // for example: $('body').load(' ...
- OpenTK 入门 初始化窗口
本文属于 OpenTK 入门博客,这是一项使用 C# 做底层调用 OpenGL 和 OpenAL 和 OpenCL 的技术.但值得一提的是,如果是想做渲染相关的话,当前是不建议使用 OpenGL 的, ...