矩阵与矩阵快速幂

矩阵与矩阵快速幂

概念

定义矩阵(Matrix)是由 行(Row)列(Column) 组成的 二维矩形数组,其中每个元素称为矩阵的元素(Element)。元素的类型任意。

表示形式

一个 \(m\times n\) 矩阵(\(m\) 行,\(n\) 列)通常用大写字母表示,例如 \(A\),写作:

\[A=\begin{bmatrix} a_{11}&a_{12}&\cdots&a_{1n}\\ a_{21}&a_{22}&\cdots&a_{2n}\\ \vdots&\vdots&\ddots&\vdots\\ a_{m1}&a_{m2}&\cdots&a_{mn} \end{bmatrix} \]

其中 \(a_{ij}\) 表示矩阵 \(A\) 中第 \(i\) 行,第 \(j\) 列的元素。

同型矩阵:两个矩阵的行数和列数必须完全相同(即维度相同)。

特殊矩阵

零矩阵

定义:所有元素均为 \(0\) 的矩阵,记作 \(O_{m\times n}\)

性质

  1. 加法单位元\(A + O = A\)
  2. 乘法吸收性\(AO=O\)

表示

\[O_{2\times 3}=\begin{bmatrix} 0&0&0\\ 0&0&0 \end{bmatrix} \]

单位矩阵

定义:主对角线元素为 \(1\) ,其它元素均为 \(0\) 的方矩阵,记作 \(I_{n}\)

性质

  1. 乘法单位元\(AI = A,IA=A\)
  2. 可逆性\(I^{-1}=I\)

表示

\[I_{3}=\begin{bmatrix} 1&0&0\\ 0&1&0\\ 0&0&1 \end{bmatrix} \]

矩阵的运算

\(A = \begin{bmatrix}a_1&a_2\\a_{3}&a_{4}\\\end{bmatrix}\)\(B = \begin{bmatrix}b_1&b_2\\b_{3}&b_{4}\\\end{bmatrix}\)

矩阵加法

条件同型矩阵

运算规则对应元素相加 \((A+B)_{ij}=A_{ij}+B_{ij}\)

\[A+B=\begin{bmatrix}a_1+b_1&a_2+b_2\\a_3+b_3&a_4+b_4\end{bmatrix} \]

性质

  1. 交换律\(A+B=B+A\)
  2. 结合律\(A+B+C=A+(B+C)\)
for (int i = 1;i <= A.n;i ++) for (int j = 1;j <= A.m;j ++) C[i][j] = A[i][j] + B[i][j];

矩阵减法

条件同型矩阵

运算规则对应元素相减 \((A-B)_{ij}=A_{ij}-B_{ij}\)

\[A-B=\begin{bmatrix}a_1-b_1&a_2-b_2\\a_3-b_3&a_4-b_4\end{bmatrix} \]

for (int i = 1;i <= A.n;i ++) for (int j = 1;j <= A.m;j ++) C[i][j] = A[i][j] - B[i][j];

标量乘法

运算规则:一个标量依次乘上所有元素 \((kA)_{ij}=kA_{ij}\)

\[kA=\begin{bmatrix}ka_1&ka_2\\ka_{3}&ka_{4}\\\end{bmatrix} \]

性质:分配律:\(k(A+B)=kA+kB\)

for (int i = 1;i <= A.n;i ++) for (int j = 1;j <= A.m;j ++) C[i][j] = A[i][j] * k;

矩阵乘法

条件维度匹配:若 \(A\)\(m\times n\) 矩阵,\(B\)\(n\times p\) 矩阵,则乘积 \(AB\)\(m\times p\) 矩阵

运算规则:行 \(\times\) 列再求和 \((AB)_{ij}=\sum\limits_{k=1}^{n}a_{ik}\times a_{kj}\)

\[AB=\begin{bmatrix} a_1\times b_1+a_2\times b_3&a_1\times b_2+a_2\times b_4\\ a_3\times b_1+a_4\times b_3&a_3\times b_2+a_4\times b_4 \end{bmatrix} \]

性质

  1. 结合律\((AB)C=A(BC)\)
  2. 分配律\(A(B+C)=AB+AC\)
  3. 注意满足交换律 \(AB\ne BA\)
for (int i = 1;i <= n;i ++) // A 的行数
  for (int j = 1;j <= m;j ++) // B 的列数
    for (int k = 1;k <= p;k ++) // B 的行数/A 的列数
      C[i][j] += A[i][k] * B[k][j];

矩阵的幂

条件:行数列数相同

运算规则:同矩阵乘法

矩阵快速幂

Matrix pow(Matrix A,ll p) {
  Matrix B(2);
  for (int i = 1;i <= B.n;i ++) B[i][i] = 1ll; // 单位矩阵
  while (p) {
    if (p & 1) B = B * A; // 矩阵乘法,下同
    A = A * A;
    p = p >> 1;
  }
  return B;
}

矩阵矩阵快速幂模板代码:

const int maxn = 105;
const ll mod = 1e9 + 7;
struct Matrix {
	ll a[maxn + 5][maxn + 5];
	int n;
	Matrix (int _n) {
		memset(a,0,sizeof(a));
		n = _n;
	}
	friend Matrix operator * (Matrix A,Matrix B);
	friend Matrix operator ^ (Matrix A,ll p);
}; 
Matrix operator * (Matrix A,Matrix B) { // 乘法
	Matrix C(A.n);
	for (int i = 1;i <= A.n;i ++)
		for (int j = 1;j <= A.n;j ++)
			for (int k = 1;k <= A.n;k ++)
				C.a[i][j] = (C.a[i][j] % mod + (A.a[i][k] % mod * B.a[k][j] % mod) % mod) % mod;
	return C;
}
Matrix operator ^ (Matrix A,ll p) { // 快速幂
	Matrix C(2);
	for (int i = 1;i <= C.n;i ++) C.a[i][i] = 1ll;
	while (p) {
		if (p & 1) C = C * A;
		A = A * A;
		p = p >> 1;
	}
	return C;
}

矩阵快速幂的应用

加速递推

例:求斐波那契数列第 \(n\) 项对 \(10^9+7\) 取模的结果(\(1\le n\le 10^{18}\)

观察递推式 \(f(n) = f(n-1)+f(n-2)\),将它写为矩阵的形式:

\[A=\begin{bmatrix} f(n-1)&f(n-2)\\ 0 & 0 \end{bmatrix}\\ B=\begin{bmatrix} f(n)&f(n-1)\\ 0 & 0 \end{bmatrix}\\ \]

如何通过矩阵运算将矩阵 \(A\) 转换为矩阵 \(B\) 呢?这时,我们可以用矩阵乘法

\(P=\begin{bmatrix} a&b\\c&d \end{bmatrix}\\\)

已知 \(AP=B\)

\[AP=\begin{bmatrix} af(n-1)+cf(n-2)&bf(n-1)+df(n-2)\\ 0+0&0+0 \end{bmatrix}\\ f(n)=f(n-1)+f(n-2) \]

所以:

\[\begin{cases} af(n-1)+cf(n-2)=f(n)\\\\ bf(n-1)+df(n-2)=f(n-1) \end{cases} \]

显然,\(a=b=c=1,d=0\)

所以 \(P=\begin{bmatrix} 1&1\\1&0 \end{bmatrix}\\\)

将乘法操作连续进行可以得到:\(f(n)=AP^{n-2}\)

其中 \(A\) 为初始矩阵:\(f(1)=f(2)=1\)

可以发现:\(P\)第一列为递推式系数,后面的列则是决定保留前一个式子中的项

// 矩阵模板(省)……
Matrix A(2),B(2);
void init() { // A初始矩阵和B变换矩阵
	A.a[1][1] = A.a[1][2] = 1ll;
	B.a[1][1] = B.a[1][2] = B.a[2][1] = 1ll; 
}
int main() {
  init();
  ll n; scanf("%lld",&n);
  if (n <= 2ll) {
  	printf("1");
  	return 0;
	}
	B = B ^ (n - 2ll); // 求解
	A = A * B;
	printf("%lld",A.a[1][1]);
	return 0;
}

路径方案数

例:给定一个有向图,求两点之间经过 \(m\) 条边到达的总方案数(可重复走)

我们先将有向图转化为邻接矩阵 \(G\),这时 \(G_{ij}\) 表示从 \(i\)\(j\) 的连通情况

\(m = 1\) 时:\(i\rightarrow j\) 的路径条数正好是 \(G_{ij}\),也就是直连点。

\(m = 2\) 时:\(i\rightarrow k\rightarrow j\) 经过中间节点 \(k\),路径方案数为 (\(i\rightarrow k\) 的方案数)\(\times\)(\(k\rightarrow j\) 的方案数),而 \(k\) 可以是任意节点,所以:

\[G_{ij}=\sum_{k=1}^n G_{ik}\times G_{kj} \]

这个式子则与矩阵乘法完全相同

再进行推导,当 \(m=m\) 时:\(i\rightarrow k\rightarrow j\),先经过 \(m-1\) 步到达 \(k\),再从 \(k\) 到达 \(j\),显然这两个量都是已知的。

综上,\(i\rightarrow j\) 的方案数为 \((G^m)_{ij}\)

// 矩阵模板(省)……
int n,m;
ll t;
int main() {
  scanf("%d %d",&n,&m);
  Matrix A(n);
  for (int i = 1;i <= m;i ++) {
  	int u,v; scanf("%d %d",&u,&v);
  	A.a[u][v] = 1ll;
	}
	scanf("%lld",&t); // 走 t 步
	A = A ^ t;
	for (int i = 1;i <= A.n;i ++) {
  	for (int j = 1;j <= A.n;j ++) printf("%lld ",A.a[i][j]);
    printf("\n");
  }
	return 0;
}

posted @ 2025-05-17 21:59  nightmare_lhh  阅读(24)  评论(0)    收藏  举报