Definition of characteristic polynomial
Let's consider a matrix , among which . Its characteristic polynomial is written as Among them
Among them for one the identity matrix. In some places it is defined as differs by only one symbol from our definition , but using this definition we get must be a leading polynomial, and other definitions are only if It is a leading polynomial only when it is an even number. It should be noted that The matrix determinant of is It is well defined.
How to find characteristic polynomials
If matrix is an upper triangular matrix such as
Then
It can be easily obtained, and the lower triangular matrix is similar. But if Does not belong to these two matrices, we need to use similarity transformation to make the matrix into a form that is easy to find the characteristic polynomial.
similarity transformation
for matrix and , when exists the invertible matrix satisfy
we say and Similar, note transformation is a similar transformation. Let us say and have the same characteristic polynomials.
consider
proved, and we found Same thing. In addition , because Therefore 。
Similarity transformation using Gaussian elimination
Yes matrix The three basic operations for performing Gaussian elimination row transformation are
- will of the , Row swap:。
- will of the travel by :。
- will of the OK doubled to Line:。
for These few The elementary matrices of are respectively
two of them respectively in of the and OK, pay attention 。
Among them in of the line of work column, note 。
Among them in of the line of work column, note 。
If we remember for the first line of work The elements of the column are , the rest is zero matrix, then
It is easy to verify its inverse matrix.
After we use the above operation on the matrix (left multiplication of the elementary matrix), we then right-multiply its inverse matrix, which is the similarity transformation. The left multiplication is the row transformation, and it is easy to find that the right multiplication is the column transformation.
If we can transform the matrix into an upper triangular or lower triangular form through similarity transformation, then its characteristic polynomial can be easily obtained. But we found that if we apply a transformation to the elements on the main diagonal will later cause the original pass General line of work The elements of the column are eliminated to zero and then multiplied by the right Coming soon of the column doubled to The operation of column makes it possible that elements that were previously eliminated to zero may now be non-zero, and may not be transformed into upper or lower triangular form.
It will be explained later that the matrix obtained after applying transformation to the elements on the subdiagonal can still easily obtain its characteristic polynomial.
Upper Hessenberg matrix
for shaped like
The matrix is called the upper Hessenberg matrix, where is a sub-diagonal line.
We can obtain the upper Hessenberg matrix by using a similarity transformation to eliminate the elements below the sub-diagonal to zero, and find a The characteristic polynomial of the upper Hessenberg matrix can be found in Time is done.
we remember reserved for only before line and before column matrix, note Then
When calculating the determinant, we generally choose to expand by the cofactor of the row or column with the most zeros. The cosubform is the matrix after deleting the row and column of the currently selected element. Here we choose to expand by the last row, and there is
Observe and summarize, yes Yes
This completes the entire algorithm, which is generally called the Hessenberg algorithm.
Application
In informatics we generally consider matrix, usually is a prime number, it is simple to perform the above similarity transformation, when When it is a composite number, we can consider a method similar to euclidean division.
#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; }
The above Hessenberg algorithm is not numerically stable, so The matrix above needs to be adjusted by other algorithms or switched to other algorithms with numerical stability before use.
We can connect characteristic polynomials with homogeneous linear recursion with constant coefficients, or combine it with the Cayley-Hamilton theorem and polynomial modulo to speed up algorithms for finding matrix powers in some domains.
The Cayley-Hamilton theorem states
Among them for The zero matrix of And for The characteristic polynomial of .
if we ask Among them is larger, then we can find Later use 。
And Obviously. we order And Then
Order Calculations can be found It takes about Submatrix and matrix multiplication.