分布式找环算法
参考
https://www.researchgate.net/publication/283642998_Distributed_cycle_detection_in_large-scale_sparse_graphs
- 从每个起点出发,将自己身上的id发给邻居;
- 将自己上一轮收到的点id并加上自己的点id,将此路径继续发送给邻居;
- 不断的将上一轮收到的路径加上自己的点id,将此路径继续发送给邻居;
- 每一轮中,没有收到路径的节点,将此节点删除;
- 当某一点收到的路径中包含自己的id,则判断此点id是否为路径中的最小值,若是,则计一环;
- 当某一点收到的路径中包换自己的id,此路径就会被砍掉,不再继续传递。
参考这篇分布式找环算法的论文,其中主要方法是用 bfs 把每个点当前点存储的路径发送给邻居,在分布式的情况下需要通过文件传输给邻居。主要针对的是稀疏图,所以对我们资金链路图(点:边=1:1000)、社交图(1:30)并不合适,并且此方法会造成环重复记录。通过此论文,我们进行一些改进找到适合的方式进行找环。
剪枝方式
避免在找环过程中走了重复的路径,所以在论文的基础上,提出了一些剪枝的方式。
拓扑排序
假如点的出度或者入度为0,则此点肯定不能成环,所以可以将这类点从起点中排除掉,因此在去掉此点的过程中也会出现新的出度或入度为0的点。可以迭代将此类点都去掉。
起点id最小
由于每个点都会作为起点,并且每个环都会被环上的每个点找一遍,所以就会造成重复找环的问题。
可以采用当起点出发,每传递到下一个节点的时候,就判断是否起点比当前点小,如果不是,则这条路径砍掉。按照这样子的方式最后找到的环就会是此环上id最小的点找到的。
起点的前驱id比起点id大
在上条起点id最小的情况下,其实走到最后一步是不允许起点的前驱id比起点id小,所以与其走到最后一步判断出来,可以提前在选起点的时候就先判断起点的前驱,这样子可以排除掉一些起点,提高效率。
此方法不能以起点就作为环上的一点,不然会丢失一些环。
因此遇到与路径中有相同点的时候,即成环,会有一些重复记录的环。
优点:减少起点的个数,提高效率
缺点:无法判断成环是否唯一,所以会重复计环
算法执行
结合以上三种剪枝方式,可按照bfs、dfs实现找环
BFS
传路径
按照论文中的方法结合以上三种剪枝方式,传路径属于容易理解且好实现的方法,但是问题在于每次存储的路径量为n*e^d(n:起点个数、e:平均边数、d:深度),会随着找环深度,路径会时平均边数的指数倍增加。因此此方法适合平均边数小于等于1的稀疏图。
在1400点 196w边的图中(2台128g的机器)进行分布式执行,其中按照拓扑排序过滤掉185个起点
根据以下结果可以看出到深度为4时,存储量就过大导致程序无法执行。
深度 | 过程文件大小 | 总环数 | 时间 |
---|---|---|---|
2 | 1MB | 6.1w | 1.5s |
3 | 343MB | 1270w | 86s |
4 | 75GB | — | — |
传起点
传起点会大大减少存储和传输的量,此方法适用于记录每个点的成环个数,被短路的环不会被记录在内。
在1400点 196w边的图中(2台128g的机器)进行分布式执行,其中按照拓扑排序过滤掉185个起点
根据以下结果可以看出到深度为4时,由于被短路的问题,使其环数已达到平稳
深度 | 环数最多的点id | 环数最多的点的环数 | 时间 |
---|---|---|---|
2 | 1275 | 600 | 10s |
3 | 937 | 3.44w | 43s |
4 | 937 | 3.45w | 48s |
5 | 937 | 3.45w | 48s |
DFS
按照dfs的方式并结合以上三种剪枝方式,每次只存储当前的路径,执行完成之后再回退,所以不会造成存储和传输上的压力,但是轮次也会随着边的数量而增加,尤其是在分布式的情况下,每执行一个点就要去传输一次文件,会造成大量小文件并发传输,端口不足,效率比bfs低。