det(A+Bx) 及其推广

矩阵的特征多项式

对于一个矩阵 \(\mathbf A\),其特征多项式为 \(f_{\mathbf A}(\lambda) = \det(\lambda \mathbf I - \mathbf A)\)

注意到 \(\det(\lambda \mathbf I - \mathbf A) = \det(\mathbf P (\lambda \mathbf I - \mathbf A) \mathbf P^{-1}) = \det(\lambda \mathbf I - \mathbf P\mathbf A \mathbf P^{-1})\),这就是说我们可以通过相似变换将 \(\mathbf A\) 相似成上海森堡矩阵。

对于上海森堡矩阵求行列式,这个是经典问题,可以 \(O(n^3)\) DP 求出答案。

std::vector<Z> charPoly(std::vector<std::vector<Z>> A) {
	const size_t n = A.size();
	for (size_t i = 0; i < n; i ++) {
		for (size_t j = 0; j < n; j ++) {
			A[i][j] = -A[i][j];
		}
	}
	for (size_t i = 0; i + 2 < n; i ++) {
		size_t pivot = i + 1;
		for (; pivot < n && !A[pivot][i]; ++ pivot);
		if (pivot == n) {
			continue;
		}
		if (pivot > i + 1) {
			for (size_t j = i; j < n; j ++) {
				std::swap(A[i + 1][j], A[pivot][j]);
			}
			for (size_t j = 0; j < n; j ++) {
				std::swap(A[j][i + 1], A[j][pivot]);
			}
		}
		const Z inv = A[i + 1][i].inv();
		for (size_t j = i + 2; j < n; j ++) {
			if (A[j][i]) {
				const Z t = A[j][i] * inv;
				for (size_t k = i; k < n; k ++) {
					A[j][k] -= t * A[i + 1][k];
				}
				for (size_t k = 0; k < n; k ++) {
					A[k][i + 1] += t * A[k][j];
				}
			}
		}
	}
	std::vector<std::vector<Z>> dp(n + 1);
	dp[0] = {Z(1)};
	for (size_t i = 0; i < n; i ++) {
		dp[i + 1].assign(i + 2, Z(0));
		for (size_t k = 0; k <= i; k ++) {
			dp[i + 1][k + 1] = dp[i][k];
		}
		for (size_t k = 0; k <= i; k ++) {
			dp[i + 1][k] += A[i][i] * dp[i][k];
		}
		Z prod = 1;
		for (size_t j = i; j; j --) {
			prod *= -A[j][j - 1];
			const Z t = prod * A[j - 1][i];
			for (size_t k = 0; k < j; k ++) {
				dp[i + 1][k] += t * dp[j - 1][k];
			}
		}
	}
	return dp[n];
}

\(\det(\mathbf A + \mathbf Bx)\)

想法是把它转化成特征多项式问题,也就是说我们需要把 \(\mathbf B\) 消成 \(\mathbf I\)

直接高消的话会有一点小问题,就是如果有一行的 \(\mathbf B\) 全是 \(0\),那你就消不动了。解决方法是把这一行乘 \(x\),然后用 \(B\) 的前 \(i - 1\) 行把这一行的前 \(i - 1\) 列消掉,最后把答案除以 \(x\)。一直进行直到此行不全 \(0\),或除的 \(x\) 次数 \(>n\)

高消完成之后剩下的就是特征多项式问题,总时间复杂度 \(O(n^3)\)

\(\det(\mathbf A_0 + \mathbf A_1 x + \mathbf A_2 x^2 + \cdots + \mathbf A_k x^k)\)

拉格朗日插值

首先我们知道答案一定是 \(nk\) 次多项式,所以我们可以带入 \(nk\) 个点值然后拉插。

时间复杂度 \(O(n^4k + n^3k^2)\)

转化为特征多项式

同样地,先把 \(\mathbf A_k\) 消成 \(\mathbf I\),这样问题就转化为了求 \(\det(x^k \mathbf I - \mathbf F_1 x^{k - 1} - \cdots - \mathbf F_k)\)

如果记:

\[\mathbf A = \begin{bmatrix} 0 & \mathbf{I} & & & \\ 0 & 0 & \mathbf{I} & & \\ \vdots & \vdots & \ddots & \ddots & \\ 0 & 0 & \cdots & 0 & \mathbf{I} \\ \mathbf{F}_k & \mathbf{F}_{k-1} & \cdots & \mathbf{F}_2 & \mathbf{F}_1 \end{bmatrix} \]

那么我们就有 \(\det(x^m \mathbf I - \mathbf F_1 x^{m - 1} - \cdots - \mathbf F_k) = \det(x \mathbf I - \mathbf A)\)

\(\text{Proof}\):首先我们知道:

\[x\mathbf{I} - \mathbf{A} = \begin{bmatrix}x\mathbf{I} & -\mathbf{I} & & & \\0 & x\mathbf{I} & -\mathbf{I} & & \\\vdots & \vdots & \ddots & \ddots & \\0 & 0 & \cdots & x\mathbf{I} & -\mathbf{I} \\-\mathbf{F}_k & -\mathbf{F}_{k-1} & \cdots & -\mathbf{F}_2 & x\mathbf{I} - \mathbf{F}_1\end{bmatrix} \]

可以将矩阵分块为:

\[x\mathbf{I} - \mathbf{A} = \begin{bmatrix}\mathbf{P} & \mathbf{Q} \\\mathbf{R} & \mathbf{S}\end{bmatrix} \]

\(\mathbf P\) 是左上角的 \((k - 1)n \times (k - 1)n\) 的分块矩阵:

\[\mathbf{P} = \begin{bmatrix}x\mathbf{I} & -\mathbf{I} & & \\0 & x\mathbf{I} & \ddots & \\\vdots & \ddots & \ddots & -\mathbf{I} \\0 & \cdots & 0 & x\mathbf{I}\end{bmatrix} \]

\(\mathbf Q\) 是右上角的 \((k - 1)n \times n\) 的分块矩阵:

\[\mathbf{Q} = \begin{bmatrix}0 \\\vdots \\0 \\-\mathbf{I}\end{bmatrix} \]

\(\mathbf R\) 是左下角 \(n \times (k - 1) n\) 的分块矩阵:

\[\mathbf{R} = \begin{bmatrix}-\mathbf{F}_k & -\mathbf{F}_{k-1} & \cdots & -\mathbf{F}_2\end{bmatrix} \]

\(\mathbf S\) 是右下角的 \(n \times n\) 的分块矩阵:

\[\mathbf{S} = x\mathbf{I} - \mathbf{F}_1 \]

显然 \(\mathbf P\) 是可逆的,那么行列式就可以表示为:

\[\det(x \mathbf I - \mathbf A) = \det(\mathbf P) \cdot \det(\mathbf S - \mathbf R \mathbf P^{-1}\mathbf Q) \]

首先可以简单得到:

\[\mathbf{P}^{-1} = \begin{bmatrix}x^{-1}\mathbf{I} & x^{-2}\mathbf{I} & x^{-3}\mathbf{I} & \cdots \\0 & x^{-1}\mathbf{I} & x^{-2}\mathbf{I} & \cdots \\\vdots & \ddots & \ddots & \vdots \\0 & \cdots & 0 & x^{-1}\mathbf{I}\end{bmatrix} \]

然后可以计算得出:

\[\begin{aligned}\mathbf{R} \mathbf{P}^{-1} \mathbf{Q} &= \mathbf{F}_k x^{-k} + \mathbf{F}_{k-1} x^{-(k-1)} + \cdots + \mathbf{F}_2 x^{-2}\\ \mathbf{S} - \mathbf{R} \mathbf{P}^{-1} \mathbf{Q} &= x\mathbf{I} - \mathbf{F}_1 - \mathbf{F}_k x^{-k} - \mathbf{F}_{k-1} x^{-(k-1)} - \cdots - \mathbf{F}_2 x^{-2}\end{aligned} \]

由于 \(\mathbf P\) 是分块上三角矩阵,那么就有 \(\det(\mathbf P) = x^{n(k - 1)}\),这样就能得到:

\[\begin{aligned}\det(x \mathbf I - \mathbf A) &= x^{n(k - 1)} \cdot \det\left(x\mathbf{I} - \sum_{1 \le i \le k} \mathbf{F}_i x^{-(i-1)}\right) \\&= \det\left(x^k \mathbf{I} - \mathbf{F}_1 x^{k-1} - \mathbf{F}_2 x^{k-2} - \cdots - \mathbf{F}_k\right) \\\end{aligned} \]

这样时间复杂度是 \(O((nk)^3)\) 的。

std::vector<Z> detPoly(std::vector<std::vector<std::vector<Z>>> A) {
	assert(A.size());
	const int m = A.size() - 1, n = A[0].size();
	Z prod = Z(1); int offset = 0;
	for (int h = 0; h < n; h ++) {
		for (; ; ) {
			if (A[m][h][h]) {
				break;
			}
			for (int j = h + 1; j < n; j ++) {
				if (A[m][h][j]) {
					prod = -prod;
					for (int d = 0; d <= m; d ++) {
						for (int i = 0; i < n; i ++) {
							std::swap(A[d][i][h], A[d][i][j]);
							break;
						}
					}
				}
			}
			if (A[m][h][h]) {
				break;
			}
			if (++ offset > n * m) {
				return std::vector<Z>(n * m + 1, Z(0));
			}
			for (int d = m; -- d >= 0; ) {
				for (int j = 0; j < n; j ++) {
					A[d + 1][h][j] = A[d][h][j];
				}
			}
			for (int j = 0; j < n; j ++) {
				A[0][h][j] = Z(0);
			}
			for (int i = 0; i < h; i ++) {
				const Z t = A[m][h][i];
				for (int d = 0; d <= m; d ++) {
					for (int j = 0; j < n; j ++) {
						A[d][h][j] -= t * A[d][i][j];
					}
				}
			}
		}
		prod *= A[m][h][h];
		const Z inv = A[m][h][h].inv();
		for (int d = 0; d <= m; d ++) {
			for (int j = 0; j < n; j ++) {
				A[d][h][j] *= inv;
			}
		}
		for (int i = 0; i < n; i ++) {
			if (h != i) {
				const Z t = A[m][i][h];
				for (int d = 0; d <= m; d ++) {
					for (int j = 0; j < n; j ++) {
						A[d][i][j] -= t * A[d][h][j];
					}
				}
			}
		}
	}
	std::vector<std::vector<Z>> B(n * m, std::vector<Z>(n * m, Z(0)));
	for (int i = 0; i < (m - 1) * n; i ++) {
		B[i][n + i] = Z(1);
	}
	for (int d = 0; d < m; d ++) {
		for (int i = 0; i < n; i ++) {
			for (int j = 0; j < n; j ++) {
				B[(m - 1) * n + i][d * n + j] = -A[d][i][j];
			}
		}
	}
	const std::vector<Z> f = charPoly(B);
	std::vector<Z> g(n * m + 1, Z(0));
	for (int i = 0; i + offset <= n * m; i ++) {
		g[i] = prod * f[i + offset];
	}
	return g;
}
posted @ 2025-05-18 17:48  definieren  阅读(19)  评论(1)    收藏  举报