【问题标题】:Finding elements (neighbors) in a representation of a sparse graph (matrix)在稀疏图(矩阵)的表示中查找元素(邻居)
【发布时间】:2012-09-27 16:23:29
【问题描述】:

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

0 7
4 0

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

例如[4,1,2] 表示从节点 1 到节点 2 有一条有向边,其值/权重为 4。在矩阵术语中,这本质上是 [ value, row, column ]。

此外,这个“边数组”将按第一个元素排序。在上面的例子中,经过排序,数组变成了,

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

问题

对于某个值i,需要在这个排序的“边数组”中找到第二个值等于i的所有元素。即找到j,这样example_array[j][1] = i

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

问题

有没有更有效的方法来做到这一点?我不介意使用矩阵/图形的不同表示。我是用 C 写的。

其他信息

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

【问题讨论】:

  • 这是一个无向图吗?每条边是否在 [4,2,1] 和 [4,1,2] 中表示两次?
  • @ajon - 定向和加权。

标签: c sparse-matrix


【解决方案1】:

您可能应该为此目的使用稀疏压缩行存储。简而言之,您逐行存储矩阵,因此您不需要保留两个(行,列)索引。相反,您保留一个行指针,即一个数组,它告诉您给定行在内存中的起始位置。然后你保留列向量 (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)。

【讨论】:

  • CRS表示可以按值排序吗?即从最小到最大列出第一行的值。我想必须调整列索引和行指针。有没有我可以做到这一点的例子? (我用谷歌搜索)
  • @Legendre 通常这些值不是稀疏存储的主要关注点,而是行内的列索引。但是,如果您愿意,当然没有什么可以阻止您按值排序。唯一的问题是在矩阵中添加新的/更新现有的非零条目。如果您的列索引排序,要添加一个新的非零条目,您只需将其添加到行列表的末尾即可。但是,如果要更新现有值,则需要查看所有列索引以查看该值是否已经存在。但您当然可以根据自己的需要进行调整。
  • @Legendre 我肯定会建议这样做,而不是一些链表方法。我至少谈论性能差异的数量级。我不确定示例。 CRS 非常受欢迎,因此您可以找到大量关于它的论文。你想要什么样的例子?
  • 给定一个稀疏矩阵,我需要选择具有最小非零值的条目,如果它在第 j 列,则找到第 j 行的所有非零条目。将矩阵视为有向加权图,我将找到最小的边权重,找到它指向的节点(称为“v”),然后找到“v”的非零边权重。在我的示例中,最小的非零值是 4,在第 1 列中,那么第 1 列的非零条目是 7。我需要对下一个最小的值执行此操作,以此类推。
  • 有CRS排序的例子吗?一直在谷歌搜索,没有看到任何东西。对于前面提到的目的,我需要对第一个数组(包含值的数组)进行排序。但这会弄乱 column_index 和 row 指针(我需要在排序后告诉值来自哪一列 + 行)。
【解决方案2】:

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

【讨论】:

  • 我需要按第一个元素排序的边缘列表。之后,我需要进行调查。我的意思是我不介意从边缘列表表示切换到其他表示。 (仍然需要对权重/条目进行排序)
  • 如果节点按第二个元素排序,则两次二进制搜索就足以找到单元格的范围。总复杂度 nlog n (排序) + 2*log n = nlog n
  • n*log n 用于std qsort-如果数据太多而无法放入内存可能需要使用归并排序
【解决方案3】:

使用指针有什么问题?

// 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 列表上按权重进行有序插入。现在,要回答有关查找从某个节点开始的所有边的问题,您只需在数组中查找该节点并遍历其边列表。好处是您可以按排序顺序访问它的边,而无需搜索图表的任何其他部分。

【讨论】:

  • 你指的是这个:cs.bu.edu/teaching/c/graph/linked?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 1970-01-01
  • 2016-07-12
相关资源
最近更新 更多