📅  最后修改于: 2023-12-03 15:42:16.779000             🧑  作者: Mango
GATE-CS-2009 | 第31题
给定两个有向图$G$和$H$,判断$G$是否和$H$同构。
两个有向图$G$和$H$,以邻接矩阵的形式给出。
如果$G$和$H$同构,输出YES
,否则输出NO
。
输入:
G:
0 1 1 0
0 0 1 1
1 0 0 1
0 0 0 0
H:
0 1 1 0
0 0 0 1
1 0 0 1
0 0 0 0
输出:
YES
本题是一个数学问题,需要用到图同构的判断方法。具体的做法有很多,其中最经典的方法是使用哈希表。我们可以把每个图看成一个字符串,然后使用哈希函数计算出它的哈希值。如果两个图的哈希值相等,那么它们很有可能是同构的。
我们先定义一个hash
函数:
def hash(graph):
"""计算有向图的哈希值。
Args:
graph: 有向图,用邻接矩阵表示。
Returns:
哈希值,用字符串表示。
"""
row, col = graph.shape
hash_value = ""
# 遍历图中的每个节点,将其邻居节点的编号拼接起来
for i in range(row):
neighbors = ""
for j in range(col):
if graph[i][j] == 1:
neighbors += str(j)
# 对每个节点的邻居节点编号进行排序,然后拼接起来
hash_value += "".join(sorted(neighbors))
return hash_value
这个函数将会遍历每个节点,将其邻居节点的编号拼接起来,然后对邻居节点的编号进行排序,最后将所有的拼接起来,得到图的哈希值。
接下来,我们考虑如何判断两个图是否同构。假设我们已经分别计算出了$G$和$H$的哈希值,如果哈希值相等,那么它们很有可能是同构的。但这只是一个初步的判断,接下来需要对哈希值进行进一步的检验。
我们假设$G$和$H$都有$n$个节点,我们从$G$中任意选择一个节点$x$,从$H$中任意选择一个节点$y$,如果$x$和$y$的度数相等,那么它们很有可能是同构的。接下来,我们将$G$和$H$中的所有节点按照度数从大到小排序,然后尝试一一匹配它们。假设第$i$个节点在$G$中的度数为$d_i^G$,在$H$中的度数为$d_i^H$,如果$d_i^G\neq d_i^H$,那么$G$和$H$一定不同构。否则,我们将$G$和$H$中所有和第$i$个节点相邻的节点按照度数从大到小排序,然后尝试一一匹配它们。如果匹配成功,那么我们就把这一对节点映射起来,然后继续匹配下一个节点。如果匹配失败,那么说明$G$和$H$不同构。
import numpy as np
def hash(graph):
"""计算有向图的哈希值。
Args:
graph: 有向图,用邻接矩阵表示。
Returns:
哈希值,用字符串表示。
"""
row, col = graph.shape
hash_value = ""
# 遍历图中的每个节点,将其邻居节点的编号拼接起来
for i in range(row):
neighbors = ""
for j in range(col):
if graph[i][j] == 1:
neighbors += str(j)
# 对每个节点的邻居节点编号进行排序,然后拼接起来
hash_value += "".join(sorted(neighbors))
return hash_value
def is_isomorphic(G, H):
"""判断有向图G和H是否同构。
Args:
G: 有向图,用邻接矩阵表示。
H: 有向图,用邻接矩阵表示。
Returns:
如果G和H同构,返回True,否则返回False。
"""
# 先计算出G和H的哈希值
hash_G = hash(G)
hash_H = hash(H)
# 如果哈希值不相等,那么G和H一定不同构
if hash_G != hash_H:
return False
# 接下来对G和H的每个节点进行度数排序
row_G, col_G = G.shape
degrees_G = np.sum(G, axis=1)
nodes_G = [(i, degrees_G[i]) for i in range(row_G)]
nodes_G = sorted(nodes_G, key=lambda x: x[1], reverse=True)
row_H, col_H = H.shape
degrees_H = np.sum(H, axis=1)
nodes_H = [(i, degrees_H[i]) for i in range(row_H)]
nodes_H = sorted(nodes_H, key=lambda x: x[1], reverse=True)
# 将G和H中每个节点按照度数从大到小匹配
matched = {}
for i in range(row_G):
u, deg_u = nodes_G[i]
v, deg_v = nodes_H[i]
# 如果u和v的度数不同,那么G和H一定不同构
if deg_u != deg_v:
return False
# 找到G中与u相邻的节点,并按照度数从大到小排序
neighbors_G = [(j, degrees_G[j]) for j in range(col_G) if G[u][j] == 1]
neighbors_G = sorted(neighbors_G, key=lambda x: x[1], reverse=True)
# 找到H中与v相邻的节点,并按照度数从大到小排序
neighbors_H = [(j, degrees_H[j]) for j in range(col_H) if H[v][j] == 1]
neighbors_H = sorted(neighbors_H, key=lambda x: x[1], reverse=True)
# 对G中与u相邻的节点依次尝试匹配H中与v相邻的节点
is_matched = False
for j in range(len(neighbors_G)):
x = neighbors_G[j][0]
y = neighbors_H[j][0]
if x in matched or y in matched.values():
# 如果x已经匹配了,或者y已经被其他节点匹配了,那么继续尝试匹配
continue
if not is_isomorphic(G[:, list(matched.keys()) + [u, x]], H[:, list(matched.values()) + [v, y]]):
# 如果G和H中的子图不同构,那么继续尝试其他节点
continue
# 匹配成功,记录下来
matched[x] = y
is_matched = True
break
if not is_matched:
# 如果G和H中的节点不能匹配,那么G和H不同构
return False
# 排序已经匹配的节点,保证匹配顺序不影响结果
matched = {k: matched[k] for k in sorted(matched)}
# G和H同构
return True
哈希函数的时间复杂度为$O(n^2\log n)$,$n$是图中节点的数量。对每个图进行哈希之后,对每个节点进行度数排序,时间复杂度为$O(n\log n)$。对G和H的每个节点进行匹配,时间复杂度为$O(n^2)$。如果两个子图不同构,那么需要对它们重新计算哈希值,时间复杂度为$O(n^2\log n)$。因此,总时间复杂度为$O(n!n^2\log n)$。由于$n$通常比较小,所以该方法在实际工程中是可行的。
[1] 盛斌, 赵建. 图同构问题. 计算机学报, 2009, 32(01): 1-8.