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

news2024/9/21 0:46:01

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}};

    // 创建结果矩阵,初始为0
    vector<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 到 9
    Eigen::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 的元素置 0
    Eigen::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和B
    C.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 和 B
    Eigen::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 和 v2
    Eigen::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() {
    // 定义矩阵 A
    Eigen::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 矩阵 A
    Eigen::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 矩阵 B
    Eigen::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 矩阵 C
    Eigen::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 和 B
    Eigen::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 和向量 b
    Eigen::Matrix3d A;
    Eigen::Vector3d b;

    // 填充矩阵 A 和向量 b
    A << 1, 2, 3,
         4, 5, 6,
         7, 4, 3;
    b << 1, 2, 3;

    // 求解线性方程组 Ax = b
    Eigen::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^2
        A(i, 1) = x(i);         // x
        A(i, 2) = 1;            // 常数项
    }

    // 使用最小二乘法求解 A * coeffs = y
    Eigen::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.coloradmin.cn/o/2134603.html

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

相关文章

8月最新大模型新书-《自然语言处理:大模型理论与实践》(附PDF)西财赵宇教授新作

介绍 本书主要面向高校本科生、研究生及教学科研人员&#xff0c;适合作为教学用书。同时&#xff0c;它也适合计算语言学家、语言学家、数据科学家和NLP开发人员等专业人士使用。为了照顾不同读者的学科背景差异&#xff0c;书中附录部分专门介绍了与NLP密切相关的基础知识&a…

HAL库学习梳理——SPI(收发Flash数据实验)

实验现象&#xff1a;将LED灯的状态保存到Flash里&#xff0c;掉电读取Flash并设置LED。 1&#xff0c;STM32CubeMx 配置 1.1 SPI引脚说明 外设连接 1.2 参数配置 配置工作模式 主机工作模式 SPI 工作参数设置 1.3 GPIO配置 SPI接线配置 SPI引脚的GPIO配置 2&#xff0c;程序…

大模型开源:ChatGLM-6B (介绍以及本地部署)

简介 ChatGLM-6B 是一个开源的、支持中英双语问答的对话语言模型&#xff0c;基于 General Language Model (GLM) 架构&#xff0c;具有 62 亿参数。结合模型量化技术&#xff0c;用户可以在消费级的显卡上进行本地部署&#xff08;INT4 量化级别下最低只需 6GB 显存&#xff…

水位雨量自动监测站的工作原理

水位雨量自动监测站是一种集自动化、智能化于一体的水文监测设施&#xff0c;主要用于实时监测和记录水位及降雨量的变化&#xff0c;为水文预报、水资源管理、防洪减灾等提供科学依据。水位雨量自动监测站主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构…

js读取文件,生成随机题目,多项选择题则提供随机答案供选择

一.第一个要求 根据模板生成随机题目可以将 --- 内的内容 ---变成JSON然后根据参数的限制条件来生成随机参数&#xff0c;再替换到题目中example.md --- Type: Quiz Template Domain: - Geometry Knowledge:- - 數學- 度量、圖形與空間範疇- 弧長和扇形面積- 理解圓的弧長公…

掌握项目全流程:10个项目管理烫知识

一、启动阶段 1、一个清晰且有吸引力的项目愿景对于项目的成功启动至关重要。它能够让所有项目成员以及相关干系人明白项目的最终目标和意义。 在这个阶段&#xff0c;我们可以利用项目管理工具进度猫来可视化地呈现项目的整体愿景&#xff0c;将大的目标拆分成初步的任务板…

RT-DETR训练自己的数据集(从代码下载到实例测试)

文章目录 前言一、RT-DETR简介二、环境搭建三、构建数据集四、修改配置文件①数据集文件配置②模型文件配置③训练文件配置 五、模型训练和测试模型训练模型测试 总结 前言 提示&#xff1a;本文是RT-DETR训练自己数据集的记录教程&#xff0c;需要大家在本地已配置好CUDA,cuD…

302状态如何进行重定向

文章目录 一、302状态是什么意思二、遇到的使用场景三、如何处理customservice.wxmlcustomservice.js 一、302状态是什么意思 302状态码是临时重定向&#xff08;Move Temporarily&#xff09;&#xff0c;表示所请求的资源临时地转移到新的位置。此外还有一个301永久重定向&a…

【spring】maven引入okhttp的日志拦截器打开增量注解进程

HttpLoggingInterceptor 是在logging-interceptor库中的:这个logging库老找不到 import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor;发现这仨是独立的库 pom中三个依赖 <!-- OKHTTP3 --><

在group by分组的时候,某个key过多导致数据倾斜

解决方案&#xff1a;将 key 打散&#xff0c;给 key 增加随机前缀 在进行 group by 之前&#xff0c;先给每个 user_id 增加一个随机前缀&#xff0c;使得原本相同的 user_id 被打散到不同的分组中。 按带前缀的 key 进行分组 对带有随机前缀的 user_id 进行分组和聚合。 …

重要涉密文件如何防窃取?四个方法有效防止文件泄密【文件保密管理】

随着信息化时代的发展&#xff0c;数据安全问题变得日益突出&#xff0c;特别是对于一些重要的涉密文件&#xff0c;其泄密将带来严重后果。因此&#xff0c;企业和个人在处理机密文件时&#xff0c;必须采取有效的措施来防止文件被窃取。 小编在本文将介绍四个有效的方法&…

三招教你搞定GPU服务器配置→收藏推荐配置

在AI人工智能应用日益渗透各行各业的今天&#xff0c;图形处理器&#xff08;GPU&#xff09;市场呈现出蓬勃发展的态势&#xff0c;其中GPU服务器市场更是炙手可热&#xff0c;其热度始终居高不下。随着人工智能、深度学习、大数据分析等前沿领域的不断拓展与深化&#xff0c;…

python+matplotlib 画一个漂亮的折线统计图

pythonmatplotlib 画一个漂亮的折线统计图 有详细的注释说明…… import matplotlib.pyplot as plt import numpy as np import mathdef draw_line_chart(Line_data_list,title,pic_name)::param Line_data_list: 折线数据源:param title: 图表名称:param pic_name: 保存图片名…

免费!OpenAI发布最新模型GPT-4o mini,取代GPT3.5,GPT3.5退出历史舞台?

有个小伙伴问我&#xff0c;GPT-4O mini是什么&#xff0c;当时我还一脸懵逼&#xff0c;便做了一波猜测&#xff1a; 我猜测哈&#xff0c;这个可能是ChatGPT4o的前提下&#xff0c;只支持文本功能的版本&#xff0c;速度更快 结果&#xff0c;大错特错。 让我们一起看看Open…

理解高并发

文章目录 1、如何理解高并发2、高并发的关键指标3、高并发系统设计的目标是什么&#xff1f;1_宏观目标2_微观目标1.性能指标2.可用性指标3.可扩展性指标 4、高并发的实践方案有哪些&#xff1f;1_通用的设计方法1.纵向扩展&#xff08;scale-up&#xff09;2.横向扩展&#xf…

【隐私计算】Cheetah安全多方计算协议-阿里安全双子座实验室

2PC-NN安全推理与实际应用之间仍存在较大性能差距&#xff0c;因此只适用于小数据集或简单模型。Cheetah仔细设计DNN&#xff0c;基于格的同态加密、VOLE类型的不经意传输和秘密共享&#xff0c;提出了一个2PC-NN推理系统Cheetah&#xff0c;比CCS20的CrypTFlow2开销小的多&…

数据结构—线性表和顺序表

线性表&#xff1a; 线性表是一个由n个具有相同特性的数据元素构成的有限序列。常用到的线性表都有&#xff1a;链表、队列、栈、顺序表.... 顺序表&#xff1a; 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff08;顺序表的元素类型是包装类&#x…

[苍穹外卖]-10WebSocket入门与实战

WebSocket WebSocket是基于TCP的一种新的网络协议, 实现了浏览器与服务器的全双工通信, 即一次握手,建立持久连接,双向数据传输 区别 HTTP是短连接, WebSocket是长连接HTTP单向通信, 基于请求响应模型WebSocket支持双向通信 相同 HTTP和WebSocket底层都是TCP连接 应用场景…

Android 通过相机和系统相册获取图片,压缩,结果回调

一、需求背景 在常规的App开发中&#xff0c;很多时候需要用户上传图片来进行一些业务上的实现&#xff0c;例如用户反馈&#xff0c;图片凭证等。 二、实现功能 1.选择弹窗&#xff08;即选择拍照或者相册&#xff09; 2.申请权限&#xff08;相机权限&#xff09; 3.相机…

油耳用什么掏耳朵比较好?可视挖耳勺推荐平价

掏耳朵是一个轻松又舒服的感觉&#xff0c;很多人就会用棉签和普通耳勺越掏越进&#xff0c;在盲掏的过程中容易弄伤耳膜。所以我们在掏耳时要选好工具。市面上的智能可视挖耳勺&#xff0c;顶端带有摄像头&#xff0c;可以通过清楚的观察到耳道中的情况。但现在市面上关于可视…