C++教程(一):超详细的C++矩阵操作和运算(附实例代码,与python对比)

python教程(一):超详细的numpy矩阵操作和运算(附实例代码)

在之前的章节中,我们详细介绍了python中numpy的矩阵操作。但在自动驾驶开发中,我们一般采用C++进行功能开发,因此C++矩阵运算就是自动驾驶开发工程师不可或缺的能力。在C++中没有直接进行矩阵操作的功能函数,需要采用数组或者vector等容器实现,或者引用第三方库,例如Eigen(一个高效的C++模板库,用于矩阵和向量的线性代数运算)、Armadillo(提供简洁语法和高效的矩阵操作,支持线性代数和统计学运算)、Boost uBLAS(Boost库中的矩阵运算模块)。在自动驾驶开发中,我们常用Eigen库,因此本文主要介绍Eigen库的矩阵操作,同时为了方便对比,我们用C++完成之前numpy中所有的操作和运算。

1. 直接运算

1.1 采用数组进行运算

如果矩阵的大小是固定的,简单的矩阵运算可以用C++二维数组来实现。下面是一个简单的矩阵加法、乘法的例子:

#include <iostream>
using namespace std;// 定义矩阵的大小
const int ROW = 2;
const int COL = 2;// 矩阵加法
void addMatrices(int A[ROW][COL], int B[ROW][COL], int result[ROW][COL]) {for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {result[i][j] = A[i][j] + B[i][j];}}
}// 矩阵乘法
void multiplyMatrices(int A[ROW][COL], int B[ROW][COL], int result[ROW][COL]) {for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {result[i][j] = 0;for (int k = 0; k < COL; k++) {result[i][j] += A[i][k] * B[k][j];}}}
}int main() {int A[ROW][COL] = {{1, 2}, {3, 4}};int B[ROW][COL] = {{5, 6}, {7, 8}};int result[ROW][COL];// 矩阵加法addMatrices(A, B, result);cout << "矩阵加法结果:" << endl;for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {cout << result[i][j] << " ";}cout << endl;}// 矩阵乘法multiplyMatrices(A, B, result);cout << "矩阵乘法结果:" << endl;for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {cout << result[i][j] << " ";}cout << endl;}return 0;
}

矩阵加法结果:
6 8 
10 12 
矩阵乘法结果:
19 22 
43 50

1.2 采用vector进行运算

#include <iostream>
#include <vector>using namespace std;// 矩阵加法
void addMatrices(const vector<vector<int>>& A, const vector<vector<int>>& B, vector<vector<int>>& result) {int ROW = A.size();int COL = A[0].size();for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {result[i][j] = A[i][j] + B[i][j];}}
}// 矩阵乘法
void multiplyMatrices(const vector<vector<int>>& A, const vector<vector<int>>& B, vector<vector<int>>& result) {int ROW = A.size();int COL = B[0].size();int commonDim = A[0].size(); // A的列数和B的行数必须相同for (int i = 0; i < ROW; i++) {for (int j = 0; j < COL; j++) {result[i][j] = 0;for (int k = 0; k < commonDim; k++) {result[i][j] += A[i][k] * B[k][j];}}}
}// 打印矩阵
void printMatrix(const vector<vector<int>>& matrix) {for (const auto& row : matrix) {for (const auto& elem : row) {cout << elem << " ";}cout << endl;}
}int main() {// 定义2x2矩阵vector<vector<int>> A = {{1, 2}, {3, 4}};vector<vector<int>> B = {{5, 6}, {7, 8}};// 创建结果矩阵,初始为0vector<vector<int>> result(2, vector<int>(2, 0));// 矩阵加法addMatrices(A, B, result);cout << "矩阵加法结果:" << endl;printMatrix(result);// 矩阵乘法multiplyMatrices(A, B, result);cout << "矩阵乘法结果:" << endl;printMatrix(result);return 0;
}

2. 采用Eigen库进行计算

Eigen 是一个高效且功能强大的 C++ 模板库,主要用于矩阵和向量的数值计算,包括线性代数、矩阵分解、特征值分解等操作。它具有良好的性能优化,广泛应用于科学计算、机器学习、物理模拟等领域。使用Eigen数据库如何进行编译请参考 Linux下vscode配置C++和python编译调试环境。

2.1 矩阵创建

与numpy中的array不同,c++对于向量、矩阵和张量用不同的名称表述,分别用Eigen::Vector、Eigen::Matrix和Eigen::Tensor,其中Vector和Matrix提供了固定大小和动态大小两种方式。

2.1.1. 向量表示

固定大小的向量

对于固定大小的向量,Eigen 提供了一些预定义的类型,如 Eigen::Vector2d、Eigen::Vector3d 等,这些类型分别表示 2 维、3 维的双精度向量。

Eigen::Vector3d vec;

动态大小的向量

如果向量的大小在运行时确定,可以使用 Eigen::VectorXd 来创建动态大小的向量。

Eigen::VectorXd vec(5);

2.1.2. 矩阵表示

固定大小的矩阵

Eigen 提供了 Eigen::Matrix 类型来表示固定大小的矩阵。Matrix 类型需要指定数据类型、行数和列数。例如,Eigen::Matrix3d 表示 3x3 的双精度矩阵,Eigen::Matrix2f 表示 2x2 的单精度矩阵。

Eigen::Matrix<double, 2, 3> matrix;

动态大小的矩阵

如果矩阵的大小在运行时确定,可以使用 Eigen::MatrixXd 来创建动态大小的矩阵。

Eigen::MatrixXd matrix(3, 3);

2.1.3. 张量表示

张量是高维数组,在 Eigen 中使用 Eigen::Tensor 来表示。与向量和矩阵不同,张量可以有任意维度。张量的模板参数包括数据类型和维度数。

    // 创建一个 3x3x3 的张量
    Eigen::Tensor<float, 3> tensor(3, 3, 3);

接下来我们举例主要以矩阵为主,向量和张量也类似,特殊情况会特别指出。

2.1.4 普通矩阵
#include <iostream>
#include <Eigen/Dense>  // 引入 Eigen 库int main() {// 使用 Eigen 创建一个 2x3 整数矩阵Eigen::Matrix<int, 2, 3> matrix;// 初始化矩阵matrix << 1, 2, 3,4, 5, 6;// 输出矩阵std::cout << "矩阵内容:" << std::endl;std::cout << matrix << std::endl;return 0;
}
2.1.5 特殊矩阵

与python类似,Eigen 库可以直接创建全零矩阵、全 1 矩阵、单位矩阵、随机矩阵、随机整数矩阵、特定范围的矩阵、等间隔数值矩阵以及对角矩阵。

#include <iostream>
#include <Eigen/Dense>int main() {// 1. 创建一个 3x3 的全零矩阵Eigen::Matrix3d zeroMatrix = Eigen::Matrix3d::Zero();std::cout << "1. 3x3 全零矩阵:\n" << zeroMatrix << "\n\n";// 2. 创建一个 3x3 的全 1 矩阵Eigen::Matrix3d onesMatrix = Eigen::Matrix3d::Ones();std::cout << "2. 3x3 全 1 矩阵:\n" << onesMatrix << "\n\n";// 3. 创建一个 4x4 的单位矩阵Eigen::Matrix4d identityMatrix = Eigen::Matrix4d::Identity();std::cout << "3. 4x4 单位矩阵:\n" << identityMatrix << "\n\n";// 4. 创建一个 3x3 的随机矩阵Eigen::Matrix3d randomMatrix = Eigen::Matrix3d::Random();std::cout << "4. 3x3 随机矩阵:\n" << randomMatrix << "\n\n";// 5. 创建一个 3x3 的随机整数矩阵,取值范围为 [-10, 10]Eigen::Matrix3i randomIntMatrix = (Eigen::Matrix3i::Random().array() + 1) * 5;std::cout << "5. 3x3 随机整数矩阵 ([-10, 10]):\n" << randomIntMatrix << "\n\n";// 6. 创建一个包含从 1 到 6 的线性间隔向量Eigen::VectorXd linSpaced = Eigen::VectorXd::LinSpaced(6, 1, 6);std::cout << "6. 从 1 到 6 的线性间隔向量:\n" << linSpaced << "\n\n";// 7. 创建一个 3x3 等间隔数值矩阵,范围从 1 到 9Eigen::MatrixXd linSpacedMatrix(3, 3);linSpacedMatrix << Eigen::VectorXd::LinSpaced(3, 1, 3),Eigen::VectorXd::LinSpaced(3, 4, 6),Eigen::VectorXd::LinSpaced(3, 7, 9);std::cout << "7. 3x3 等间隔数值矩阵:\n" << linSpacedMatrix << "\n\n";// 8. 创建一个 3x3 的对角矩阵,使用对角线元素 [1, 2, 3]Eigen::Vector3d diagElements(1, 2, 3);Eigen::Matrix3d diagMatrix = diagElements.asDiagonal();std::cout << "8. 3x3 对角矩阵:\n" << diagMatrix << "\n\n";return 0;
}

2.2 矩阵参数

使用 Eigen 库可以获取矩阵和张量的 ndim、shape、size、dtype、itemsize 和 nbytes 等信息。

#include <iostream>
#include <unsupported/Eigen/CXX11/Tensor>
#include <Eigen/Dense>int main() {// 1. 创建一个 3x3 的矩阵Eigen::Matrix3d matrix;matrix.setRandom(); // 随机初始化矩阵std::cout << "矩阵内容:\n" << matrix << "\n\n";// 2. 矩阵的维数 (ndim)std::cout << "矩阵的维数 (ndim): 2" << std::endl; // 矩阵总是二维的// 3. 矩阵的形状 (shape)std::cout << "矩阵的形状 (shape): " << matrix.rows() << " x " << matrix.cols() << std::endl;// 4. 矩阵的元素总个数 (size)std::cout << "矩阵的元素总个数 (size): " << matrix.size() << std::endl;// 5. 矩阵中每个元素的字节大小 (itemsize)std::cout << "矩阵中每个元素的字节大小 (itemsize): " << sizeof(matrix(0, 0)) << " bytes" << std::endl;// 6. 矩阵的总字节数 (nbytes)std::size_t matrix_nbytes = matrix.size() * sizeof(matrix(0, 0));std::cout << "矩阵的总字节数 (nbytes): " << matrix_nbytes << " bytes\n\n";// ---------------------------// 7. 创建一个 2x3x4 的张量Eigen::Tensor<float, 3> tensor(2, 3, 4);tensor.setRandom(); // 随机初始化张量std::cout << "张量内容 (部分显示):\n";std::cout << "tensor(0, 0, 0) = " << tensor(0, 0, 0) << "\n";std::cout << "tensor(1, 2, 3) = " << tensor(1, 2, 3) << "\n\n";// 8. 张量的维数 (ndim)std::cout << "张量的维数 (ndim): " << Eigen::Tensor<float, 3>::NumDimensions << std::endl;// 9. 张量的形状 (shape)auto dims = tensor.dimensions();std::cout << "张量的形状 (shape): " << dims[0] << " x " << dims[1] << " x " << dims[2] << std::endl;// 10. 张量的元素总个数 (size)std::cout << "张量的元素总个数 (size): " << tensor.size() << std::endl;// 11. 张量中每个元素的字节大小 (itemsize)std::cout << "张量中每个元素的字节大小 (itemsize): " << sizeof(tensor(0, 0, 0)) << " bytes" << std::endl;// 12. 张量的总字节数 (nbytes)std::size_t tensor_nbytes = tensor.size() * sizeof(tensor(0, 0, 0));std::cout << "张量的总字节数 (nbytes): " << tensor_nbytes << " bytes" << std::endl;return 0;
}

2.3 矩阵索引

在 C++ 的 Eigen 库中,可以通过多种方式进行矩阵索引、切片、以及子矩阵的操作。

1. 基本索引
Eigen 支持通过 () 操作符进行基本的矩阵索引。您可以通过指定矩阵的行和列索引来访问或修改矩阵中的元素。

2. 矩阵切片(子矩阵)
Eigen 提供了 block() 方法,可以方便地提取矩阵的子矩阵。这类似于 Python 中的矩阵切片操作。

3. 布尔索引
Eigen 并不直接支持像 NumPy 那样的布尔索引,但可以通过条件操作生成布尔掩码,然后利用矩阵运算或块操作来实现类似功能。

4. 花式索引(Fancy Indexing)
Eigen 不支持像 NumPy 那样的花式索引(使用数组或列表作为索引)。然而,可以通过手动提取行或列,以及使用 block() 方法结合条件筛选实现类似效果。

5. 子矩阵索引(块索引)
除了前面的 block() 方法,Eigen 还提供了其他方式来获取子矩阵,如 topLeftCorner()、bottomRightCorner()、topRows()、bottomCols() 等。

#include <iostream>
#include <Eigen/Dense>int main() {// 创建一个 4x4 的矩阵并初始化Eigen::Matrix4d matrix;matrix << 1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12,13, 14, 15, 16;std::cout << "原始 4x4 矩阵:\n" << matrix << "\n\n";// 1. 基本索引std::cout << "矩阵中 (1, 2) 处的元素: " << matrix(1, 2) << std::endl;matrix(2, 3) = 100;  // 修改矩阵中的元素std::cout << "修改后的矩阵:\n" << matrix << "\n\n";// 2. 矩阵切片(子矩阵) - 提取子矩阵Eigen::Matrix2d block = matrix.block<2, 2>(1, 1);std::cout << "2x2 子矩阵 (从 (1, 1) 开始):\n" << block << "\n\n";Eigen::MatrixXd dynamic_block = matrix.block(0, 0, 3, 3);  // 3x3 子矩阵std::cout << "3x3 动态大小子矩阵:\n" << dynamic_block << "\n\n";// 3. 布尔索引 - 将大于 10 的元素置 0Eigen::Matrix<bool, 4, 4> mask = (matrix.array() > 10).matrix();std::cout << "布尔掩码 (matrix > 10):\n" << mask << "\n\n";matrix = (matrix.array() > 10).select(0, matrix);  // 应用布尔掩码std::cout << "将大于 10 的元素置 0 后的矩阵:\n" << matrix << "\n\n";// 4. 花式索引 - 手动选择特定的行和列Eigen::MatrixXd selected_rows(2, matrix.cols());selected_rows << matrix.row(0), matrix.row(2);std::cout << "选择第 0 行和第 2 行的矩阵:\n" << selected_rows << "\n\n";Eigen::MatrixXd selected_cols(matrix.rows(), 2);selected_cols << matrix.col(1), matrix.col(3);std::cout << "选择第 1 列和第 3 列的矩阵:\n" << selected_cols << "\n\n";// 5. 子矩阵索引 - 使用快捷方法提取子矩阵Eigen::Matrix2d top_left = matrix.topLeftCorner(2, 2);std::cout << "左上角 2x2 子矩阵:\n" << top_left << "\n\n";Eigen::Matrix2d bottom_right = matrix.bottomRightCorner(2, 2);std::cout << "右下角 2x2 子矩阵:\n" << bottom_right << "\n\n";Eigen::MatrixXd top_rows = matrix.topRows(2);std::cout << "前两行的矩阵:\n" << top_rows << "\n\n";return 0;
}

原始 4x4 矩阵:
 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15 16

矩阵中 (1, 2) 处的元素: 7
修改后的矩阵:
  1   2   3   4
  5   6   7   8
  9  10  11 100
 13  14  15  16

2x2 子矩阵 (从 (1, 1) 开始):
 6  7
10 11

3x3 动态大小子矩阵:
 1  2  3
 5  6  7
 9 10 11

布尔掩码 (matrix > 10):
0 0 0 0
0 0 0 0
0 0 1 1
1 1 1 1

将大于 10 的元素置 0 后的矩阵:
 1  2  3  4
 5  6  7  8
 9 10  0  0
 0  0  0  0

选择第 0 行和第 2 行的矩阵:
 1  2  3  4
 9 10  0  0

选择第 1 列和第 3 列的矩阵:
 2  4
 6  8
10  0
 0  0

左上角 2x2 子矩阵:
1 2
5 6

右下角 2x2 子矩阵:
0 0
0 0

前两行的矩阵:
1 2 3 4
5 6 7 8

2.4 矩阵拼接

使用Eigen库可以很方便对矩阵进行水平和垂直拼接,Eigen库还提供了block函数来对矩阵进行更加灵活的操作,允许在矩阵中插入其他矩阵。

#include <iostream>
#include <Eigen/Dense>int main() {Eigen::MatrixXd A(2, 2);A << 1, 2,3, 4;Eigen::MatrixXd B(2, 2);B << 5, 6,7, 8;// 水平拼接:在列方向上拼接两个矩阵Eigen::MatrixXd horizontal(A.rows(), A.cols() + B.cols());  // 行数不变,列数为A和B的列数之和horizontal << A, B;  // 使用Eigen的<<操作符直接拼接std::cout << "水平拼接后的矩阵:\n" << horizontal << std::endl;// 垂直拼接:在行方向上拼接两个矩阵Eigen::MatrixXd vertical(A.rows() + B.rows(), A.cols());  // 列数不变,行数为A和B的行数之和vertical << A,B;  // 使用Eigen的<<操作符直接拼接std::cout << "垂直拼接后的矩阵:\n" << vertical << std::endl;// 块操作:使用block函数在矩阵中插入其他矩阵Eigen::MatrixXd C(4, 4);C.setZero();  // 初始化为零矩阵// 使用block函数来指定位置拼接A和BC.block(0, 0, A.rows(), A.cols()) = A;  // 将A放置在左上角C.block(2, 2, B.rows(), B.cols()) = B;  // 将B放置在右下角std::cout << "块拼接后的矩阵:\n" << C << std::endl;return 0;
}

水平拼接后的矩阵:
1 2 5 6
3 4 7 8
垂直拼接后的矩阵:
1 2
3 4
5 6
7 8
块拼接后的矩阵:
1 2 0 0
3 4 0 0
0 0 5 6
0 0 7 8

2.5 矩阵初级运算

Eigen库可以非常方便地进行矩阵的逐元素加减乘除操作。Eigen库重载了基本的数学运算符,因此你可以像对待普通变量一样对矩阵进行逐元素的加、减、乘、除操作。

#include <iostream>
#include <Eigen/Dense>int main() {Eigen::MatrixXd A(2, 2);A << 1, 2,3, 4;Eigen::MatrixXd B(2, 2);B << 5, 6,7, 8;// 逐元素加法Eigen::MatrixXd C_add = A + B;std::cout << "逐元素加法 A + B:\n" << C_add << std::endl;// 逐元素减法Eigen::MatrixXd C_sub = A - B;std::cout << "逐元素减法 A - B:\n" << C_sub << std::endl;// 逐元素乘法 (注意这不是矩阵乘法,而是逐元素相乘)Eigen::MatrixXd C_mul = A.array() * B.array();std::cout << "逐元素乘法 A .* B:\n" << C_mul << std::endl;// 逐元素除法Eigen::MatrixXd C_div = A.array() / B.array();std::cout << "逐元素除法 A ./ B:\n" << C_div << std::endl;return 0;
}

2.6 向量&矩阵乘法

矩阵的点乘:通过 .array() 方法实现逐元素相乘。
矩阵的乘法:通过 * 实现标准的线性代数矩阵乘法。
向量的点乘:使用 .dot() 函数进行向量的内积计算。
向量的叉乘:使用 .cross() 函数进行三维向量的外积计算。

#include <iostream>
#include <Eigen/Dense>int main() {// 定义矩阵 A 和 BEigen::MatrixXd A(2, 2);A << 1, 2,3, 4;Eigen::MatrixXd B(2, 2);B << 5, 6,7, 8;// 矩阵的逐元素点乘 (逐元素相乘)Eigen::MatrixXd elementwiseProduct = A.array() * B.array();std::cout << "矩阵的点乘 (逐元素相乘):\n" << elementwiseProduct << std::endl;// 矩阵的标准乘法 (矩阵乘法)Eigen::MatrixXd matrixProduct = A * B;std::cout << "矩阵的乘法 (矩阵乘法):\n" << matrixProduct << std::endl;// 定义向量 v1 和 v2Eigen::Vector3d v1(1, 2, 3);Eigen::Vector3d v2(4, 5, 6);// 向量的点乘 (内积)double dotProduct = v1.dot(v2);std::cout << "向量的点乘 (内积): " << dotProduct << std::endl;// 向量的叉乘 (外积)Eigen::Vector3d crossProduct = v1.cross(v2);std::cout << "向量的叉乘 (外积):\n" << crossProduct << std::endl;return 0;
}

2.7 矩阵的高级操作

2.7.1 矩阵属性

在Eigen库中,C++代码可以非常方便地执行矩阵的各种线性代数操作,比如转置、逆、行列式、迹、特征值与特征向量、广义逆矩阵以及矩阵范数。

#include <iostream>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>  // 用于特征值和特征向量
#include <unsupported/Eigen/MatrixFunctions>  // 用于广义逆int main() {// 定义矩阵 AEigen::MatrixXd A(3, 3);A << 1, 2, 3,0, 1, 4,5, 6, 0;// 1. 矩阵的转置Eigen::MatrixXd A_transpose = A.transpose();std::cout << "矩阵的转置:\n" << A_transpose << std::endl;// 2. 矩阵的逆Eigen::MatrixXd A_inverse = A.inverse();std::cout << "矩阵的逆:\n" << A_inverse << std::endl;// 3. 矩阵的行列式double determinant = A.determinant();std::cout << "矩阵的行列式: " << determinant << std::endl;// 4. 矩阵的迹(对角线元素之和)double trace = A.trace();std::cout << "矩阵的迹: " << trace << std::endl;// 5. 矩阵的特征值和特征向量Eigen::EigenSolver<Eigen::MatrixXd> eigen_solver(A);Eigen::VectorXcd eigenvalues = eigen_solver.eigenvalues();Eigen::MatrixXcd eigenvectors = eigen_solver.eigenvectors();std::cout << "矩阵的特征值:\n" << eigenvalues << std::endl;std::cout << "矩阵的特征向量:\n" << eigenvectors << std::endl;// 6. 广义逆矩阵(Moore-Penrose伪逆)Eigen::MatrixXd A_pseudo_inverse = A.completeOrthogonalDecomposition().pseudoInverse();std::cout << "矩阵的广义逆 (伪逆):\n" << A_pseudo_inverse << std::endl;// 7. 矩阵的范数 (默认为L2范数)double norm = A.norm();std::cout << "矩阵的范数: " << norm << std::endl;return 0;
}

矩阵的转置:
1 0 5
2 1 6
3 4 0
矩阵的逆:
-24  18   5
 20 -15  -4
 -5   4   1
矩阵的行列式: 1
矩阵的迹: 2
矩阵的特征值:
(-0.0263528,0)
   (7.25602,0)
  (-5.22967,0)
矩阵的特征向量:
 (0.757698,0)  (-0.49927,0)  (-0.22578,0)
(-0.632128,0) (-0.466742,0) (-0.526348,0)
 (0.162197,0) (-0.729987,0)  (0.819744,0)
矩阵的广义逆 (伪逆):
-24  18   5
 20 -15  -4
 -5   4   1
矩阵的范数: 9.59166

 2.7.2 矩阵分解

在Eigen库中,矩阵分解操作,如LU分解、QR分解和奇异值分解(SVD),都有良好的支持。以下是如何使用Eigen库执行这些分解操作的完整示例代码。

#include <iostream>
#include <Eigen/Dense>
#include <Eigen/Eigenvalues>  // 用于特征值和特征向量
#include <unsupported/Eigen/MatrixFunctions>  // 用于广义逆int main() {// =========================== LU 分解 ===========================// 定义一个 3x3 矩阵 AEigen::Matrix3d A;A << 4, 3, 2,3, 2, 1,2, 1, 3;// LU分解Eigen::PartialPivLU<Eigen::Matrix3d> lu(A);// 获取 L 和 U 矩阵Eigen::Matrix3d L = lu.matrixLU().triangularView<Eigen::Lower>();Eigen::Matrix3d U = lu.matrixLU().triangularView<Eigen::Upper>();std::cout << "矩阵 A:\n" << A << std::endl;std::cout << "LU 分解的 L 矩阵:\n" << L << std::endl;std::cout << "LU 分解的 U 矩阵:\n" << U << std::endl;// =========================== QR 分解 ===========================// 定义一个 3x3 矩阵 BEigen::Matrix3d B;B << 12, -51, 4,6, 167, -68,-4, 24, -41;// QR分解Eigen::HouseholderQR<Eigen::Matrix3d> qr(B);// 获取 Q 和 R 矩阵Eigen::Matrix3d Q = qr.householderQ();Eigen::Matrix3d R = qr.matrixQR().triangularView<Eigen::Upper>();std::cout << "矩阵 B:\n" << B << std::endl;std::cout << "QR 分解的 Q 矩阵:\n" << Q << std::endl;std::cout << "QR 分解的 R 矩阵:\n" << R << std::endl;// =========================== SVD 分解 ===========================// 定义一个 3x3 矩阵 CEigen::Matrix3d C;C << 1, 2, 3,4, 5, 6,7, 8, 9;// SVD分解Eigen::JacobiSVD<Eigen::Matrix3d> svd(C, Eigen::ComputeFullU | Eigen::ComputeFullV);// 获取 U, S 和 V 矩阵Eigen::Matrix3d U_svd = svd.matrixU();Eigen::Vector3d S_svd = svd.singularValues();Eigen::Matrix3d V_svd = svd.matrixV();std::cout << "矩阵 C:\n" << C << std::endl;std::cout << "SVD 分解的 U 矩阵:\n" << U_svd << std::endl;std::cout << "SVD 分解的奇异值 S:\n" << S_svd << std::endl;std::cout << "SVD 分解的 V 矩阵:\n" << V_svd << std::endl;return 0;
}

矩阵 A:
4 3 2
3 2 1
2 1 3
LU 分解的 L 矩阵:
   4    0    0
 0.5 -0.5    0
0.75  0.5 -1.5
LU 分解的 U 矩阵:
   4    3    2
   0 -0.5    2
   0    0 -1.5
矩阵 B:
 12 -51   4
  6 167 -68
 -4  24 -41
QR 分解的 Q 矩阵:
 -0.857143   0.394286   0.331429
 -0.428571  -0.902857 -0.0342857
  0.285714  -0.171429   0.942857
QR 分解的 R 矩阵:
 -14  -21   14
   0 -175   70
   0    0  -35
矩阵 C:
1 2 3
4 5 6
7 8 9
SVD 分解的 U 矩阵:
 0.214837  0.887231  0.408248
 0.520587  0.249644 -0.816497
 0.826338 -0.387943  0.408248
SVD 分解的奇异值 S:
    16.8481
    1.06837
2.14779e-16
SVD 分解的 V 矩阵:
  0.479671  -0.776691   0.408248
  0.572368 -0.0756865  -0.816497
  0.665064   0.625318   0.408248

2.7.3 张量计算

在Eigen库中,contract操作类似于张量的缩并(contraction),用于对两个张量进行指定维度上的乘积操作,这类似于NumPy中的np.tensordot函数。在张量计算中,缩并是一种在指定轴上进行乘积并对该轴进行求和的操作。

#include <iostream>
#include <unsupported/Eigen/CXX11/Tensor>int main() {// 定义两个张量 A 和 BEigen::Tensor<float, 2> A(2, 3);  // 2x3 张量Eigen::Tensor<float, 2> B(3, 2);  // 3x2 张量// 为张量 A 和 B 填充数据A.setValues({{1, 2, 3}, {4, 5, 6}});B.setValues({{7, 8}, {9, 10}, {11, 12}});// 张量缩并 (类似于 np.tensordot(A, B, axes=1))// 缩并的维度:A 的第 1 维和 B 的第 0 维Eigen::array<Eigen::IndexPair<int>, 1> contraction_dims = { Eigen::IndexPair<int>(1, 0) };Eigen::Tensor<float, 2> result = A.contract(B, contraction_dims);// 输出结果std::cout << "张量 A:\n" << A << std::endl;std::cout << "张量 B:\n" << B << std::endl;std::cout << "缩并结果 (类似 np.tensordot):\n";for (int i = 0; i < result.dimension(0); ++i) {for (int j = 0; j < result.dimension(1); ++j) {std::cout << result(i, j) << " ";}std::cout << std::endl;}return 0;
}

张量 A:
1 2 3
4 5 6
张量 B:
 7  8
 9 10
11 12
缩并结果 (类似 np.tensordot):
58 64 
139 154

2.8 应用案例

2.8.1 解3x3线性方程组

考虑线性方程组:

\begin{cases} x + 2y + 3z = 1 \\ 4x + 5y + 6z = 2 \\ 7x + 4y + 3z = 3 \end{cases}

对应的矩阵形式为:

A = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 4 & 3 \end{bmatrix}          B = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}

#include <iostream>
#include <Eigen/Dense>int main() {// 定义矩阵 A 和向量 bEigen::Matrix3d A;Eigen::Vector3d b;// 填充矩阵 A 和向量 bA << 1, 2, 3,4, 5, 6,7, 4, 3;b << 1, 2, 3;// 求解线性方程组 Ax = bEigen::Vector3d x = A.colPivHouseholderQr().solve(b);// 输出结果std::cout << "线性方程组的解:\n" << x << std::endl;return 0;
}

线性方程组的解:
      1
     -2
1.33333

2.8.2  二次多项式拟合
假设我们有一组数据点,拟合一条二次曲线y = ax^2 + bx + c

#include <iostream>
#include <Eigen/Dense>int main() {// 给定的 x 和 y 数据Eigen::VectorXd x(6);x << 0, 1, 2, 3, 4, 5;Eigen::VectorXd y(6);y << 1, 1.8, 3.2, 4.5, 6.1, 8;// 构造 Vandermonde 矩阵 (用于多项式拟合的设计矩阵)Eigen::MatrixXd A(x.size(), 3);for (int i = 0; i < x.size(); ++i) {A(i, 0) = x(i) * x(i);  // x^2A(i, 1) = x(i);         // xA(i, 2) = 1;            // 常数项}// 使用最小二乘法求解 A * coeffs = yEigen::VectorXd coeffs = A.colPivHouseholderQr().solve(y);// 输出拟合的多项式系数std::cout << "拟合的二次多项式系数 (a, b, c):\n" << coeffs << std::endl;return 0;
}

拟合的二次多项式系数 (a, b, c):
  0.1125
0.843214
0.960714

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1534455.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

重学SpringBoot3-集成RocketMQ(二)

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成RocketMQ&#xff08;二&#xff09; 1. 基础概念2. 准备工作3. 实现事务消息的生产者4. 事务监听器实现5. 消费者示例6. 发送事务消息7. 测试7.1 模…

【新片场-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

如何用SQL Server和Oracle进行数据同步?

数据同步是大数据应用中非常重要的环节&#xff0c;它可以保证数据的实时性和一致性&#xff0c;为数据分析和决策提供重要依据。常见的数据同步方式包括ETL实时同步和实时ETL工具&#xff0c;其中实时ETL工具又可以分为基于日志追踪和基于触发器两种。 针对不同的数据库系统&…

在grafana上配置显示全部node资源信息概览

在grafana上配置显示全部node资源信息概览&#xff0c;便于巡检 1&#xff0c;注册grafana官网账号&#xff1a;Grafana dashboards | Grafana Labs&#xfeff; 2、寻找可以展示所有node资源概览信息的dashboard&#xff0c;并下载支持prometheus数据源的dashboard&#xff…

DevOps 中常常被忽略却至关重要的一个工具

在今天的云原生开发时代&#xff0c;DevOps 已经成为许多企业提高开发和运维效率的标准流程。DevOps 工具链广泛而复杂&#xff0c;涵盖了从规划、开发、测试到部署、监控和运维的多个环节。例如&#xff1a;规划与项目管理工具 GitLab、Jira&#xff1b;版本控制工具 Git&…

c++—多态【万字】【多态的原理】【重写的深入学习】【各种继承关系下的虚表查看】

目录 C—多态1.多态的概念2.多态的定义及实现2.1多态的构成条件2.2虚函数的重写2.2.1虚函数重写的两个例外&#xff1a;2.2.1.1协变2.2.1.2析构函数的重写 2.3 c11的override和final2.3.1final2.3.2override 2.4 重载、重写、重定义的对比 3.抽象类3.1抽象类的概念3.2接口继承和…

搜索树和Map

一.搜索树 1.概念 二叉搜索树又叫二叉排序树&#xff0c;它可以是一颗空树也可以是具有以下性质的二叉树 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左子树也分别为二…

Redis 篇-深入了解使用 Redis 中的 GEO 数据结构实现查询附近店铺、BitMap 实现签到功能、HyperLogLog 实现 UV 流量统计

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 GEO 数据结构的基本用法 1.1 使用 GEO 导入数据 1.2 使用 GEO 实现查找附近店铺功能 2.0 BitMap 基本用法 2.1 使用 BitMap 实现签到功能 2.2 统计连续签到功能 3…

windows server2012 配制nginx安装为服务的时候,直接跳要安装.net框架,用自动的安装,直接失败的解决。

1、上一个已成功在安装过程中的图&#xff1a; 2、之前安装过程中错误的图&#xff1a; 3、离线安装解决&#xff1a; 下载.net framework 3.5&#xff0c;然后解压后&#xff0c;选择指定备用源路径&#xff0c;然后选择.net安装包所在目录&#xff1a; 只要指定上面全路径就…

4G模块点对点传输手把手教程!如何实现远程设备直接通信

使用4G模块进行点对点传输&#xff0c;可以实现远程设备的直接通信&#xff0c;广泛应用于工业控制、远程监控、物联网等领域。本教程将详细讲解如何通过4G模块&#xff0c;构建设备之间的点对点&#xff08;P2P&#xff09;传输系统&#xff0c;从配置设备、建立通信通道到实际…

Delphi Web和Web服务开发目前有哪些选择

Delphi Web和Web服务开发目前有哪些选择 Delphi Web和Web服务开发目前有以下几个选择&#xff1a; Delphi MVC Framework&#xff08;https://github.com/delphimvcframework/delphimvcframework&#xff09;&#xff1a;这是一个开源的Delphi Web框架&#xff0c;基于MVC&am…

【Linux】基本指令及其周边知识

1.准备阶段 在介绍Linux的基本指令之前&#xff0c;我先先向大家介绍一下我的Linux平台&#xff0c;首先我是在阿里云买了个服务器&#xff0c;然后使用Xshell来远程登录Linux&#xff0c;之后有关Linux上的操作都是在这上面进行的。如果你也买了相关的服务器并且设置了相关示…

Parallels Desktop19中文版2024九月最新

Parallels Desktop可以使轻松地在 MAC上运行成千上万款 Windows应用程序&#xff0c;如Excel&#xff0c;会计交易软件等。针对最新版 windows11和macOS Sonoma 进行优化。在 MAC虚拟机中跨多个操作系统开发和测试。包含 Parallels Toolbox – 40 多个适用于 Mac 和 PC 的一键…

ROS1录包偶现一次崩溃问题定位

现象&#xff1a;崩到了mogo_reporter里面 堆栈&#xff1a;crash里面同时存在两个主线程的堆栈 代码 #include "boost/program_options.hpp" #include <signal.h> #include <string> #include <sstream> #include <iostream> #include <…

[“1“, “2“, “3“].map(parseInt)结果

parseInt 的用法 parseInt 是 JavaScript 中的一个全局函数&#xff0c;用于将字符串转换为整数。它的基本语法如下&#xff1a; parseInt(string, radix);string&#xff1a;要解析的字符串。radix&#xff08;可选&#xff09;&#xff1a;字符串的基数&#xff0c;可以是 …

高科技企业选择跨网文件系统最容易踩坑的地方

在数字化时代&#xff0c;高科技企业频繁使用跨网文件交换系统的原因多种多样。首先&#xff0c;随着全球化的推进&#xff0c;企业需要在不同地理位置的分支机构之间传输敏感数据和重要文件。其次&#xff0c;跨网文件交换能够提高工作效率&#xff0c;确保信息的实时更新和共…

开源 TTS 模型「Fish Speech」1.4 发布;GameGen-O :生成开放世界游戏视频模型丨 RTE 开发者日报

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。 我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、…

高并发下的生存之道:如何巧妙化解热Key危机?

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 哈喽,大家好!我是小米,29岁,喜欢分享技术的小米上线啦!今天咱们来聊聊在互联网高并发场景下,一个让大家又爱又恨的问题——热Key问题。热Key是什么…

【C++】_stack和_queue容器适配器、_deque

当别人都在关注你飞的有多高的时候&#xff0c;只有父母在关心你飞的累不累。&#x1f493;&#x1f493;&#x1f493; 目录 ✨说在前面 &#x1f34b;知识点一&#xff1a;stack •&#x1f330;1.stack介绍 •&#x1f330;2.stack的基本操作 &#x1f34b;知识点二&…

【电路笔记】-反相运算放大器

反相运算放大器 文章目录 反相运算放大器1、概述2、理想反相运算放大器3、实际反相运算放大器3.1 闭环增益3.2 输入阻抗3.3 输出阻抗4、反相运算放大器示例5、总结1、概述 上一篇关于同相运算放大器的文章中已介绍了该运算放大器配置的所有细节,该配置在同相引脚 (+) 上获取输…