行列式求值,从 $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 ...
随机推荐
- 重学c#系列——DiagnosticListener [三十五]
前言 简单介绍一下DiagnosticListener,一个比较常见的事件通知模型,可以说是事件发布订阅模型,常用于监控. 正文 直接编写代码: using System.Diagnostics; p ...
- 重新整理数据结构与算法(c#)——算法套马踏棋算法[三十三]
前言 马踏棋盘 概念在这,不做过多复述. https://baike.sogou.com/v58959803.htm?fromTitle=马踏棋盘 思路是这样子的,一匹马有上面几种做法,然后进行尝试, ...
- Python 生成带Logo的圆角带边框二维码
Python 生成二维码方式就不累述了,不会的自己百度吧 但python生成的二维码太难看了,要么没有logo,要么logo直接贴进去的,难看死了,有的也处理了一下,但没有圆角,也难看: 以下:是不是 ...
- APISIX 简单的自定义插件开发步骤
本文基于 APISIX 3.2 版本进行插件开发并运行通过. APISIX 目前开发插件比较简单,只需要编写 Lua 源代码并放到默认的插件目录下,然后通过配置文件开启插件即可,我们如果使用 Dock ...
- IDEA操作MyBatis实现数据库增删改查
"感谢您阅读本篇博客!如果您觉得本文对您有所帮助或启发,请不吝点赞和分享给更多的朋友.您的支持是我持续创作的动力,也欢迎留言交流,让我们一起探讨技术,共同成长!谢谢!" 前置环境 ...
- CF1857G Counting Graphs 题解
题目描述 给定一棵最小生成树,求有多少张图的最小生成树是给定的树,并且这张图的所有边边权不超过 \(S\). 思路 考虑在最小生成树中加边. 我们回顾一下 Kruskal 的过程: 找到没被用过的,最 ...
- JDBC 在性能测试中的应用
简介: 我们能否绕开 http 协议,直接测试数据库的性能?是否觉得从数据库中导出 CSV 文件来构造压测数据很麻烦?怎样在压测结束后做数据清理?能不能通过数据库中的插入(删除)记录对压测请求做断言? ...
- 滴滴 Flink-1.10 升级之路
简介: 滴滴实时计算引擎从 Flink-1.4 无缝升级到 Flink-1.10 版本,做到了完全对用户透明.并且在新版本的指标.调度.SQL 引擎等进行了一些优化,在性能和易用性上相较旧版本都有很大 ...
- 网易游戏基于 Flink 的流式 ETL 建设
简介: 网易游戏流式 ETL 建设实践及调优经验分享- 网易游戏资深开发工程师林小铂为大家带来网易游戏基于 Flink 的流式 ETL 建设的介绍.内容包括: 专用 ETL EntryX 通用 ETL ...
- 一文理解 K8s 容器网络虚拟化
简介:本文需要读者熟悉 Ethernet(以太网)的基本原理和 Linux 系统的基本网络命令,以及 TCP/IP 协议族并了解传统的网络模型和协议包的流转原理.文中涉及到 Linux 内核的具体实现 ...