在稀疏图(矩阵)的表示中查找元素(邻居)

时间:2012-09-25 22:08:07

标签: c sparse-matrix

我有一个大的稀疏图,我将其表示为邻接矩阵(100k×100k或更大),存储为边数组。具有(非稀疏)4乘4矩阵的示例:

0 7
4 0

example_array = [ [7,1,2], [4,2,1] ]

E.g。 [4,1,2]表示从节点1到节点2有一个带有值/权重4的有向边。在矩阵术语中,这基本上是[值,行,列]。

此外,这个“边缘数组”将按第一个元素排序。在上面的示例中,排序后,数组变为

example_array = [ [4,2,1], [7,1,2] ]

问题

对于某个值i,需要查找此排序“边缘数组”中的所有元素,其中第二个值等于i。即查找jexample_array[j][1] = i

我的初步实现是简单地迭代数组中的所有元素,将每个元素的第二个值与i进行比较。这在计算上是昂贵的,因为可能仍有很多(例如500k)元素要循环。

问题

有更有效的方法吗?我不介意使用矩阵/图的不同表示。我是用C写的。

其他信息

这基本上是找到节点i的所有邻居及其边权重。即从边缘列表中查找从i到另一个节点的所有有向边。

3 个答案:

答案 0 :(得分:0)

如果您不介意更改表示,那么如果您按第二个元素排序,那么计算密集程度就会降低,因为这样您就可以单步执行,一旦找到一个大于i的元素就可以完成。在最坏的情况下,最好的算法是O(n),但是如果按第二个元素排序,那么在预期时这将在n / 2时间内运行。

答案 1 :(得分:0)

使用指针有什么问题?

// A list of edges emanating from one node.
typedef struct {
    int weight;    
    int nodeId;    // The target node
    Edge *next;    // Next edge in the list
} Edge;

typedef struct {
    int nodeId;
    Edge *edges;   // This node's edge list
} Node;

// Now just store all your nodes in an array
Node *example_array[MAX_NODES];

在节点上插入边时,按重量对edges列表执行有序插入。现在,要回答关于从某个节点开始查找所有边的问题,只需在数组中查找该节点并遍历其边列表即可。奖励是您按排序顺序访问其边缘,而无需搜索图表的任何其他部分。

答案 2 :(得分:0)

您可能应该使用稀疏压缩行存储来实现此目的。简而言之,您逐行存储矩阵,因此您不需要保留两个(行,列)索引。而是保留一个行指针,即一个数组,它告诉你给定行在内存中的起始位置。然后保留列向量(col_ind),它告诉您该行中非零列的位置,并存储相应的值(val)。这降低了存储要求,但也加快了矩阵搜索,因为每行的col_ind都已排序。因此,您可以直接访问每个矩阵行,并且可以使用二分法或您选择的任何其他排序列表搜索快速本地化每行中的条目。

可以使用插入到排序列表中快速创建CRS矩阵,例如,如果你已经显式构造了每个矩阵条目的(i,j)坐标,那么就进行排序。在MATLAB中,您可以使用“稀疏”功能执行此操作。如果您不想自己编写代码并需要库,请查看SuiteSparse by Tim Davis

有关CRS格式的简要说明,请参阅here,但还有成千上万的其他来源。

编辑您可以使用修改后的CRS存储轻松完成所需操作。首先,您需要通过按值而不是列索引对每行内的列进行排序来创建矩阵,这通常是这样做的。这意味着每行的最小值存储为每行中的第一个条目。然后,要查找全局最小值,请搜索所有行的第一个条目(O(n)复杂度)。通过读取包含最小值的行中的第一列索引,知道您在恒定时间内获得相应的列索引。你可以做到这一切,因为你知道你的行指针在内存中的行开始。

您可以查看this code。它是用于在C中实现的matlab的一组mex文件。您感兴趣的是sparse_create_mex.c。它使用(i,j,value)迭代添加到排序列表中来创建稀疏矩阵结构。您需要稍微修改排序列表 - 现在它们是针对整数列索引和双值实现的。由于排序列表是作为宏模板实现的,因此您只需要声明一个新的排序列表类型(请参阅sorted_list.h和sorted_list_templates.h)。