一个 O(n^3) 计算矩阵特征多项式的算法
先看一个粗暴的朴素算法
首先是矩阵特征多项式的定义:
最直接的做法就是直接按行展开矩阵行列式并递归计算。这样是一个 的暴力算法,显然是不能接受的。
再看一个多项式复杂度的简单算法
由朴素算法可知, 是一个 阶多项式,于是可以带入 个点进行插值(例如 )。然后 地求出一个矩阵的行列式,总体的复杂度就是 了。
最后是一个优秀的 n^3 算法
先是化简矩阵的部分
由于我本人是一个脏脏的 CS 人,所以我并不打算从 householder-transform 的角度下手
其实是看不懂。
由相似的性质知,若 则 的特征多项式相同。如果能使 满足某些性质能更轻松地求出特征多项式,就能借助求解 的特征多项式得到 的特征多项式。
于是考虑通过初等变换来构造 :
- 行变化: ,互换第 行、第 列。
- 行倍乘: ,第 行乘以 ,第 列乘以
- 行倍加:,第 行加上 倍的第 行,第 列减去 倍的第 列。
由于每次对第 行的操作都会影响到第 列,所以考虑将 化简为 Upper-Hessenberg 矩阵 上海堡垒矩阵,而非常见的上三角矩阵。
这样处理第 列时,对应的主元在第 行,就不会因为行倍加操作影响到第 列了。
那么如何计算上海森堡矩阵的特征多项式呢
由于 和 特征多项式一致,所以我们只要算出 的特征多项式即可。
令第 行到第 行,第 列到第 列相交形成的矩阵的特征多项式为 。特别的,令 。则按照第一列展开可以得到:
这样直接后往前计算 ,复杂度也是 的。加上前面化简矩阵,整体的复杂度就是 了。
一个参考代码:
#include <iostream>
#include <vector>
#include <algorithm>
typedef std::vector<std::vector<double>> matrix;
typedef std::vector<double> polynomial;
void swapLine(matrix &A, int i, int j) {
std::swap(A[i], A[j]);
}
void swapColumn(matrix &A, int i, int j) {
for (auto& k : A) {
std::swap(k[i], k[j]);
}
}
void addLine(matrix &A, int i, int j, double k) {
for (int l = 0; l < A[0].size(); l++) {
A[i][l] += k * A[j][l];
}
}
void addColumn(matrix &A, int i, int j, double k) {
for (auto& l : A) {
l[i] += k * l[j];
}
}
matrix simplify(matrix A) {
int n = static_cast<int>(A.size());
for (int i = 0; i + 1 < n; i++) {
for (int j = i + 1; j < n; j++) {
if (A[j][i] != 0) {
if (j != i + 1) {
swapLine(A, i + 1, j);
swapColumn(A, i + 1, j);
}
break;
}
}
if (A[i + 1][i] == 0) {
continue;
}
for (int k = i + 2; k < n; k++) {
double t = A[k][i] / A[i + 1][i];
addLine(A, k, i + 1, -t);
addColumn(A, i + 1, k, t);
}
}
return A;
}
polynomial calcPolynomial(const matrix& A) {
int n = static_cast<int>(A.size());
auto D = std::vector(n + 1, polynomial());
D[n] = {1};
for (int i = n - 1; i >= 0; i--) {
D[i].resize(n - i + 1);
double k = 1;
for (int j = i + 1; j < n; j++) {
k *= A[j][j - 1];
for (int l = 0; l < D[j + 1].size(); l++) {
D[i][l] += ((j - i) % 2 == 0 ? 1 : -1) * A[i][j] * k * D[j + 1][l];
}
}
for (int j = 0; j < D[i + 1].size(); j++) {
D[i][j] += A[i][i] * D[i + 1][j];
D[i][j + 1] -= D[i + 1][j];
}
}
return D[0];
}
int main() {
matrix m = {
{1, 1, 1},
{1, 2, 3},
{1, 3, 6}
};
m = simplify(m);
auto p = calcPolynomial(m);
for (int i = 0; i < p.size(); i++) {
if (i == 0) {
std::cout << p[i];
} else {
std::cout << " + " << p[i] << "x^" << i;
}
}
std::cout << std::endl;
return 0;
}
一个 O(n^3) 计算矩阵特征多项式的算法
https://littlejianch.github.io/compute-characteristic-polynomial-in-On3-time/