我有一个包含数百万条边的无向边列表。 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”条目是否存在,但对于包含数百万条目的大型边缘列表,这不起作用。
问题
我该如何解决这个问题?或者我的方法是错的?
答案 0 :(得分:0)
您可以扫描边缘列表,交换索引,以便每个(i, j)
始终为i < j
。这是你在O(N)。
您还需要一个排序边列表,这是O(N log N)。获得排序边缘列表后,可以将其存储为Symmetric-CSR格式。在阅读单元格(y,x)
时,如果y > x
,您可以交换y
和x
。然后,您阅读row_pointer[y]
和row_pointer[y+1]
,让它们为Pa
和Pb
,然后在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
,如果存在i
和p
,q
和NodesIJ[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)
您的数据采用所谓的三元组稀疏格式,其中您有明确的行列索引。你想要做的是两件事:
要跟进您的示例,最终A将包含(0,2)
和(2,0)
条目。
转换可以通过多种方式完成。看看一个非常成熟的SuiteSparse library,特别是cholmod_triplet.c文件,它实现了你需要的功能。基本上,它是在行和列索引上使用两阶段桶排序执行的,同时删除重复项。如果您对处理大型数据集感兴趣,此算法具有线性复杂性。转换和许多其他有用的稀疏操作也可以使用该包完成。