首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

5. 高级初始化方法(Advanced Initialization)

本文介绍几种高级的初始化矩阵的方法。将更详细的介绍先前提到的comma-initializer,还会介绍获取一些特殊矩阵的方法。

5.1 The comma initializer

Eigen提供了一种comma-initializer的初始化方法,使得我们可以很方便地定义一个矩阵(Matrix)、向量(Vector)或者是数组(Array)的所有元素。 comma-initializer允许我们从左上角到右下角依次列出矩阵的所有元素,但是元素个数必须和声明的矩阵大小匹配。 这就是我们在 初识Eigen中见到使用"<<"进行初始化的方法:

        Matrix3f m;
        m << 1, 2, 3,
             4, 5, 6,
             7, 8, 9;
        std::cout << m; 

输出结果如下:

    1 2 3
    4 5 6
    7 8 9

此外,comma-initializer进行初始化时也可以赋予向量或者是矩阵。逗号的使用则是把矩阵或者向量拼接起来。 需要强调的是用于初始化的矩阵大小应当能够匹配目标矩阵的大小。参考如下例程:

        RowVectorXd vec1(3);
        vec1 << 1, 2, 3;
        std::cout << "vec1 = " << vec1 << std::endl;
        
        RowVectorXd vec2(4);
        vec2 << 1, 4, 9, 16;;
        std::cout << "vec2 = " << vec2 << std::endl;
        
        RowVectorXd joined(7);
        joined << vec1, vec2;
        std::cout << "joined = " << joined << std::endl;
        
        MatrixXf matA(2, 2);
        matA << 1, 2, 3, 4;

        MatrixXf matB(4, 4);
        matB << matA, matA/10, matA/10, matA;
        std::cout << "matB:" << std::endl << matB << std::endl; 

输出结果如下:

    vec1 = 1 2 3
    vec2 =  1  4  9 16
    joined =  1  2  3  1  4  9 16
    matB:
      1   2 0.1 0.2
      3   4 0.3 0.4
    0.1 0.2   1   2
    0.3 0.4   3   4

comma-initializer还可以用来给分块表达式(block expression)赋值。参考如下例程:

        Matrix3f m;
        m.row(0) << 1, 2, 3;
        m.block(1,0,2,2) << 4, 5, 7, 8;
        m.col(2).tail(2) << 6, 9;                   
        std::cout << m; 

输出结果如下:

    1 2 3
    4 5 6
    7 8 9

5.2 特殊的矩阵和数组

Matrix和Array类都有一个静态函数::Zero(), 用来将所有元素都赋0,有三个重载函数。 第一种没有参数只能用于fixed-size的矩阵或数组。如果想将dynamic-size的矩阵或数组初始化为0,必须提供矩阵或数组的大小。 因此,剩余两种重载函数分别要求一个和两个参数用来声明矩阵或数组的大小。参考如下例程:

        std::cout << "A fixed-size array:\n";
        Array33f a1 = Array33f::Zero();
        std::cout << a1 << "\n\n";
        std::cout << "A one-dimensional dynamic-size array:\n";
        ArrayXf a2 = ArrayXf::Zero(3);
        std::cout << a2 << "\n\n";
        std::cout << "A two-dimensional dynamic-size array:\n";
        ArrayXXf a3 = ArrayXXf::Zero(3, 4);
        std::cout << a3 << "\n"; 

输出结果如下:

    A fixed-size array:
    0 0 0
    0 0 0
    0 0 0

    A one-dimensional dynamic-size array:
    0
    0
    0

    A two-dimensional dynamic-size array:
    0 0 0 0
    0 0 0 0
    0 0 0 0

类似的,还有一些其它的静态函数

下面是一个使用LinSpaced()的一个例子,生成一个等差数列表示角度,然后计算它们的正弦和余弦函数值:

        ArrayXXf table(10, 4);
        table.col(0) = ArrayXf::LinSpaced(10, 0, 90);
        table.col(1) = M_PI / 180 * table.col(0);
        table.col(2) = table.col(1).sin();
        table.col(3) = table.col(1).cos();
        std::cout << "  Degrees   Radians      Sine    Cosine\n";
        std::cout << table << std::endl; 

输出结果如下:

      Degrees   Radians      Sine    Cosine
        0         0         0         1
       10     0.175     0.174     0.985
       20     0.349     0.342      0.94
       30     0.524       0.5     0.866
       40     0.698     0.643     0.766
       50     0.873     0.766     0.643
       60      1.05     0.866       0.5
       70      1.22      0.94     0.342
       80       1.4     0.985     0.174
       90      1.57         1 -4.37e-08

上面的例子展示了把初始化函数的输出赋值给一个对象或者表达式。 为了方便Eigen还提供了像.setZero(),.setIdentity(),.setLinSpaced()这样的函数直接操作对象。 下面的一个例子用了三种方法构造矩阵\(J=\begin{bmatrix}0 & I \\ I & 0\end{bmatrix}\), 依次为静态函数+赋值、静态函数+comma-initializer、和.SetXxx()函数

        const int size = 6;
        MatrixXd mat1(size, size);
        mat1.topLeftCorner(size/2, size/2)     = MatrixXd::Zero(size/2, size/2);
        mat1.topRightCorner(size/2, size/2)    = MatrixXd::Identity(size/2, size/2);
        mat1.bottomLeftCorner(size/2, size/2)  = MatrixXd::Identity(size/2, size/2);
        mat1.bottomRightCorner(size/2, size/2) = MatrixXd::Zero(size/2, size/2);
        std::cout << mat1 << std::endl << std::endl;
        
        MatrixXd mat2(size, size);
        mat2.topLeftCorner(size/2, size/2).setZero();
        mat2.topRightCorner(size/2, size/2).setIdentity();
        mat2.bottomLeftCorner(size/2, size/2).setIdentity();
        mat2.bottomRightCorner(size/2, size/2).setZero();
        std::cout << mat2 << std::endl << std::endl;
        
        MatrixXd mat3(size, size);
        mat3 << MatrixXd::Zero(size/2, size/2), MatrixXd::Identity(size/2, size/2),
                MatrixXd::Identity(size/2, size/2), MatrixXd::Zero(size/2, size/2);
        std::cout << mat3 << std::endl; 

5.3 临时对象的使用

如前面提到的,静态函数Zero()和Constant()可以在声明变量时用于初始化对象,也可以作为赋值运算的右值。 我们可以认为这些函数返回的是一个Matrix或者一个Array。实际上,它们返回的是一个被称为expression的对象, 这个对象可以在必要时转换为Matrix或者Array。

这些expression也可以用作临时对象。在 初识Eigen 中的第二个例程中我们就已经涉及到了,这里再详细介绍一下:

        #include <iostream>
        #include <Eigen/Dense>

        using namespace Eigen;
        using namespace std;
        int main()
        {
            MatrixXd m = MatrixXd::Random(3,3);
            m = (m + MatrixXd::Constant(3,3,1.2)) * 50;
            cout << "m =" << endl << m << endl;
            VectorXd v(3);
            v << 1, 2, 3;
            cout << "m * v =" << endl << m * v << endl;
        } 

输出结果如下:

    m =
      94 89.8 43.5
    49.4  101 86.8
    88.3 29.8 37.8
    m * v =
    404
    512
    261

在上面的例子中表达式MatrixXd::Constant(3,3,1.2)生成了一个临时的对象。comma-initializer也可以生成临时对象。 下面的例子里,先构建了一个2*3的矩阵,然后与\(\begin{bmatrix}0 & 1 \\ 1 & 0\end{bmatrix}\)相乘。

        MatrixXf mat = MatrixXf::Random(2, 3);
        std::cout << mat << std::endl << std::endl;
        mat = (MatrixXf(2,2) << 0, 1, 1, 0).finished() * mat;
        std::cout << mat << std::endl; 

输出结果如下:

      0.68  0.566  0.823
    -0.211  0.597 -0.605

    -0.211  0.597 -0.605
      0.68  0.566  0.823

为了和后面的实际的矩阵对象相乘必须调用finished()。




Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1