复杂网络搜索特定结构的环

复杂网络环搜索算法

有向异构网络搜索特定结构的环or路径

1 问题描述

​ 有向异构网络,即网络中的边是有方向的,且节点分为不同类型,拥有不同属性。要从一个复杂网络中搜索出所有满足某个特定结构的环,特定结构的环即环中每个节点的类型确定。

​ 设有图G=(V,E,Lv)G=(V,E,L_v)G=(V,E,Lv)VVV是节点(node)集合, EEE是边(edge)集合,且E⊂V×VE\subset V\times VEV×V ,用LvL_vLv表示节点上的标签集合,且Lv={l1,l2,...,lq}L_v=\{l_1,l_2,...,l_q\}Lv={l1,l2,...,lq},需求是要找出所有某种结构的环,比如Vl1→Vl2→Vl3→Vl1V_{l_1}\rightarrow V_{l_2}\rightarrow V_{l_3}\rightarrow V_{l_1}Vl1Vl2Vl3Vl1这种形式的环。

在这里插入图片描述

​ 比如上图中,要找出所有的Vl1→Vl2→Vl3→Vl1V_{l_1}\rightarrow V_{l_2}\rightarrow V_{l_3}\rightarrow V_{l_1}Vl1Vl2Vl3Vl1形式的环,共有两个,分别如下:

在这里插入图片描述

2 邻接矩阵与路径数

​ 下面以4种节点类型的一个图为例介绍算法,节点的标签分别为A,B,C,DA,B,C,DA,B,C,D,要找出所有A→B→C→AA\rightarrow B\rightarrow C \rightarrow AABCA类型的环,图如下所示。

在这里插入图片描述

​ 图中共有4个A→B→C→AA\rightarrow B\rightarrow C \rightarrow AABCA类型的环,分别为:

  1. A1→B1→C1→A1A1\rightarrow B1\rightarrow C1 \rightarrow A1A1B1C1A1
  2. A2→B1→C2→A2A2\rightarrow B1\rightarrow C2 \rightarrow A2A2B1C2A2
  3. A2→B2→C2→A2A2\rightarrow B2\rightarrow C2 \rightarrow A2A2B2C2A2
  4. A2→B2→C3→A2A2\rightarrow B2\rightarrow C3 \rightarrow A2A2B2C3A2

在这里插入图片描述

​ 首先建立邻接矩阵,邻接矩阵构建规则如下:


aij={1    存在Vi→Vj边0 不存在Vi→Vj边 a_{ij}=\left\{ \begin{aligned} 1 \ \ \ \ 存在V_i\rightarrow V_j边 \\ 0 \ 不存在V_i\rightarrow V_j边 \end{aligned} \right. aij={1    存在ViVj0 不存在ViVj


​ 按ABCDABCDABCD的类型顺序,上述图的邻接矩阵如下:


M=[0    0    1    0    0    0    0    00    0    1    1    0    0    0    00    0    0    0    1    1    0    00    0    0    0    0    1    1    01    0    0    0    0    0    0    00    1    0    0    0    0    0    10    1    0    0    0    0    0    00    0    0    0    1    0    0    0]⇒MA1A2B1B2C1C2C3D1A100100000A200110000B100001100B200000110C110000000C201000001C301000000D100001000 M=\begin{gathered} \begin{bmatrix} 0\ \ \ \ 0\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\\ 0\ \ \ \ 0\ \ \ \ 1\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\\ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 1\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\\ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 1\ \ \ \ 1\ \ \ \ 0\\ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\\ 0\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 1\\ 0\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\\ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0\ \ \ \ 1\ \ \ \ 0\ \ \ \ 0\ \ \ \ 0 \end{bmatrix} & & & \Rightarrow & & & \begin{array}{c||cc|cc|ccc|c} M & A1 & A2 & B1 & B2 & C1 & C2 & C3 & D1 \\ \hline A1 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\ A2 & 0 & 0 & 1 & 1 & 0 & 0 & 0 & 0\\ \hline B1 & 0 & 0 & 0 & 0 & 1 & 1 & 0 & 0\\ B2 & 0 & 0 & 0 & 0 & 0 & 1 & 1 & 0\\ \hline C1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\ C2 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 1\\ C3 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\ \hline D1 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \end{array} \end{gathered} M=0    0    1    0    0    0    0    00    0    1    1    0    0    0    00    0    0    0    1    1    0    00    0    0    0    0    1    1    01    0    0    0    0    0    0    00    1    0    0    0    0    0    10    1    0    0    0    0    0    00    0    0    0    1    0    0    0MA1A2B1B2C1C2C3D1A100001000A200000110B111000000B201000000C100100001C200110000C300010000D100000100


​ 将矩阵按节点类型分块,可以分为16个子矩阵,由于我们要找的是A→B→C→AA\rightarrow B\rightarrow C \rightarrow AABCA类型的环,仅与A→BA\rightarrow BABB→CB\rightarrow CBCC→AC\rightarrow ACA 3个类型的边有关,因此我们仅关注这3种边构成的邻接矩阵,分别为:


MAB=MABB1B2A110A211        MBC=MBCC1C2C3B1110B2011        MCA=MCAA1A2C110C201C301 M_{AB}= \begin{array}{c|cc} M_{AB} & B_1 & B_2 \\ \hline A_1 & 1 & 0 \\ A_2 & 1 & 1 \end{array} \ \ \ \ \ \ \ \ M_{BC}= \begin{array}{c|ccc} M_{BC} & C_1 & C_2 & C_3 \\ \hline B_1 & 1 & 1 & 0 \\ B_2 & 0 & 1 & 1 \end{array} \ \ \ \ \ \ \ \ M_{CA}= \begin{array}{c|cc} M_{CA} & A_1 & A_2 \\ \hline C_1 & 1 & 0 \\ C_2 & 0 & 1 \\ C_3 & 0 & 1 \end{array} MAB=MABA1A2B111B201        MBC=MBCB1B2C110C211C301        MCA=MCAC1C2C3A1100A2011


​ 将MABM_{AB}MABMBCM_{BC}MBC相乘,得到MABCM_{ABC}MABC


MABC=MAB×MBC=MABB1B2A110A211×MBCC1C2C3B1110B2011=MABCC1C2C3A1110A2121 M_{ABC}=M_{AB}\times M_{BC}= \begin{array}{c|cc} M_{AB}& B_1 & B_2 \\ \hline A_1 & 1 & 0 \\ A_2 & 1 & 1 \end{array} \times \begin{array}{c|ccc} M_{BC} & C_1 & C_2 & C_3 \\ \hline B_1 & 1 & 1 & 0 \\ B_2 & 0 & 1 & 1 \end{array} = \begin{array}{c|ccc} M_{ABC} & C_1 & C_2 & C_3 \\ \hline A_1 & 1 & 1 & 0 \\ A_2 & 1 & 2 & 1 \end{array} MABC=MAB×MBC=MABA1A2B111B201×MBCB1B2C110C211C301=MABCA1A2C111C212C301


该矩阵表达了每个AAA类节点到每个CCC类节点的路径数,矩阵每个数字表示路径数量,比如第二行第二列的数字2,表示节点A2A_2A2到节点C2C_2C2并且经过BBB类节点的路径有2条。

解释一下:这个数字2由矩阵MABM_{AB}MAB的第2行与矩阵MBCM_{BC}MBC的第2列相乘得到,

其中MAB[A2][B1]=1M_{AB}[A_2][B_1]=1MAB[A2][B1]=1表示节点A2A_2A2到节点B1B_1B1有一条路径,MBC[B1][C2]=1M_{BC}[B_1][C_2]=1MBC[B1][C2]=1表示节点B1B_1B1到节点C2C_2C2有一条路径,因此A2A_2A2经过B1B_1B1到达C2C_2C2有一条路径;

MAB[A2][B2]=1M_{AB}[A_2][B_2]=1MAB[A2][B2]=1表示节点A2A_2A2到节点B2B_2B2有一条路径,MBC[B2][C2]=1M_{BC}[B_2][C_2]=1MBC[B2][C2]=1表示节点B2B_2B2到节点C2C_2C2有一条路径,因此A2A_2A2经过B2B_2B2到达C2C_2C2有一条路径;

所以1×1+1×1=21\times 1 + 1\times 1 = 21×1+1×1=2分别表示了上述2条路径。

​ 接下来将MABCM_{ABC}MABCMCAM_{CA}MCA相乘,得到MABCAM_{ABCA}MABCA


MABCA=MABC×MCA=MABCC1C2C3A1110A2121×MCAA1A2C110C201C301=MABCAA1A2A111A213 M_{ABCA}=M_{ABC}\times M_{CA}= \begin{array}{c|ccc} M_{ABC} & C_1 & C_2 & C_3 \\ \hline A_1 & 1 & 1 & 0 \\ A_2 & 1 & 2 & 1 \end{array} \times \begin{array}{c|cc} M_{CA} & A_1 & A_2 \\ \hline C_1 & 1 & 0 \\ C_2 & 0 & 1 \\ C_3 & 0 & 1 \end{array} = \begin{array}{c|cc} M_{ABCA} & A_1 & A_2 \\ \hline A_1 & 1 & 1 \\ A_2 & 1 & 3 \end{array} MABCA=MABC×MCA=MABCA1A2C111C212C301×MCAC1C2C3A1100A2011=MABCAA1A2A111A213


该矩阵表达了每个AAA类节点到每个AAA类节点的路径数,以第2行第2列的数字3为例,其表示节点A2A_2A2先经过BBB类节点再经过CCC类节点最终到达节点A2A_2A2路径有3条。

这个数字3由矩阵MABCM_{ABC}MABC的第2行与矩阵MCAM_{CA}MCA的第2列相乘得到,

其中MABC[A2][C1]=1M_{ABC}[A_2][C_1]=1MABC[A2][C1]=1表示节点A2A_2A2经过BBB类节点再到节点C1C_1C1有1条路径,MCA[C1][A2]=0M_{CA}[C_1][A_2]=0MCA[C1][A2]=0表示节点C1C_1C1到节点A2A_2A2没有直接路径,因此A2A_2A2经过BBB类节点和C1C_1C1节点到达A2A_2A2没有路径;

MABC[A2][C2]=2M_{ABC}[A_2][C_2]=2MABC[A2][C2]=2表示节点A2A_2A2经过BBB类节点再到节点C2C_2C2有2条路径,MCA[C2][A2]=1M_{CA}[C_2][A_2]=1MCA[C2][A2]=1表示节点C2C_2C2到节点A2A_2A2有1条路径,因此A2A_2A2经过BBB类节点和C2C_2C2节点到达A2A_2A22×1=22\times 1=22×1=2条路径;

MABC[A2][C3]=1M_{ABC}[A_2][C_3]=1MABC[A2][C3]=1表示节点A2A_2A2经过BBB类节点再到节点C3C_3C3有1条路径,MCA[C3][A2]=1M_{CA}[C_3][A_2]=1MCA[C3][A2]=1表示节点C3C_3C3到节点A2A_2A2有1条路径,因此A2A_2A2经过BBB类节点和C3C_3C3节点到达A2A_2A21×1=11\times 1=11×1=1条路径;

所以A2A_2A2先经过BBB类节点再经过CCC类节点最终回到A2A_2A2的路径共有:1×0+2×1+1×1=31\times 0 + 2\times 1 +1\times 1= 31×0+2×1+1×1=3条。

3 找出所有具体的环

​ 由第二节可以得到任意两个节点间,通过任意类型的节点的路径数,若关注环则只需要关注最终矩阵中的对角线元素,也就是自己回到自己的路径数。

​ 现在需要把每一个这种类型的环由哪些节点构成找出来,其实由上面矩阵相乘的内部分析已经可以看出每一个环了,但是如果网络节点非常多,像上面那样顺着相乘并且乘到哪都把相应的路径记录下来,可能在相乘了几个矩阵时,对应的路径会非常多,而实际上最终需要找的路径或环没有那么多,这会大量消耗计算空间和时间。

​ 举个极端例子,一个网络有4000个节点,其中A,B,C,DA,B,C,DA,B,C,D类型的节点各1000个,其中A→B,B→C,C→DA\rightarrow B,B\rightarrow C,C\rightarrow DAB,BC,CD均全连接,那么这三种类型的边各有1000000个,A→B→C→DA\rightarrow B\rightarrow C\rightarrow DABCD这种类型的路径将有101810^{18}1018条,然而,D→AD\rightarrow ADA的边一条都没有,因此A→B→C→D→AA\rightarrow B\rightarrow C\rightarrow D\rightarrow AABCDA类型的环一个都没有,于是前面计算并存储的那么多路径都没有用,在最后一步都被约束掉了。那么如果先从边数最少的那个类型开始算呢?事实上路径的中期还是会有很多可能,但路径长了以后就逐渐被约束掉了。这种方法其实相当于暴力搜索。

​ 因此我们从矩阵相乘得到的最终结果出发,逐个往前推,逐层将节点寻找出来,下面具体展开第2节的例子~


MABCAA1A2A111A213 \begin{array}{c|cc} M_{ABCA} & A_1 & A_2 \\ \hline A_1 & 1 & 1 \\ A_2 & 1 & 3 \end{array} MABCAA1A2A111A213


​ 由MABCAM_{ABCA}MABCA的对角线可知,共有4个A→B→C→AA\rightarrow B\rightarrow C \rightarrow AABCA类型的环,因此首先建立4条链表,用来存储这4个环,而且还可以知道,其中1条是A1A_1A1A1A_1A1的,3条是A2A_2A2A2A_2A2的,因此每条链表中最后一个节点分别是A1A_1A1A2A_2A2A2A_2A2A2A_2A2。并且先建立一个队列,放入对角线两个元素的位置[A1][A1],[A2][A2][A_1][A_1],[A_2][A_2][A1][A1],[A2][A2],后面需要用到~

在这里插入图片描述

​ 接下来往前推,为了找到连在AAA类节点前面的CCC类节点,需要用到MABC,MCAM_{ABC},M_{CA}MABC,MCA矩阵。


MABCA=MABC×MCA=MABCC1C2C3A1110A2121×MCAA1A2C110C201C301=MABCAA1A2A111A213 M_{ABCA}=M_{ABC}\times M_{CA}= \begin{array}{c|ccc} M_{ABC} & C_1 & C_2 & C_3 \\ \hline A_1 & 1 & 1 & 0 \\ A_2 & 1 & 2 & 1 \end{array} \times \begin{array}{c|cc} M_{CA} & A_1 & A_2 \\ \hline C_1 & 1 & 0 \\ C_2 & 0 & 1 \\ C_3 & 0 & 1 \end{array} = \begin{array}{c|cc} M_{ABCA} & A_1 & A_2 \\ \hline A_1 & 1 & 1 \\ A_2 & 1 & 3 \end{array} MABCA=MABC×MCA=MABCA1A2C111C212C301×MCAC1C2C3A1100A2011=MABCAA1A2A111A213


MABCA[A1][A1]=1M_{ABCA}[A_1][A_1]=1MABCA[A1][A1]=1是由MABC[A1][C1]×MCA[C1][A1]+MABC[A1][C2]×MCA[C2][A1]+MABC[A1][C3]×MCA[C3][A1]M_{ABC}[A_1][C_1]\times M_{CA}[C_1][A_1]+M_{ABC}[A_1][C_2]\times M_{CA}[C_2][A_1]+M_{ABC}[A_1][C_3]\times M_{CA}[C_3][A_1]MABC[A1][C1]×MCA[C1][A1]+MABC[A1][C2]×MCA[C2][A1]+MABC[A1][C3]×MCA[C3][A1]得到的,其中后面两项都为0,有效的只有第一项MABC[A1][C1]×MCA[C1][A1]M_{ABC}[A_1][C_1]\times M_{CA}[C_1][A_1]MABC[A1][C1]×MCA[C1][A1],也就是表示A1→B→C→A1A_1\rightarrow B\rightarrow C \rightarrow A_1A1BCA1由一条A1→B→C1A_1\rightarrow B\rightarrow C_1A1BC1路径和一条C1→A1C_1\rightarrow A_1C1A1路径组合而成,因此图中第一个A1A_1A1节点前面应该连C1C_1C1节点。

MABCA[A2][A2]=3M_{ABCA}[A_2][A_2]=3MABCA[A2][A2]=3是由MABC[A2][C1]×MCA[C1][A2]+MABC[A2][C2]×MCA[C2][A2]+MABC[A2][C3]×MCA[C3][A2]M_{ABC}[A_2][C_1]\times M_{CA}[C_1][A_2]+M_{ABC}[A_2][C_2]\times M_{CA}[C_2][A_2]+M_{ABC}[A_2][C_3]\times M_{CA}[C_3][A_2]MABC[A2][C1]×MCA[C1][A2]+MABC[A2][C2]×MCA[C2][A2]+MABC[A2][C3]×MCA[C3][A2]得到的。其中第一项为0;第二项MABC[A2][C2]×MCA[C2][A2]=2×1=2M_{ABC}[A_2][C_2]\times M_{CA}[C_2][A_2]=2\times 1=2MABC[A2][C2]×MCA[C2][A2]=2×1=2表示有2条A2→B→C2A_2\rightarrow B\rightarrow C_2A2BC2路径和1条C2→A2C_2\rightarrow A_2C2A2路径组合成2条A2→B→C→A2A_2\rightarrow B\rightarrow C \rightarrow A_2A2BCA2路径;第三项MABC[A2][C3]×MCA[C3][A2]=1×1=1M_{ABC}[A_2][C_3]\times M_{CA}[C_3][A_2]=1\times 1=1MABC[A2][C3]×MCA[C3][A2]=1×1=1表示有1条A2→B→C3A_2\rightarrow B\rightarrow C_3A2BC3路径和1条C3→A2C_3\rightarrow A_2C3A2路径组合成1条A2→B→C→A2A_2\rightarrow B\rightarrow C \rightarrow A_2A2BCA2路径。因此图中后面三个A3A_3A3节点前面应该分别连C2,C2,C3C_2,C_2,C_3C2,C2,C3节点。

在这里插入图片描述

​ 这里可以看到,MCAM_{CA}MCA矩阵中有用的是每一列中数字为1的行数,可以提前用一个map来存储,避免循环搜索,在矩阵比较稀疏的时候会大量节省时间。另外,这一步中MABCAM_{ABCA}MABCA矩阵中[A1][A1],[A2][A2][A_1][A_1],[A_2][A_2][A1][A1],[A2][A2]这两个位置已经用完了,将其移出队列,而新的有效的位置为MABCM_{ABC}MABC矩阵中的[A1][C1],[A2][C2],[A2][C3][A_1][C_1],[A_2][C_2],[A_2][C_3][A1][C1],[A2][C2],[A2][C3],因为我们用到了其对应的1,2,1三个数字,下一步再往前推时要考虑这三个数字的由来。

​ 接下来继续往前推,为了找到连在CCC类节点前面的BBB类节点,需要用到MAB,MBCM_{AB},M_{BC}MAB,MBC矩阵。


MABC=MAB×MBC=MABB1B2A110A211×MBCC1C2C3B1110B2011=MABCC1C2C3A1110A2121 M_{ABC}=M_{AB}\times M_{BC}= \begin{array}{c|cc} M_{AB}& B_1 & B_2 \\ \hline A_1 & 1 & 0 \\ A_2 & 1 & 1 \end{array} \times \begin{array}{c|ccc} M_{BC} & C_1 & C_2 & C_3 \\ \hline B_1 & 1 & 1 & 0 \\ B_2 & 0 & 1 & 1 \end{array} = \begin{array}{c|ccc} M_{ABC} & C_1 & C_2 & C_3 \\ \hline A_1 & 1 & 1 & 0 \\ A_2 & 1 & 2 & 1 \end{array} MABC=MAB×MBC=MABA1A2B111B201×MBCB1B2C110C211C301=MABCA1A2C111C212C301


​ 由上一步可知,这里有效的路径数是MABC[A1][C1],MABC[A2][C2],MABC[A2][C3]M_{ABC}[A_1][C_1],M_{ABC}[A_2][C_2],M_{ABC}[A_2][C_3]MABC[A1][C1],MABC[A2][C2],MABC[A2][C3],将这三个数字用完全相同的方法可得前面连接的BBB类节点应分别为{B1},{B1,B2},{B2}\{B_1\},\{B_1,B_2\},\{B_2\}{B1},{B1,B2},{B2}

在这里插入图片描述

​ 到这一步实际上已经把4条环都找出来了,将首尾相连即可,结果与前面列出的相同~

在这里插入图片描述

4 总结

​ 这个算法实际上是搜索出所有特定结构的路径,环是一种特殊的路径,因此也可以找出来。在给定一个网络时,先按节点类型排列并构建邻接矩阵,可以搜索出任意一个长度和结构的路径。

​ 假设一个网络有qqq类节点,网络节点总数为NNN,且每类节点数量相同,要搜索长度为lll的路径,需要矩阵相乘(l−1)(l-1)(l1)次,因此时间复杂度为O((l−1)(Nq)3)O((l-1)(\frac{N}{q})^3)O((l1)(qN)3),这是在完全不考虑矩阵相乘的优化的情况下。假如最终找到了MMM条路径,在往回推逐个找到具体节点的过程中,时间复杂度为O(lM)O(lM)O(lM)。如果各类节点数量不太平均的话,矩阵相乘的复杂度就要具体情况具体分析了,当然也很容易~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值