特征多项式
特征多项式的定义¶
我们考虑一个
其中
特征多项式的求法¶
若
那么
可轻松求得,下三角矩阵也是类似的。但如果
相似变换¶
对于
我们说
考虑
得证,且我们发现
使用高斯消元进行相似变换¶
对
- 将
B i j B\mapsto P_{ij}B - 将
B i k\neq 0 B\mapsto D_i(k)B - 将
B j k i B\mapsto T_{ij}(k)B
对于
其中两个
其中
其中
若我们记
P_{ij}=I_n-E_{ii}-E_{jj}+E_{ij}+E_{ji} D_i(k)=I_n+(k-1)E_{ii} T_{ij}(k)=I_n+kE_{ij}
易验证其逆矩阵。
我们在对矩阵使用上述操作(左乘初等矩阵)后再右乘其逆矩阵即相似变换,左乘为行变换,易发现右乘即列变换。
若我们能将矩阵通过相似变换变为上三角或下三角的形式,那么可以轻松求出其特征多项式。但我们发现若对主对角线上的元素应用变换
后文将说明我们对次对角线上的元素应用变换后得到的矩阵依然可以轻松得到其特征多项式。
上 Hessenberg 矩阵¶
对于
的矩阵我们称为上 Hessenberg 矩阵,其中
我们使用相似变换将次对角线以下的元素消为零后即能得到上 Hessenberg 矩阵,而求出一个
我们记
在计算行列式时我们一般选择按零最多的行或列余子式展开,余子式即删除了当前选择的元素所在行和列之后的矩阵,在这里我们选择按最后一行进行展开,有
观察并归纳,对
至此完成了整个算法,该算法一般被称为 Hessenberg 算法。
应用¶
在信息学中我们一般考虑
参考实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #include <cassert>
#include <iostream>
#include <random>
#include <vector>
typedef std::vector<std::vector<int>> Matrix;
typedef long long i64;
Matrix to_upper_Hessenberg(const Matrix &M, int mod) {
Matrix H(M);
int n = H.size();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if ((H[i][j] %= mod) < 0) H[i][j] += mod;
}
}
for (int i = 0; i < n - 1; ++i) {
int pivot = i + 1;
for (; pivot < n; ++pivot) {
if (H[pivot][i] != 0) break;
}
if (pivot == n) continue;
if (pivot != i + 1) {
for (int j = i; j < n; ++j) std::swap(H[i + 1][j], H[pivot][j]);
for (int j = 0; j < n; ++j) std::swap(H[j][i + 1], H[j][pivot]);
}
for (int j = i + 2; j < n; ++j) {
for (;;) {
if (H[j][i] == 0) break;
if (H[i + 1][i] == 0) {
for (int k = i; k < n; ++k) std::swap(H[i + 1][k], H[j][k]);
for (int k = 0; k < n; ++k) std::swap(H[k][i + 1], H[k][j]);
break;
}
if (H[j][i] >= H[i + 1][i]) {
int q = H[j][i] / H[i + 1][i], mq = mod - q;
for (int k = i; k < n; ++k)
H[j][k] = (H[j][k] + i64(mq) * H[i + 1][k]) % mod;
for (int k = 0; k < n; ++k)
H[k][i + 1] = (H[k][i + 1] + i64(q) * H[k][j]) % mod;
} else {
int q = H[i + 1][i] / H[j][i], mq = mod - q;
for (int k = i; k < n; ++k)
H[i + 1][k] = (H[i + 1][k] + i64(mq) * H[j][k]) % mod;
for (int k = 0; k < n; ++k)
H[k][j] = (H[k][j] + i64(q) * H[k][i + 1]) % mod;
}
}
}
}
return H;
}
std::vector<int> get_charpoly(const Matrix &M, int mod) {
Matrix H(to_upper_Hessenberg(M, mod));
int n = H.size();
std::vector<std::vector<int>> p(n + 1);
p[0] = {1 % mod};
for (int i = 1; i <= n; ++i) {
const std::vector<int> &pi_1 = p[i - 1];
std::vector<int> &pi = p[i];
pi.resize(i + 1, 0);
int v = mod - H[i - 1][i - 1];
if (v == mod) v -= mod;
for (int j = 0; j < i; ++j) {
pi[j] = (pi[j] + i64(v) * pi_1[j]) % mod;
if ((pi[j + 1] += pi_1[j]) >= mod) pi[j + 1] -= mod;
}
int t = 1;
for (int j = 1; j < i; ++j) {
t = i64(t) * H[i - j][i - j - 1] % mod;
int prod = i64(t) * H[i - j - 1][i - 1] % mod;
if (prod == 0) continue;
prod = mod - prod;
for (int k = 0; k <= i - j - 1; ++k)
pi[k] = (pi[k] + i64(prod) * p[i - j - 1][k]) % mod;
}
}
return p[n];
}
bool verify(const Matrix &M, const std::vector<int> &charpoly, int mod) {
if (mod == 1) return true;
int n = M.size();
std::vector<int> randvec(n), sum(n, 0);
std::mt19937 gen(std::random_device{}());
std::uniform_int_distribution<int> dis(1, mod - 1);
for (int i = 0; i < n; ++i) randvec[i] = dis(gen);
for (int i = 0; i <= n; ++i) {
int v = charpoly[i];
for (int j = 0; j < n; ++j) sum[j] = (sum[j] + i64(v) * randvec[j]) % mod;
std::vector<int> prod(n, 0);
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
prod[j] = (prod[j] + i64(M[j][k]) * randvec[k]) % mod;
}
}
randvec.swap(prod);
}
for (int i = 0; i < n; ++i)
if (sum[i] != 0) return false;
return true;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, mod;
std::cin >> n >> mod;
Matrix M(n, std::vector<int>(n));
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) std::cin >> M[i][j];
std::vector<int> charpoly(get_charpoly(M, mod));
for (int i = 0; i <= n; ++i) std::cout << charpoly[i] << ' ';
assert(verify(M, charpoly, mod));
return 0;
}
|
上述 Hessenberg 算法不具有数值的稳定性,所以
我们可以将特征多项式与常系数齐次线性递推联系起来,也可结合 Cayley-Hamilton 定理、多项式取模加速一些域上求矩阵幂次的算法。
Cayley-Hamilton 定理指出
其中
若我们要求
而
令
参考文献¶
- Rizwana Rehman, Ilse C.F. Ipsen.La Budde’s Method for Computing Characteristic Polynomials.
- Marshall Law.Computing Characteristic Polynomials of Matrices of Structured Polynomials.
- Mike Paterson.On the Number of Nonscalar Multiplications Necessary to Evaluate Polynomials.
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用