Eigen库:如何更新SparseMatrix的值?能否通过三元组初始化生成M'?
嘿,我来帮你梳理下Eigen里SparseMatrix的这两个问题,都是日常用稀疏矩阵时很常见的需求~
更新稀疏矩阵的值分几种场景,不同场景的最优操作不一样:
修改已存在的非零元素
如果确定要修改的位置已经是矩阵的非零元素,可以直接用coeffRef(i,j)或者operator()(i,j),后者更简洁。比如:Eigen::SparseMatrix<double> M(3,3); // 先插入初始元素 M.insert(0,0) = 1.0; M.insert(1,1) = 2.0; M.makeCompressed(); // 压缩矩阵(可选,优化存储和访问) // 修改已存在的元素 M.coeffRef(0,0) = 3.0; M(1,1) = 4.0; // 效果和上面一致注意:如果用
operator()(i,j)访问原本为0的位置,Eigen会自动插入一个零元素到稀疏矩阵中,这可能会增加不必要的存储开销,所以不确定元素是否存在时,别随便用这个方式。修改可能不存在的元素(或插入新元素)
如果不确定目标位置有没有非零元素,建议先查找再操作,避免意外插入零元素:int row = 2, col = 2; double new_val = 5.0; auto it = M.find(row, col); if (it != M.end()) { *it = new_val; // 元素存在,直接修改 } else { M.insert(row, col) = new_val; // 元素不存在,插入新值 }要是你需要批量插入/修改多个元素,也可以先收集所有要更新的三元组,再用
setFromTriplets重新构建矩阵(下面第二个问题会详细说)。批量更新元素
如果有大量元素需要修改或添加,最高效的方式是先把所有需要保留的元素(原矩阵的+新增/修改的)整理成三元组列表,然后重新初始化矩阵,这种方式比逐个插入更高效。
当然可以,而且这是构建修改版稀疏矩阵的高效方式,步骤很清晰:
从原矩阵M提取现有非零元素的三元组
遍历M的所有非零元素,把它们转换成Triplet对象存入列表:std::vector<Eigen::Triplet<double>> triplets; triplets.reserve(M.nonZeros()); // 预分配空间,提升效率 for (int k = 0; k < M.outerSize(); ++k) { for (Eigen::SparseMatrix<double>::InnerIterator it(M, k); it; ++it) { // 把每个非零元素转换成三元组(行,列,值) triplets.emplace_back(it.row(), it.col(), it.value()); } }修改或添加新的三元组
你可以直接在列表里修改已有三元组的值,或者添加新的三元组来更新矩阵结构:// 示例1:修改原矩阵中(0,0)位置的值 for (auto& t : triplets) { if (t.row() == 0 && t.col() == 0) { t.value() = 10.0; break; } } // 示例2:添加一个新的非零元素(2,0) triplets.emplace_back(2, 0, 7.0);用三元组初始化M'
最后用setFromTriplets方法构建新的稀疏矩阵M':Eigen::SparseMatrix<double> M_(M.rows(), M.cols()); // 保持和M相同的尺寸 M_.setFromTriplets(triplets.begin(), triplets.end()); M_.makeCompressed(); // 压缩矩阵,优化后续操作
这种方式的好处是,你可以完全控制M'的所有非零元素,而且setFromTriplets内部会高效地处理重复的三元组(默认保留最后一个出现的值),非常适合需要对原矩阵做批量修改的场景。
内容的提问来源于stack exchange,提问作者Chris

