行列式介绍及Eigen/OpenCV/C++的三种实现
行列式,記作det(A),是一個將方陣A映射到實數的函數。行列式等于矩陣特征值的乘積。行列式的絕對值可以用來衡量矩陣參與矩陣乘法后空間擴大或者縮小了多少。如果行列式是0,那么空間至少沿著某一維完全收縮了,使其失去了所有的體積。如果行列式是1,那么這個轉換保持空間體積不變。
行列式(Determinant)是數學中的一個函數,將一個n*n的矩陣A映射到一個標量,記作det(A)或|A|。行列式可以看作是有向面積或體積的概念在一般的歐幾里得空間中的推廣。或者說,在n維歐幾里得空間中,行列式描述的是一個線性變換對”體積”所造成的影響。
對于簡單的2階和3階的矩陣,行列式的表達式相對簡單,而且恰好是每條主對角線(左上至右下)元素乘積之和減去每條副對角線(右上至左下)元素乘積之和。
一個n階方塊矩陣A的行列式可直觀地定義如下,來自于維基百科:
按照拉普拉斯公式進行遞推計算:
對一個n階的行列式M,去掉M的第i行第j列后形成的n-1階的行列式叫做M關于元素mij的余因式(又稱余子式),記作Mij。
M關于元素mij的代數余子式記作Cij:Cij=(-1)(i+j)*Mij。
一個n階的行列式M可以寫成一行(或一列)的元素與對應的代數余子式的乘積之和,叫做行列式按一行(或一列)的展開,如:
det(A)=a11C11+a21C21+…+an1Cn1?或 ?det(A)=a11C11+a12C12+…+a1nC1n
這個公式又稱拉普拉斯公式,把n維矩陣的行列式計算變成了n個n-1維的行列式的計算。另一方面,拉普拉斯公式可以作為行列式的一種歸納定義。
行列式性質:
(1)、單位矩陣的行列式為1,若矩陣的某幾行線性相關,則它的行列式為零。
(2)、一個矩陣的行列式等于它的轉置矩陣的行列式。
(3)、在行列式中,一行(列)元素全為0,則此行列式的值為0。
(4)、在行列式中,某一行(列)有公因子k,則可以提出k。
(5)、在行列式中,某一行(列)的每個元素是兩數之和,則此行列式可拆分為兩個相加的行列式。
(6)、行列式中的兩行(列)互換,改變行列式正負符號。
(7)、在行列式中,有兩行(列)對應成比例或相同,則此行列式的值為0。
(8)、將一行(列)的k倍加進另一行(列)里,行列式的值不變。注:一行(列)的k倍加上另一行(列),行列式的值改變。
(9)、將行列式的行列互換,行列式的值不變,其中行列互換相當于轉置。這個性質可以簡單地記作:D=|aij|=|aji|=DT
(10)、行列式的乘法定理:方塊矩陣的乘積的行列式等于行列式的乘積:det(AB) = det(A)det(B)
(11)、若A是可逆矩陣,det(A-1)=(det(A))-1
(12)、若兩個矩陣相似,那么它們的行列式相同。
(13)、行列式是所有特征值(按代數重數計)的乘積。
行列式的計算:(1)、按照定義公式;(2)、按照拉普拉斯公式進行遞歸計算;(3)、利用高斯消去法;(4)、LU分解法。
以下測試代碼是采用Eigen實現的行列式計算:
int test_mat_determinant()
{std::vector<float> vec{ 1, 0, 2, -1, 3, 0, 0, 5, 2, 1, 4, -3, 1, 0, 5, 0 };Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> map(vec.data(), 4, 4);double det = map.determinant();fprintf(stderr, "det: %f\n", det);return 0;
}
執行結果如下:
以下是分別采用OpenCV和C++實現的行列式計算:
#include "funset.hpp"
#include <math.h>
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>static double determinant_opencv(const std::vector<float>& vec)
{int length = std::sqrt(vec.size());cv::Mat mat(length, length, CV_32FC1, const_cast<float*>(vec.data()));// In OpenCV, for small matrices(rows=cols<=3),the direct method is used.// For larger matrices the function uses LU factorization with partial pivoting.return cv::determinant(mat);
}template<typename _Tp>
static _Tp det(const std::vector<std::vector<_Tp>>& mat, int N)
{if (mat.size() != N) {fprintf(stderr, "mat must be square matrix\n");return -1;}for (int i = 0; i < mat.size(); ++i) {if (mat[i].size() != N) {fprintf(stderr, "mat must be square matrix\n");return -1;}}_Tp ret{ 0 };if (N == 1) return mat[0][0];if (N == 2) {return (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]);} else {// first colfor (int i = 0; i < N; ++i) {std::vector<std::vector<_Tp>> m(N - 1);std::vector<int> m_rows;for (int t = 0; t < N; ++t) {if (i != t) m_rows.push_back(t);}for (int x = 0; x < N - 1; ++x) {m[x].resize(N - 1);for (int y = 0; y < N - 1; ++y) {m[x][y] = mat[m_rows[x]][y + 1];}}int sign = (int)pow(-1, 1 + i + 1);ret += mat[i][0] * sign * det<_Tp>(m, N-1);}}return ret;
}int test_determinant()
{std::vector<float> vec{ 1, 0, 2, -1, 3, 0, 0, 5, 2, 1, 4, -3, 1, 0, 5, 0};const int N{ 4 };if (vec.size() != (int)pow(N, 2)) {fprintf(stderr, "vec must be N^2\n");return -1;}double det1 = determinant_opencv(vec);std::vector<std::vector<float>> arr(N);for (int i = 0; i < N; ++i) {arr[i].resize(N);for (int j = 0; j < N; ++j) {arr[i][j] = vec[i * N + j];}}double det2 = det<float>(arr, N);fprintf(stderr, "det1: %f, det2: %f\n", det1, det2);return 0;
}
執行結果如下:
經測試,使用C++實現的行列式的計算與調用OpenCV和Eigen接口實現的結果是一致的。
GitHub:
https://github.com/fengbingchun/NN_Test
https://github.com/fengbingchun/Eigen_Test
總結
以上是生活随笔為你收集整理的行列式介绍及Eigen/OpenCV/C++的三种实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gtest使用初级指南
- 下一篇: Caffe中计算图像均值的实现(cifa