在C中将edgelist转换为压缩稀疏行

时间:2012-10-19 07:58:46

标签: c sparse-matrix

我有一个包含数百万条边的无向边列表。 10x10稀疏邻接矩阵的无向边列表的简化示例:

0 2
0 9
2 8
6 9

我想将边缘列表转换为压缩稀疏行(definition)格式。这意味着读取边缘列表并写入三个数组:值(在我的情况下总是“1”),Column_Index和Row_Pointer。

读取示例边缘列表,我可以轻松地重建第0行:它在第2和第9列中有一个“1”。然后,第一行没有非零条目。

问题

对于第2行,因为边是无向的,所以我假设在第0列和第8列中有一个“1”。但是列表中不存在“2 0”条目。我想这个信息已经编码在“0 2”条目中。

我可以读取部分构造的压缩稀疏行数组以查看“2 0”条目是否存在,但对于包含数百万条目的大型边缘列表,这不起作用。

问题

我该如何解决这个问题?或者我的方法是错的?

2 个答案:

答案 0 :(得分:0)

您可以扫描边缘列表,交换索引,以便每个(i, j)始终为i < j。这是你在O(N)。

您还需要一个排序边列表,这是O(N log N)。获得排序边缘列表后,可以将其存储为Symmetric-CSR格式。在阅读单元格(y,x)时,如果y > x,您可以交换yx。然后,您阅读row_pointer[y]row_pointer[y+1],让它们为PaPb,然后在CSR[i]和{{1}之间开始扫描Pa }};如果Pb&gt; = x(根据=或&gt;找到或未找到),或CSR[i](未找到),则退出。

您还可以生成i == Pb的第二个边缘列表,并对其进行排序。此时,您可以同时扫描两个边,并生成CSR列表而无需对称。

j > i

上述算法可以改进,观察任何给定的j0 = j1 = N+1 # i-th row: # we are scanning NodesIJ[ij] and NodesJI[ji]. If NodesIJ[ij][0] == i j0 = NodesIJ[ij][1] If NodesJI[ji][0] == i j1 = NodesIJ[ji][1] if (j0 < j1) j = j0 j0 = N+1 ij++ else if (j1 == N+1) # Nothing more on this row, and j0 and j1 are both N+1. i++; continue j = j1 j1 = N+1 ji++ # We may now store (i,j) in CSR if (-1 == row_ind[i]) row_ind[i] = csr; col_ind[csr++] = j ,如果存在ipqNodesIJ[p] = i,始终为NodesJI[q] = i,因为前一个列表描述了右上三角形而后一个描述了左下角。因此,我们可以扫描NodesJI,直到NodesIJ[p][1] > NodesJI[q][1]为i,然后转到NodesJI[p][0]

我们也可以避免总是检查NodesJI[q]初始化,注意如果row_ind索引没有改变,那么该行为空,相应的值可以是-1(或N + 1,或者其他我们想要的“无效”值,否则它必须是csr的前一个值。

csr

以上以O(N log N)运行。

另一种方法是分配矩阵,将边缘列表解码到矩阵中并将其解析为CSR。这是O(N),但可能需要太多内存;对于列表大小为N,您可能最多有N ^ 2(或(N / a)^ 2,a是平均连接数)单元格。数百万个边缘的列表可能很容易需要几十GB的存储空间。

答案 1 :(得分:0)

您的数据采用所谓的三元组稀疏格式,其中您有明确的行列索引。你想要做的是两件事:

  • 将三元组格式转换为CRS
  • 由于某些条目可能会丢失而你想要一个非方向图,你的最终矩阵将是A = A + A'(转置)

要跟进您的示例,最终A将包含(0,2)(2,0)条目。

转换可以通过多种方式完成。看看一个非常成熟的SuiteSparse library,特别是cholmod_triplet.c文件,它实现了你需要的功能。基本上,它是在行和列索引上使用两阶段桶排序执行的,同时删除重复项。如果您对处理大型数据集感兴趣,此算法具有线性复杂性。转换和许多其他有用的稀疏操作也可以使用该包完成。