本题的任务是kmax-truss问题的求解和算法优化。这个对于K-Truss最著名方法就是Truss Decomposition,通过Peeling的方式一层一层对于图进行剥离,从而得到$k_{max}$的方式。我再此基础上,利用了一些K-Truss的性质,能够快速的确定K-Truss的下界,之后再通过Peeling的方式得到最终的结果。
这是前两年BDCI的题目,也是K-Truss求解得一个重要部分。
一般的方法是通过将图中的双向边转为单向边,之后将其按照拓扑序排列,从而减少了比较的次数,并且通过这种单向边的方式,对于每一个三角形恰好统计一次。
而我使用的是Bitmap,通过位运算的方式,求出两个顶点之间的相同结点的的个数,从而统计三角形的个数。虽然牺牲了使用单向边的常数优势(因为使用双向边,一个三角形会被重复计算三次,因此计数三角形的时候要除以3),但是利用了位运算的优势。同时,在针对于一部分数据特征,比如当相邻结点的编号非常密集或者节点特别多的情况,可以通过popcnt的方式,成块的统计。
这一步在K-Truss的求解中有两个作用。
在Triangle Counting的步骤之中,我们已经计算了每一条边的Sup,之后就是Peeling的步骤了,大体的算法步骤如下。
Step 0: 我们先初始化$k$为2。
Step 1: 将所有的边$Sup(e)\le k-2$的边加入$Q_{cur}$中,这些是将要被处理的边的集合,这是Peel的第一步。(特别的,当$Sup(e)$为0时,这是一个特殊的状态,这边$e$没有不能直接构成三角形的一条边,在删除这一条边之后,不会影响任何一个边的$Sup(e)$,因此不需要进一步的操作)。
Step 2: 计算有$Q_{cur}$中边$e$的所影响的其他的边的$Sup(e')$的值并且对$Sup(e')$更新。倘若其他边的个数由于$Q_{cur}$中边的受到影响,使得$Sup(e')\le k-2$,那么就将其放入$Q_{next}$中。
Step 3:将$Q_{cur}$中的边CSR中进行删除操作,最后将$Q_{next}$的加入到到$Q_{cur}$中,跳到Step 2 。
Step 4 : 这是已经没有边$e$的$Sup(e)\le k-2$,判断删除的边的个数是否为总的边的个数$|E|_{del} == |E|$,则退出,否则$k++$ ,跳到Step 1。
与之前的赛题不同,K-Truss的求解伴随着边的删除,倘若不能够及时得对CSR表进行处理,那么在处理的时候,就会有CSR中则会有很多被删除的边,这会大大降低程序在后期运行的时候(即大量的边已经被删除)的运行效率。因此需要及时的对CSR数组进行压缩。
但同时,压缩的开销也是很大的,如果我们在删除几条边之后立刻进行压缩,那么也是得不偿失的。因此我们算法的是在每次k增加的时候,进行一次整体的压缩,而在每次删除$Q_{cur}$的时候通过标记来进行删除。
在上文所说的Truss Decomposition中,需要从$k=2$一层一层往上剥离,可以通过Truss的性质快速确定下界。因此,可以通过计算三角形的迭代去掉不需要用到的边,之后从$k_{max}$的下界开始一层一层向上的剥离。
对于Truss图有两条非常明显的性质,根据$Sup$的定义可得$3T = \Sigma Sup(e)$,而如果对于一个K-Truss的图来说其中的每一条肯定满足,$Sup(e)>=k-2$,因此$3T\ge (k-2)|E|$。同时,也可以得到Truss的下界,对于整张图来说,显然满足$T \le |E|(k_{max}-2)$ ,$k_{max} \ge \dfrac{T}{|E|} + 2$。
CPU:Intel Xeon Platinum 8260 CPU (8核心),由于使用的是共享云服务器,因此在IO的开销会非常大,因此单独计算。
数据集名称 | 顶点数 | 边数 | Edge in |
IO时间 | Truss求解时间 | |
---|---|---|---|---|---|---|
s18.e16.rmat.edgelist | 262144 | 7604036 | 164 | 225529 | 0.27s | 3.12s |
s19.e16.rmat.edgelist | 524288 | 15459034 | 223 | 334934 | 2.76s | 7.89s |
cit-Patents | 3774768 | 33037894 | 36 | 2625 | 6.45s | 4.92s |
soc-LiveJournal | 4847571 | 85702474 | 362 | 72913 | 17.58s | 20.98s |
make
./main -f *.tsv
make clean && make
设置线程数: 在打开include/tools.h文件,修改其第31行,可以将其修改成设置的线程数,默认为最大线程数。
static uint32_t ThreadNum = omp_get_num_procs();