我正在尝试实现MATLAB函数sparse
的功能。
在特定索引处的稀疏矩阵中插入一个值,以便:
如果矩阵中已存在具有相同索引的值,则会添加新旧值。
否则新值将附加到矩阵中。
函数addNode
正确执行,但问题是它非常慢。我在循环中调用此函数大约100000次,程序运行时间超过3分钟。而MATLAB在几秒钟内完成了这项任务。有没有办法优化代码或使用stl算法而不是我自己的函数来实现我想要的?
struct SparseMatNode
{
int x;
int y;
float value;
};
std::vector<SparseMatNode> SparseMatrix;
void addNode(int x, int y, float val)
{
SparseMatNode n;
n.x = x;
n.y = y;
n.value = val;
bool alreadyPresent = false;
int i = 0;
for(i=0; i<SparseMatrix.size(); i++)
{
if((SparseMatrix[i].x == x) && (SparseMatrix[i].y == y))
{
alreadyPresent = true;
break;
}
}
if(alreadyPresent)
{
SparseMatrix[i].value += val;
if(SparseMatrix[i].value == 0.0f)
SparseMatrix.erase(SparseMatrix.begin + i);
}
else
SparseMatrix.push_back(n);
}
答案 0 :(得分:5)
稀疏矩阵通常不会像您尝试的那样存储为三元组的向量。
MATLAB(以及许多其他库)使用压缩稀疏列(CSC)数据结构,这对静态矩阵非常有效。 MATLAB函数sparse
也不一次构建矩阵一个条目(正如您所尝试的那样) - 它需要一个三元组条目的数组并打包整个序列成CSC矩阵。如果您正在尝试构建静态稀疏矩阵,那么这就是您的选择。
如果你想要一个支持有效插入和删除条目的动态稀疏矩阵对象,你可以查看不同的结构 - 可能是std::map
三元组,或列列表 - 参见{{3}有关数据格式的更多信息。
此外,还有很多好的图书馆。如果你想做稀疏矩阵运算/因子化等 - here是一个不错的选择,否则SuiteSparse也有很好的稀疏支持。
答案 1 :(得分:3)
稀疏矩阵通常存储在压缩稀疏行(CSR)或压缩稀疏列(CSC,也称为Harwell-Boeing)格式中。 MATLAB默认使用CSC,IIRC,而大多数稀疏矩阵包倾向于使用CSR。
无论如何,如果这是用于生产用途而不是学习练习,我建议使用支持稀疏矩阵的矩阵包。在C ++世界中,我最喜欢的是Eigen。
答案 2 :(得分:2)
您是否尝试过对稀疏节点的矢量进行排序?每次添加节点时,执行线性搜索都会变得昂贵。您可以插入到位并始终执行二进制搜索。
答案 3 :(得分:1)
第一个认为突出的是你正在实现自己的功能来寻找元素:这就是std::find
的用途。所以,而不是:
bool alreadyPresent = false;
int i = 0;
for(i=0; i<SparseMatrix.size(); i++)
{
if((SparseMatrix[i].x == x) && (SparseMatrix[i].y == y))
{
alreadyPresent = true;
break;
}
}
你应该写:
auto it = std::find(SparseMatrix.begin(), SparseMatrix().end(), Comparer);
其中Comparer
是一个比较两个SparseMatNode
对象的函数。
但主要的改进将来自使用适当的容器。使用关联容器会更好,而不是std::vector
。这样,查找元素的复杂度只有O(logN)
而不是O(N)
。您可以按照以下方式轻松修改SparseMatNode
课程:
typedef std::pair<int, int> Coords;
typedef std::pair<const Coords, float> SparseMatNode;
当然,您可以在类中包含此typedef以提供更好的界面。
然后:
std::unordered_map<Coords, float> SparseMatrix;
这样你可以使用:
auto it = SparseMatrix.find(std::make_pair(x, y));
更有效地找到元素。
答案 4 :(得分:-1)
由于稀疏矩阵可能很大且需要压缩,因此您可以使用std::unordered_map
。我假设矩阵索引(x
和y
)总是正面的。
#include <unordered_map>
const size_t MAX_X = 1000*1000*1000;
std::unordered_map <size_t, float> matrix;
void addNode (size_t x, size_t y, float val)
{
size_t index = x + y*MAX_X;
matrix[index] += val; //this function can be still faster
if (matrix[index] == 0) //using find() / insert() methods
matrix.erase(index);
}
如果您的系统无法使用std::unordered_map
,则可以尝试std::tr1::unordered_map
或stdext::hash_map
...
如果您可以使用更多内存,请使用double
代替float
,这样可以提高处理速度。