📜  门| GATE-CS-2009 |第 31 题(1)

📅  最后修改于: 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.