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

2. 矩阵和向量的基本数值计算

本章介绍对Matrix类型的矩阵、向量的数值计算方法。

2.1 加减运算符和标量乘除运算符

根据矩阵加减法的运算规则,我们知道参与运算的两个矩阵应当具有相同的行数和列数。 此外,由于C++的语法要求,两个Matrix对象的元素应当具有相同的数据类型。Eigen提供了如下的运算符:

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

        using namespace Eigen;

        int main()
        {
            Matrix2d a;
            MatrixXd b(2,2);

            a << 1, 2,
                 3, 4;
            b << 2, 3,
                 1, 4;

            std::cout << "a + b =\n" << a + b << std::endl;
            std::cout << "a - b =\n" << a - b << std::endl;
            std::cout << "Doing a += b;" << std::endl;
            a += b;
            std::cout << "Now a =\n" << a << std::endl;

            Vector3d v(1,2,3);
            Vector3d w(1,0,0);
            std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
        } 

其输出结果如下:

        a + b =
        3 5
        4 8
        a - b =
        -1 -1
        2  0
        Doing a += b;
        Now a =
        3 5
        4 8
        -v + w - v =
        -1
        -4
        -6 

也提供了标量的乘除运算符:

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

        using namespace Eigen;

        int main()
        {
            Matrix2d a;
            a << 1, 2,
                 3, 4;
            Vector3d v(1,2,3);

            std::cout << "a * 2.5 =\n" << a * 2.5 << std::endl;
            std::cout << "0.1 * v =\n" << 0.1 * v << std::endl;
            std::cout << "Doing v *= 2;" << std::endl;
            v *= 2;
            std::cout << "Now v =\n" << v << std::endl;
        } 

其输出结果如下:

        a * 2.5 =
        2.5   5
        7.5  10
        0.1 * v =
        0.1
        0.2
        0.3
        Doing v *= 2;
        Now v =
        2
        4
        6 

需要说明的是,在Eigen中像'+'这样的算术运算符并不进行计算, 它只是返回一个“表达式对象(expression object)”来记录需要进行的计算。 当解析完整个表达式,执行赋值操作(通常是'='运算符)时再具体计算整个表达式的结果。 这种机制称为'expression templates'看起来很麻烦,但现代的编译器可以做一些优化。 例如,当我们需要计算如下的表达式时:

        VectorXf a(50), b(50), c(50), d(50);
        ...
        a = 3 * b + 4 * c + 5 * d; 

实际上可以只用一个循环就完成所有的操作了:

        for (int i = 0; i < 50; i++)
            a[i] = 3*b[i] + 4*c[i] + 5*d[i]; 

因此,我们可以把一个表达式写的很长,这样Eigen可以由更多的优化可能。这种机制我们还会在后续的章节中予以详细的介绍。

2.2 转置和共轭运算

矩阵\(a\)的转置\(a^T\),共轭\(\bar{a}\),以及共轭转置\(a^*\) 可以分别通过成员函数'transpose()', 'conjugate()'和'adjoint()'获得。

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

        using namespace Eigen;
        using namespace std;

        int main()
        {
            MatrixXcf a = MatrixXcf::Random(2,2);
            cout << "Here is the matrix a\n" << a << endl;
            cout << "Here is the matrix a^T\n" << a.transpose() << endl;
            cout << "Here is the conjugate of a\n" << a.conjugate() << endl;
            cout << "Here is the matrix a^*\n" << a.adjoint() << endl;

            cout << "========" << endl;
            MatrixXf b = MatrixXf::Random(2,2);
            cout << "Here is the matrix b\n" << b << endl;
            cout << "Here is the matrix b^T\n" << b.transpose() << endl;
            cout << "Here is the conjugate of b\n" << b.conjugate() << endl;
            cout << "Here is the matrix b^*\n" << b.adjoint() << endl;
        } 

其输出结果如下:

        Here is the matrix a
        (-0.211234,0.680375) (-0.604897,0.823295)
        (0.59688,0.566198) (0.536459,-0.329554)
        Here is the matrix a^T
        (-0.211234,0.680375)   (0.59688,0.566198)
        (-0.604897,0.823295) (0.536459,-0.329554)
        Here is the conjugate of a
        (-0.211234,-0.680375) (-0.604897,-0.823295)
        (0.59688,-0.566198)   (0.536459,0.329554)
        Here is the matrix a^*
        (-0.211234,-0.680375)   (0.59688,-0.566198)
        (-0.604897,-0.823295)   (0.536459,0.329554)
        ========
       Here is the matrix b
        -0.444451 -0.0452059
         0.10794   0.257742
       Here is the matrix b^T
        -0.444451    0.10794
        -0.0452059   0.257742
       Here is the conjugate of b
        -0.444451 -0.0452059
         0.10794   0.257742
       Here is the matrix b^*
        -0.444451    0.10794
        -0.0452059   0.257742 

对于实数矩阵,共轭就是它本身所以conjugate()不做任何操作,而adjoint()与transpose()等价。 对于基本的算术运算符,transpose()和adjoint()只是返回一个代理对象并不做任何实际的转换。 只有在执行像 "b = a.transpose()"这样的操作时,才会立即计算a的转置并把结果赋予矩阵b。 但是当执行"a = a.transpose()"这样的操作时,Eigen就会在执行转置运算的同时写矩阵a的元素, 这样会导致a中的结果不是我们想要的a的转置,应当避免这样的操作。 这就是所谓的引用错误"aliasing issue",在debug模式下,如果没有关闭断言"assertions"将会报错。 Eigen提供了'transposeInPlace()'函数来实现这类原地转换。类似的,也有adjointInPlace()

        #define EIGEN_NO_DEBUG

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

        using namespace Eigen;
        using namespace std;

        int main()
        {
            Matrix2i a;
            a << 1, 2, 3, 4;

            cout << "Here is the initial matrix a:\n" << a << endl;
            cout << "Here is the matrix a^T:\n" << a.transpose() << endl;

            // 如下的操作将不能够得到a的转置
            a = a.transpose();
            cout << "After the Operation 'a = a.transpose()', the matrix a:\n" << a << endl;

            a << 1, 2, 3, 4;
            a.transposeInPlace();
            cout << "After the Operation 'a.transposeInPlace()', the matrix a:\n" << a << endl;
        } 

其输出结果如下:

        Here is the initial matrix a:
        1 2
        3 4
        Here is the matrix a^T:
        1 3
        2 4
        After the Operation 'a = a.transpose()', the matrix a:
        1 2
        2 4
        After the Operation 'a.transposeInPlace()', the matrix a:
        1 3
        2 4 

2.3 矩阵乘法运算

矩阵的乘法运算也是通过运算符*实现的。因为向量也是一种特殊的矩阵,所以这里讲的矩阵的乘法对向量同样适用。 Eigen提供了两个运算符:

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

        using namespace Eigen;

        int main()
        {
            Matrix2d mat;
            mat << 1, 2,
                   3, 4;
            Vector2d u(-1,1), v(2,0);
            std::cout << "Here is mat*mat:\n" << mat*mat << std::endl;
            std::cout << "Here is mat*u:\n" << mat*u << std::endl;
            std::cout << "Here is u^T*mat:\n" << u.transpose()*mat << std::endl;
            std::cout << "Here is u^T*v:\n" << u.transpose()*v << std::endl;
            std::cout << "Here is u*v^T:\n" << u*v.transpose() << std::endl;
            std::cout << "Let's multiply mat by itself" << std::endl;
            mat = mat*mat;
            std::cout << "Now mat is mat:\n" << mat << std::endl;
        } 

其输出结果如下:

        Here is mat*mat:
         7 10
        15 22
        Here is mat*u:
        1
        1
        Here is u^T*mat:
        2 2
        Here is u^T*v:
        -2
        Here is u*v^T:
        -2 -0
         2  0
        Let's multiply mat by itself
        Now mat is mat:
         7 10
        15 22 

细心的读者可能会对"mat = mat*mat"产生疑问,按照Eigen的'expression templates'机制,也存在类似于例程3中的'aliasing issues'。 实际上,Eigen对矩阵乘法做了特殊的处理引入了一个临时变量,所以 'm = m*m'等价于:

        tmp = m*m;
        m = tmp; 

如果我们可以确定不存在'aliasing issue'的问题时,可以通过方法'noallias()'不引入临时变量,如下:

        c.noalias() += a * b; 

关于aliasing issue的问题,我们会在后续章节中予以详细介绍。

2.4 点乘和叉乘运算

我们可以通过dot()和cross()方法实现点乘和叉乘运算。当然点乘运算也可以看作时一个1×1的矩阵u.ajoint()*v。 需要注意的时叉乘运算只针对维度为3的向量,点乘则适用于任何维度的向量。在处理复数时...

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

        using namespace Eigen;
        using namespace std;

        int main()
        {
            Vector3d v(1,2,3);
            Vector3d w(0,1,2);

            cout << "Dot product: " << v.dot(w) << endl;
            double dp = v.adjoint()*w;
            cout << "Dot product via a matrix product: " << dp << endl;
            cout << "Cross product:\n" << v.cross(w) << endl;
        } 

其输出结果如下:

        Dot product: 8
        Dot product via a matrix product: 8
        Cross product:
         1
        -2
         1 

2.5 基本的arithmetic reduction运算

Eigen也提供了一些reduction运算,把一个矩阵后者向量reduce到一个数, 例如求所有元素的和'sum()',最大值'maxCoeff()',最小值'minCoeff()'等。 此外,Eigen还提供了用来求取minCoeff和maxCoeff元素的索引的方法:

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

        using namespace Eigen;
        using namespace std;

        int main()
        {
            Matrix2d mat;
            mat << 1, 2,
                   3, 4;
            cout << "Here is mat.sum():       " << mat.sum()       << endl;
            cout << "Here is mat.prod():      " << mat.prod()      << endl;
            cout << "Here is mat.mean():      " << mat.mean()      << endl;
            cout << "Here is mat.minCoeff():  " << mat.minCoeff()  << endl;
            cout << "Here is mat.maxCoeff():  " << mat.maxCoeff()  << endl;
            cout << "Here is mat.trace():     " << mat.trace()     << endl;

            cout << "\n====\n" << endl;
            Matrix3f m = Matrix3f::Random();
            std::ptrdiff_t i, j;
            float minOfM = m.minCoeff(&i,&j);
            cout << "Here is the matrix m:\n" << m << endl;
            cout << "Its minimum coefficient (" << minOfM 
                 << ") is at position (" << i << "," << j << ")\n\n";
            RowVector4i v = RowVector4i::Random();
            int maxOfV = v.maxCoeff(&i);
            cout << "Here is the vector v: " << v << endl;
            cout << "Its maximum coefficient (" << maxOfV 
                 << ") is at position " << i << endl;
        } 

其输出结果如下:

        Here is mat.sum():       10
        Here is mat.prod():      24
        Here is mat.mean():      2.5
        Here is mat.minCoeff():  1
        Here is mat.maxCoeff():  4
        Here is mat.trace():     5

        ====

        Here is the matrix m:
         0.680375   0.59688 -0.329554
        -0.211234  0.823295  0.536459
         0.566198 -0.604897 -0.444451
        Its minimum coefficient (-0.604897) is at position (2,1)

        Here is the vector v:  115899597  -48539462  276748203 -290373134
        Its maximum coefficient (276748203) is at position 2 



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