约简化,访客和广播
- 一、约简化
- 1. 标准计算
- 2. 布尔约减
- 二、访问
- 三、部分约简
- 1. 将部分约减与其他业务相结合
- 四、广播
- 1. 将广播与其他业务相结合
一、约简化
在Eigen中,约简化是一个接受矩阵或数组并返回单个标量值的函数。最常用的约简方法之一是.sum(),它返回给定矩阵或数组中所有系数的和。
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::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;
}//输出
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
由函数 trace() 返回的矩阵的轨迹是对角线系数的和,可以等效地计算为 a.diagonal().sum()。
1. 标准计算
向量的(欧几里得也就是 ℓ2 )平方范数可以得到 squaredNorm()。它等于向量与自身的点积,也等价于它的系数绝对值的平方和。
Eigen还提供了 norm() 方法,该方法返回 squaredNorm() 的平方根。
这些运算也可以作用于矩阵;在这种情况下,n × p 矩阵被视为大小为 (n * p) 的向量,因此例如 norm() 方法返回 “Frobenius” 或“Hilbert-Schmidt” 范数。我们不讨论矩阵 ℓ2 范数因为它有不同的含义。
如果需要其他与系数相关的 ℓp 规范,请使用 lpNorm<p>() 方法。模板参数p可以取特殊值无穷大,如果你想要的是 ℓ∞ 范数,它是系数绝对值的最大值。
下面的示例演示了这些方法。
#include <Eigen/Dense>
#include <iostream>int main()
{Eigen::VectorXf v(2);Eigen::MatrixXf m(2,2), n(2,2);v << -1,2;m << 1,-2,-3,4;std::cout << "v.squaredNorm() = " << v.squaredNorm() << std::endl;std::cout << "v.norm() = " << v.norm() << std::endl;std::cout << "v.lpNorm<1>() = " << v.lpNorm<1>() << std::endl;std::cout << "v.lpNorm<Infinity>() = " << v.lpNorm<Eigen::Infinity>() << std::endl;std::cout << std::endl;std::cout << "m.squaredNorm() = " << m.squaredNorm() << std::endl;std::cout << "m.norm() = " << m.norm() << std::endl;std::cout << "m.lpNorm<1>() = " << m.lpNorm<1>() << std::endl;std::cout << "m.lpNorm<Infinity>() = " << m.lpNorm<Eigen::Infinity>() << std::endl;
}// 输出
v.squaredNorm() = 5
v.norm() = 2.23607
v.lpNorm<1>() = 3
v.lpNorm<Infinity>() = 2m.squaredNorm() = 30
m.norm() = 5.47723
m.lpNorm<1>() = 10
m.lpNorm<Infinity>() = 4
算子范数:1 范数和 ∞ 范数矩阵算子范数可以很容易地计算如下:
#include <Eigen/Dense>
#include <iostream>int main()
{Eigen::MatrixXf m(2,2);m << 1,-2,-3,4;std::cout << "1-norm(m) = " << m.cwiseAbs().colwise().sum().maxCoeff()<< " == " << m.colwise().lpNorm<1>().maxCoeff() << std::endl;std::cout << "infty-norm(m) = " << m.cwiseAbs().rowwise().sum().maxCoeff()<< " == " << m.rowwise().lpNorm<1>().maxCoeff() << std::endl;
}// 输出
1-norm(m) = 6 == 6
infty-norm(m) = 7 == 7
有关这些表达式语法的更多解释,请参见下文。
2. 布尔约减
下面的约简操作是对布尔值进行的:
- 如果给定矩阵或数组中的所有系数都为真,则 all() 返回真。
- 如果给定矩阵或数组中至少有一个系数为真,则 any() 返回真。
- count() 返回给定矩阵或数组中求值为true的系数数。
这些操作符通常与 Array 提供的系数比较和相等操作符一起使用。例如,array > 0 是与 array 大小相同的 array,在 array 对应系数为正的位置为 true。因此,(array > 0).all() 测试数组的所有系数是否都为正。这可以从下面的例子中看到:
#include <Eigen/Dense>
#include <iostream>int main()
{Eigen::ArrayXXf a(2,2);a << 1,2,3,4;std::cout << "(a > 0).all() = " << (a > 0).all() << std::endl;std::cout << "(a > 0).any() = " << (a > 0).any() << std::endl;std::cout << "(a > 0).count() = " << (a > 0).count() << std::endl;std::cout << std::endl;std::cout << "(a > 2).all() = " << (a > 2).all() << std::endl;std::cout << "(a > 2).any() = " << (a > 2).any() << std::endl;std::cout << "(a > 2).count() = " << (a > 2).count() << std::endl;
}//输出
(a > 0).all() = 1
(a > 0).any() = 1
(a > 0).count() = 4(a > 2).all() = 0
(a > 2).any() = 1
(a > 2).count() = 2
二、访问
当想要获得矩阵或数组中系数的位置时,也就是获取矩阵中的特殊元素的时候。访问器很有用。最简单的例子是 maxCoeff(&x,&y) 和 minCoeff(&x,&y),它们可用于查找矩阵或数组中最大或最小系数的位置。
传递给访问者的参数是指向存储行和列位置的变量的指针。这些变量的类型应该是Index,如下所示:
#include <iostream>
#include <Eigen/Dense>int main()
{Eigen::MatrixXf m(2,2);m << 1, 2,3, 4;//get location of maximumEigen::Index maxRow, maxCol;float max = m.maxCoeff(&maxRow, &maxCol);//get location of minimumEigen::Index minRow, minCol;float min = m.minCoeff(&minRow, &minCol);std::cout << "Max: " << max << ", at: " <<maxRow << "," << maxCol << std::endl;std:: cout << "Min: " << min << ", at: " <<minRow << "," << minCol << std::endl;
}//输出
Max: 4, at: 1,1
Min: 1, at: 0,0
这两个函数还返回最小或最大系数的值。
三、部分约简
部分约简是可以在矩阵或数组上按列或按行操作的约简,对每列或每行应用约简操作,并返回具有相应值的列或行向量。使用 colwise() 或 rowwise() 应用部分缩减。
一个简单的例子是获取给定矩阵中每列元素的最大值,并将结果存储在行向量中:
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::MatrixXf mat(2,4);mat << 1, 2, 6, 9,3, 1, 7, 2;std::cout << "Column's maximum: " << std::endl<< mat.colwise().maxCoeff() << std::endl;
}//输出
Column's maximum:
3 2 7 9
同样的操作可以按行执行:
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::MatrixXf mat(2,4);mat << 1, 2, 6, 9,3, 1, 7, 2;std::cout << "Row's maximum: " << std::endl<< mat.rowwise().maxCoeff() << std::endl;
}//输出
Row's maximum:
9
7
注意,列操作返回一个行向量,而行操作返回一个列向量。
1. 将部分约减与其他业务相结合
也可以使用部分还原的结果进行进一步处理。这里是另一个例子,它找到一个列,其元素的和在一个矩阵中是最大的。使用按列部分约减,可以将其编码为:
#include <iostream>
#include <Eigen/Dense>int main()
{Eigen::MatrixXf mat(2,4);mat << 1, 2, 6, 9,3, 1, 7, 2;Eigen::Index maxIndex;float maxNorm = mat.colwise().sum().maxCoeff(&maxIndex);std::cout << "Maximum sum at position " << maxIndex << std::endl;std::cout << "The corresponding vector is: " << std::endl;std::cout << mat.col( maxIndex ) << std::endl;std::cout << "And its sum is is: " << maxNorm << std::endl;
}//输出
Maximum sum at position 2
The corresponding vector is:
6
7
And its sum is is: 13
前面的示例通过colwise()访问器对每个列应用sum()约简,获得一个大小为1x4的新矩阵。
因此,如果
然后
最后应用maxCoeff()约简来获得找到最大和的列索引,在本例中是列索引2(第三列)。
四、广播
广播背后的概念类似于部分约简,不同之处在于广播构建了一个表达式,其中矢量(列或行)通过在一个方向上复制来解释为矩阵。
一个简单的例子是向矩阵中的每一列添加一个特定的列向量。
这可以通过以下方式实现:
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::MatrixXf mat(2,4);Eigen::VectorXf v(2);mat << 1, 2, 6, 9,3, 1, 7, 2;v << 0,1;//add v to each column of mmat.colwise() += v;std::cout << "Broadcasting result: " << std::endl;std::cout << mat << std::endl;
}//输出
Broadcasting result:
1 2 6 9
4 2 8 3
我们可以用两种等价的方式来解释指令mat.colwise() += v。它把向量v加到矩阵的每一列。或者,它可以被解释为将向量v重复四次以形成一个4乘2的矩阵,然后将其添加到mat中:
操作符-=、+和-也可以按列和按行使用。在数组上,我们还可以使用操作符*=、/=、*和/来执行按列或按行计算的系数乘法和除法。这些运算符在矩阵上是不可用的,因为不清楚它们会做什么。如果要将矩阵mat的第0列与v(0)相乘,第1列与v(1)相乘,以此类推,则使用mat = mat * v. asdiagonal()。
重要的是要指出,要按列或按行添加的向量必须是vector类型,而不能是Matrix。如果不满足这个条件,则会得到编译时错误。这也意味着广播操作只能在使用矩阵操作时应用于Vector类型的对象。这同样适用于Array类,其中等价于VectorXf的是ArrayXf。与往常一样,不应该在同一个表达式中混合使用数组和矩阵。
要按行执行相同的操作,我们可以这样做:
#include <iostream>
#include <Eigen/Dense>using namespace std;
int main()
{Eigen::MatrixXf mat(2,4);Eigen::VectorXf v(4);mat << 1, 2, 6, 9,3, 1, 7, 2;v << 0,1,2,3;//add v to each row of mmat.rowwise() += v.transpose();std::cout << "Broadcasting result: " << std::endl;std::cout << mat << std::endl;
}//输出
Broadcasting result: 1 3 8 123 2 9 5
1. 将广播与其他业务相结合
广播还可以与其他操作相结合,例如矩阵或数组操作、缩减和部分缩减。
现在已经介绍了广播、约简和部分约简,我们可以深入到一个更高级的例子中,在矩阵m的列中找到向量v的最近邻居。欧几里德距离将在这个例子中使用,通过名为squaredNorm()的部分约简计算欧几里德距离的平方:
#include <iostream>
#include <Eigen/Dense>int main()
{Eigen::MatrixXf m(2,4);Eigen::VectorXf v(2);m << 1, 23, 6, 9,3, 11, 7, 2;v << 2,3;Eigen::Index index;// find nearest neighbour(m.colwise() - v).colwise().squaredNorm().minCoeff(&index);std::cout << "Nearest neighbour is column " << index << ":" << std::endl;std::cout << m.col(index) << std::endl;
}//输出
Nearest neighbour is column 0:
1
3
完成这项工作的线是
(m.colwise() - v).colwise().squaredNorm().minCoeff(&index);
具体解析如下:
- m.colwise() - v是一个广播操作,从m的每一列中减去v。这个操作的结果是一个新的矩阵,其大小与矩阵m相同:
-
(m.colwise() - v).colwise(). squarednorm()是一个部分约简,按列计算平方范数。这个操作的结果是一个行向量,其中每个系数是m和v中每列之间的欧几里德距离的平方:
-
最后,使用minCoeff(&index)来获得m中最接近v的欧氏距离列的索引。