使用std :: vector的稀疏矩阵的缓慢性能

时间:2012-11-19 07:26:32

标签: c++ sparse-matrix

我正在尝试实现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);
}

5 个答案:

答案 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。我假设矩阵索引(xy)总是正面的。

#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_mapstdext::hash_map ...

如果您可以使用更多内存,请使用double代替float,这样可以提高处理速度。