矩阵乘法&高斯消元

Posted on 2025-04-18 23:18  K_J_M  阅读(65)  评论(0)    收藏  举报

矩阵部分

矩阵乘法

两个矩阵做乘法运算时需要保证 \(A\) 的列等于 \(B\) 的行。

换句话来说,一个 \(n\times m\) 的矩阵 \(A\) 与一个 \(m\times k\) 的矩阵 \(B\) 相乘得到大小为 \(n\times k\) 的矩阵。

定义:

\[C_{i,j}=\sum_{k=1}^{m}A_{i,k}\times B_{k,j} \]

矩阵乘法运算律

\[A\times B≠B\times A \]

\[(A\times B) \times C=A\times (B \times C) \]

\[A\times [B \ C]=[A\times B \ A\times C] \]

其中 \([A\ B]\) 表示将矩阵 \(A,B\) 合并。

单位矩阵

\(n\) 阶单位矩阵 \(I\) 定义为:

\[\begin{pmatrix} 1,0,0\dots 0\\ 0,1,0\dots 0\\ 0,0,1\dots 0\\ \dots\\ 0,0,0\dots 1\\ \end{pmatrix}\]

性质:\(A\times I=A\)

初等行变换

这里只讲初等行变换。

初等行变换有三种操作:

将矩阵 \(A\) 的两行交换
将矩阵 \(A\) 的一行乘上同一个数
将矩阵 \(A\) 的一行减去另一行

性质:对矩阵 \(A\) 进行若干次初等行变换相当于在矩阵 \(A\) 的左边乘上一个矩阵 \(B\)

初等矩阵

初等矩阵定义为 \(n\) 阶单位矩阵经过若干次初等行变换能够形成的矩阵。

逆矩阵

若一个 \(n\) 阶矩阵 \(A\),存在一个 \(n\) 阶矩阵 \(B\) 使得 \(A\times B=I\),那么称 \(B\)\(A\) 的逆矩阵。

不是所有矩阵都有逆矩阵,必须是初等矩阵,由定义可知。

矩阵求逆

对于矩阵 \(A\),我们对它进行若干次初等变换后得到 \(I\)。等价于在 \(A\) 左侧乘上一堆矩阵。

\[P_1\times P_2\times \dots \times P_s\times A=I \]

\[A^{-1}\times A=I \]

\[P_1\times P_2\times \dots \times P_s=A^{-1} \]

\[I\times P_1\times P_2\times \dots \times P_s=A^{-1}\times I=A^{-1} \]

联立两个式子

\[P_1\times P_2\times \dots \times P_s\times A=I \]

\[P_1\times P_2\times \dots \times P_s\times I=A^{-1} \]

我们可以将这一坨

\[P_1\times P_2\times \dots \times P_s \]

想象成进行若干次初等行变换,那么第一个式子相当于告诉我们 \(A\) 经过若干次变换后得到 \(I\)\(I\) 经过相同的操作就能得到 \(A\) 的逆矩阵 \(A^{-1}\)

于是我们可以将 \(A\)\(I\) 放到一起,然后将 \(A\) 变成 \(I\),那么 \(I\) 就变成了 \(A^{-1}\)

具体应用

\(1:\)
\(f_0=0,f_1=1,f_2=1\),然后有

\[f_n=3f_{n-1}+7f_{n-2}+5f_{n-3},n≥3 \]

请用矩阵加速计算。

注意到 \(f_n\)\(f_{n-1},f_{n-2},f_{n-3}\) 有关,那么我们构造矩阵 \(\begin{bmatrix} f_{n-2} & f_{n-1} & f_{n}\end{bmatrix}\),希望乘上一个 \(3\times 3\) 的矩阵得到 \(\begin{bmatrix} f_{n-1} & f_{n} & f_{n+1}\end{bmatrix}\)

由于 \(f_{n-1}=0f_{n-2}+f_{n-1}+0f_n\),所以第一列构造 \(0,1,0\)

同理我们可以依次构造出 \(\begin{bmatrix} 0 & 0 & 5\\1 & 0 & 7\\0 & 1 & 3\end{bmatrix}\)

初始矩阵为 \(\begin{bmatrix} 0,1,1 \end{bmatrix}\),乘上矩阵 \(\begin{bmatrix} 0 & 0 & 5\\1 & 0 & 7\\0 & 1 & 3\end{bmatrix}\) \(n-2\) 次可以得到 \(f_n\),用矩阵快速幂可以做到 \(\log\) 复杂度。

\(2:\)
\(f_0=0,f_1=1\),然后

\[f_n=9f_{n-1}+6f_{n-2}+5,n>1 \]

构造矩阵 \(\begin{bmatrix} 1 & 0 & 1\\0 & 0 & 6\\0 & 1 & 9 \end{bmatrix}\),初始矩阵为 \(\begin{bmatrix} 5,0,1 \end{bmatrix}\) 即可。

\(3:\)

P5175 数列

一个数列 $a_n $,已知 \(a_1\)\(a_2\) 两项。

数列 \(a_n\) 满足递推式 \(a_n=x \times a_{n-1}+ y \times a_{n-2}(n≥3).\)

\(\sum_{i=1}^na_i^2\)

由于答案可能过大,对 \(10^9+7\) 取模。

此题的难点在于 \(a_i\) 多了一个平方。

但是题目给了我们 \(a_i\) 的通式,所以我们考虑将 \(a_i\) 展开。

\(\begin{aligned} a_i^2 &= (x\times a_{i-1}+y\times a_{i-2})^2 \\ &=x^2\times a_{i-1}^2+y^2\times a_{i-2}^2+2\times x\times y\times a_{i-1}\times a_{i-2}\\ \end{aligned}\)
发现展开项包括 \(a_i^2,a_{i-1}^2,a_ia_{i-1}\),因此考虑构造矩阵 \(\begin{bmatrix} a_{i-1}^2,a_{i}^2,a_ia_{i-1} \end{bmatrix}\)
由于 \(a_{i+1}=x\times a_i+y\times a_{i-1}\)
所以 \(a_ia_{i+1}=a_i(x\times a_{i}+y\times a_{i-1})=x\times a_i^2+y\times a_{i-1}\times a_i\)
那么我们需要乘上矩阵 \(\begin{bmatrix} 0 & y^2 & 0\\ 1 & x^2 & x^2\\0 & 2xy & y \end{bmatrix}\) 来得到矩阵 \(\begin{bmatrix} a_{i}^2,a_{i+1}^2,a_{i+1}a_i \end{bmatrix}\)
但是这样还不够,因为我们要求的是 \(\sum_{i=1}^{n}a_i^2\),所以考虑在矩阵前面加上一个 \(ans_i\) 表示 \(\sum_{j=1}^{i}a_j^2\)
综上,我们的初始矩阵为 \(\begin{bmatrix} ans_{i-1},a_{i-1}^2,a_i^2,a_ia_{i-1} \end{bmatrix}\),操作矩阵为 \(\begin{bmatrix} 1 & 0 & 0 & 0\\0 & 0 & y^2 & 0\\1 & 1 & x^2 & x^2\\0 & 0 & 2xy & y \end{bmatrix}\),然后就可以矩阵快速幂了。

\(4:\)

P5136 sequence

题目描述

Wolfycz非常喜欢研究数列,同时他也喜欢研究黄金分割率,有一天Wolfycz写下了一个数列,他令\(A_i=\lceil(\dfrac{\sqrt{5}+1}{2})^i\rceil\),但是Wolfycz并不知道\(A_n\)的值,所以希望你来帮帮他

考虑构造方程使得 \(\dfrac{\sqrt{5}+1}{2}\)\(\dfrac{-\sqrt{5}+1}{2}\) 是这个方程的两个解。

方程构造为 \(x^2-x-1=0\),那么 \(x^2=x+1\),两边同时乘上 \(x^{n-2}\) 可以得到 \(x^n=x^{n-1}+x^{n-2}\),然后记 \(f(i)=(\dfrac{\sqrt{5}+1}{2})^i+(\dfrac{-\sqrt{5}+1}{2})^i\),这样做的意义是使得 \(f(i)\) 是整数。那么 \(f(i)=f(i-1)+f(i-2)\),其中 \(f(0)=2,f(1)=1\)

我们可以通过矩阵加速快速计算。

构造矩阵 \(\begin{bmatrix} f_{i-1},f_i \end{bmatrix}\),将这个矩阵乘上 \(\begin{bmatrix} 0 & 1\\1 & 1 \end{bmatrix}\) 可以得到 \(\begin{bmatrix} f_{i},f_{i+1} \end{bmatrix}\)

这样我们就可以快速计算 \(f_n\)

但是我们要求 \(\lceil(\dfrac{\sqrt{5}+1}{2})^i\rceil\),注意到 \(-1<\dfrac{-\sqrt{5}+1}{2}<0\),那么当 \(i\bmod 2=0\) 时,\(0<(\dfrac{-\sqrt{5}+1}{2})^i<1\),此时我们的答案不变。同理,我们可以求出 \(i\bmod 2=1\) 时的答案为 \(f(n)-1\),这样就完成了这道题。

P3263 [JLOI2015] 有意义的字符串

这道题和 sequence 差不多,我们同样只需要构造方程,在构造函数,最后分情况讨论即可,因此不在赘述。

P4783 【模板】矩阵求逆

这是一道板子题,直接放代码吧。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 810;
const int mod = 1e9+7;
int n,a[N][N];
int qpow(int a,int p,int m){
	int res=1;
	while(p){
		if(p&1) res=res*a%m;
		a=a*a%m;
		p>>=1;
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i){
		a[i][i+n]=1;
		for(int j=1;j<=n;++j){
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=n;++i){
		int m=i;
		for(int j=1;j<=n;++j){
			if(j<i&&a[j][j]) continue;
			if(a[j][i]>a[m][i]) m=j;
		}
		if(!a[m][i]){
			puts("No Solution");
			return 0;
		}
		swap(a[i],a[m]);
		int mul=qpow(a[i][i],mod-2,mod);
		for(int j=1;j<=2*n;++j) a[i][j]=a[i][j]*mul%mod;
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			int d=a[j][i];
			for(int k=i;k<=2*n;++k){
				a[j][k]=((a[j][k]-a[i][k]*d%mod)%mod+mod)%mod;
			}
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=n+1;j<=2*n;++j){
			cout<<a[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

线性代数部分

求解线性方程组

现在有这样一个方程组
\(\begin{cases} a_{1,1}x_1+a_{1,2}x_2+\dots +a_{1,n}x_n=b_1\\ a_{2,1}x_1+a_{2,2}x_2+\dots +a_{2,n}x_n=b_2\\ \dots\\ a_{n,1}x_1+a_{n,2}x_2+\dots +a_{n,n}x_n=b_n\\ \end{cases}\)
其中 \(a_i,b_i∈ \Z\),求出方程的实数解。

考虑增广矩阵。

\(X=\begin{pmatrix} a_{1,1},a_{1,2}\dots a_{1,n},b_1\\ a_{2,1},a_{2,2}\dots a_{2,n},b_2\\ \dots\\ a_{n,1},a_{n,2}\dots a_{n,n},b_n \end{pmatrix}\)
那么问题化为对 \(X\) 进行若干次初等行变换消元。

高斯-约旦消元

高斯消元太麻烦,这里只讲高斯-约旦消元。

具体步骤是先对于每一列,找到最大的那个数,然后用最大的那个数去消元,原因是方便判断是否全为 0 和精度问题的考虑。

直接放代码

for(int i=1;i<=n;++i){
		int m=i;
		for(int j=1;j<=n;++j){
			if(j<i&&!eq(fabs(a[j][j]),0))  continue;
            //这个判断的原因是如果上面的”钦定“项为0,那么这里的xi被约掉了,也就是说,这个x是可以有无穷多个的,那么我们也可以用这一行的钦定值去消元。
			if(fabs(a[m][i])<fabs(a[j][i])) m=j;
		}
		swap(a[m],a[i]);
		if(eq(fabs(a[i][i]),0)) continue;
        //如果最大值都为0了,那么这一列都是0,那么这里的xi有无穷多种
		double d=a[i][i];
		for(int j=i;j<=n+1;++j) a[i][j]/=d;
        //先将这一列除一个d,使得系数为1
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			d=a[j][i];
			for(int k=i;k<=n+1;++k) a[j][k]-=d*a[i][k];//消元
		}
	}

然后是判断无解或者无穷多组解的方法

bool ok=0;
	for(int i=1;i<=n;++i){
		if(eq(fabs(a[i][i]),0)){//这个的系数为0
			ok=1;
			if(!eq(fabs(a[i][n+1]),0)){
                //方程0x!=0
				puts("-1");//注意无解>无穷多解>有唯一解
				return 0;
			}
		}
	}
	if(ok){
		puts("0");
		return 0;
	}

高斯消元求解异或方程组

现在有一个异或方程组
\(\begin{cases} a_{1,1} x_1\oplus a_{1,2}x_2\oplus\dots \oplus a_{1,n}x_n=b_1\\ a_{2,1} x_1\oplus a_{2,2}x_2\oplus\dots \oplus a_{2,n}x_n=b_2\\ \dots\\ a_{n,1} x_1\oplus a_{n,2}x_2\oplus\dots \oplus a_{n,n}x_n=b_n\\ \end{cases}\)
其中 \(a_{i,j},x_k,b_k∈\{0,1\}\)

阁下该如何应对呢?

方法其实很简单,与求解线性方程组一样。考虑到第 \(i\) 列时,找到一个 \(1\) 然后让这一行去消元。

举个例子。
\(\begin{cases} x_1\oplus x_2=1\\ x_2\oplus x_3=1\\ x_1\oplus x_3=0\\ \end{cases}\)
考虑构造增广矩阵
\(\begin{pmatrix} 1,1,0,1\\ 0,1,1,1\\ 1,0,1,0\\ \end{pmatrix}\)
然后让第三行去异或上第一行得到
\(\begin{pmatrix} 1,1,0,1\\ 0,1,1,1\\ 0,1,1,1\\ \end{pmatrix}\)

再让第一行异或上第二行
\(\begin{pmatrix} 1,0,1,0\\ 0,1,1,1\\ 0,1,1,1\\ \end{pmatrix}\)

再让第三行异或上第二行
\(\begin{pmatrix} 1,0,1,0\\ 0,1,1,1\\ 0,0,0,0\\ \end{pmatrix}\)

停止操作。但是发现最后一行全部为 \(0\),说明方程多解。最后的这个矩阵表示的含义为
\(\begin{cases} x_2\oplus x_3=1\\ x_1\oplus x_3=0\\ \end{cases}\)

也就是
\(\begin{cases} x_1=1-t\\ x_2=t\\ x_3=1-t\\ t∈\{0,1\} \end{cases}\)

下面放上代码

for(int i=1;i<=n;++i){
		int m=i;
		for(int j=1;j<=n;++j){
			if(j<i&&matrix[j][j]) continue;
			if(matrix[j][i]>matrix[m][i]) m=j;
		}
		if(!matrix[m][i]) continue;
		swap(matrix[m],matrix[i]);
		for(int j=1;j<=n;++j){
			if(i==j) continue;
			if(matrix[j][i]){
				for(int k=i;k<=n+1;++k) matrix[j][k]^=matrix[i][k];
			}
		}
	}

甚至比普通还简单。。。