行列式求值,从 $n!$ 优化到 $n^3$

· 浏览次数 : 2

小编点评

**算法:** 1. 对矩阵进行排列,定义奇偶排列数为奇数。 2. 根据排列性质,确定交换行或列的操作方式。 3. 使用高斯消元计算矩阵的行列式值。 4. 考虑使用对换操作和高斯消元优化算法,减少计算时间。 5. 如果矩阵有奇数行,则行列式值为 0。 **代码:** ```c++ #include using namespace std; struct debug_ { #ifdef debugcout inline void operator << (int x) { cout << x << " "; } #endif }; const int N = 605; int n, a[N][N], mod; void solve() { cin > n > mod = 1e9 + 7; 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 << endl; } signed main() { #ifdef debug ios::sync_with_stdio(false), cin.tie(nullptr); #endif int T; cin >> T; while (T--) solve(); return 0; } ```

正文

前置知识

  • \(\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)\)

\[\begin{vmatrix} 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阶行列式:

\[\begin{bmatrix} 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\) 不看这一行和这一列剩余的矩阵。

\[\begin{vmatrix} 1 & 2 & 3\\ 4 & 5 & 6\\ 7 &8 &9\end{vmatrix}\\=(-1)^{1+1}\times 1\times \begin{vmatrix}5& 6\\8&9\end{vmatrix}+(-1)^{1+2}\times 2\times \begin{vmatrix}4&6\\7&9\end{vmatrix}+(-1)^{1+3}\times 3\times \begin{vmatrix}4&5\\7&8\end{vmatrix} \]

通过这个操作,我们就将这个行列式降阶了,接下来我们只需要一直进行递归操作直到行列式的阶成为 \(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)\)

\[\begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ b_{i,1}+c_{i,1} &b_{i,2}+c_{i,2} &\cdots &b_{i,n}+c_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} = \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ b_{i,1} &b_{i,2} &\cdots &b_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} + \begin{vmatrix} a_{1,1} &a_{1,2} &\cdots &a_{1,n}\\ a_{2,1} &a_{2,2} &\cdots &a_{2,n}\\ \vdots &\vdots &\ddots &\vdots\\ c_{i,1} &c_{i,2} &\cdots &c_{i,n}\\ \vdots &\vdots &\ddots &\vdots\\ a_{n,1}&a_{n,2}&\cdots&a_{n,n}\\ \end{vmatrix} \]

  • 将某一行的的 \(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$相似的内容:

行列式求值,从 $n!$ 优化到 $n^3$

前置知识 \(\sum\) 为累加符号,\(\prod\) 为累乘符号。 上三角矩阵指只有对角线及其右上方有数值其余都是 \(0\) 的矩阵。 如果一个矩阵的对角线全部为 \(1\) 那么这个矩阵为单位矩阵记作 \(I\)。 对于矩阵 \(A_{n,m}\) 和矩阵 \(B_{m,n}\) 满足 \

大数据 - DWS层 业务实现

统计主题 需求指标【ADS】输出方式计算来源来源层级 访客【DWS】pv可视化大屏page_log 直接可求dwd UV(DAU)可视化大屏需要用 page_log 过滤去重dwm UJ 跳出率可视化大屏需要通过 page_log 行为判断dwm 进入页面数可视化大屏需要识别开始访问标识dwd 连续

#Power Query 分组依据,数据的分类汇总

一:概述 Power Query中的分组依据,类似于Excel中的分类汇总功能,可以按照某一分类对某列数据或某几列数据进行去重操作和聚合计算(求和、计数、求平均、非重复行计数等),并在去重的过程中将其他数据列按照用户指定的方式, 对其进行聚合以便生成与依据列相对应的数据。在实际工作中,当我们遇到原始

使用位运算优化 N 皇后问题

使用位运算优化 N 皇后问题 作者:Grey 原文地址: 博客园:使用位运算优化 N 皇后问题 CSDN:使用位运算优化 N 皇后问题 问题描述 N 皇后问题是指在 n * n 的棋盘上要摆 n 个皇后, 要求:任何两个皇后不同行,不同列也不在同一条斜线上, 求给一个整数 n ,返回 n 皇后的摆法

跟女朋友介绍十个常用的 Python 内置函数,她夸了我一整天

内置函数是什么 了解内置函数之前,先来了解一下什么是函数 将使用频繁的代码段进行封装,并给它起一个名字,当我们使用的时候只需要知道名字就行 函数就是一段封装好的、可以重复使用的代码,函数使得我们的程序更加简洁、模块化,提高了代码的复用性 举个例子 我想实现一个求球表面积功能的程序,当我们知道半径 r

#PowerBi 1分钟学会,powerbi中行列值拼接(COMBINEVALUES与CONCATENATEX)

在日常的工作中,我们往往需要对表格数据的拼接,用来生成一些复合数据列,如下图类似场景。 其实,在powerbi中,我们同样也可以对表格文本进行拼接。今天我们就介绍两个DAX函数,COMBINEVALUES(表函数,新建列)与 CONCATENATEX(度量值)。示例数据表: 一:COMBINEVAL

【pandas小技巧】--反转行列顺序

反转`pandas` `DataFrame`的行列顺序是一种非常实用的操作。在实际应用中,当我们需要对数据进行排列或者排序时,通常会使用到Pandas的行列反转功能。这个过程可以帮助我们更好地理解数据集,发现其中的规律和趋势。同时,行列反转还可以帮助我们将数据可视化,使得图表更加易于理解。 除了常规

对比分析数仓中行列存的特性

摘要:行存表示了一种数据的存储方式,是最传统的一种存储方式。 本文分享自华为云社区《【玩转PB级数仓GaussDB(DWS)】行列存对比的一些事》,作者:sevenjiang。 行存表示了一种数据的存储方式,是最传统的一种存储方式。对于GaussDB(DWS)来说可以认为其表示存储引擎的基础实现,在

VTable——不只是高性能的多维数据分析表格

导读 VTable: 不只是高性能的多维数据分析表格,更是行列间创作的方格艺术家! VTable是字节跳动开源可视化解决方案 VisActor 的组件之一。 在现代应用程序中,表格组件是不可或缺的一部分,它们能够快速展示大量数据,并提供良好的可视化效果和交互体验。VTable是一款基于可视化渲染引擎

给公众号接入`FastWiki`智能AI知识库,让您的公众号加入智能行列

最近由于公众号用户太多,我就在思考有啥方式能给微信公众号的粉丝提供更多的更好的服务?这个时候我就想是否可以给公众号接入一下AI?让用户跟微信公众号对话,然后还能回到用户的问题,并且我提供一些资料让AI帮我回复用户的信息? 这个时候刚刚好我们的FastWiki项目满足了部分需求,然后我们就顺便加入了微