当前位置: 首页 > 知识库问答 >
问题:

有向无环图的哈希值

皇甫逸清
2023-03-14

如何将有向无环图转换为哈希值,以便任何两个同构图哈希到相同的值?两个同构图哈希到不同的值是可以接受的,但不可取的,这就是我在下面的代码中所做的。我们可以假设图中的顶点数最多为11个。

我对Python代码特别感兴趣。

这是我所做的。如果 self.lt 是从节点到后代(不是子节点!)的映射,那么我根据修改后的拓扑排序重新标记节点(如果可以的话,它更喜欢先对具有更多后代的元素进行排序)。然后,我对排序的字典进行哈希处理。一些同构图将散列为不同的值,特别是随着节点数量的增加。

我已经包含了所有激励我的用例的代码。我正在计算找到7个数字的中位数所需的比较次数。同构图散列到相同值的次数越多,需要重做的工作就越少。我考虑过先将较大的分支连通,但不知道如何快速做到这一点。

from tools.decorator import memoized  # A standard memoization decorator


class Graph:
    def __init__(self, n):
        self.lt = {i: set() for i in range(n)}

    def compared(self, i, j):
        return j in self.lt[i] or i in self.lt[j]

    def withedge(self, i, j):
        retval = Graph(len(self.lt))
        implied_lt = self.lt[j] | set([j])
        for (s, lt_s), (k, lt_k) in zip(self.lt.items(),
                                        retval.lt.items()):
            lt_k |= lt_s
            if i in lt_k or k == i:
                lt_k |= implied_lt
        return retval.toposort()

    def toposort(self):
        mapping = {}
        while len(mapping) < len(self.lt):
            for i, lt_i in self.lt.items():
                if i in mapping:
                    continue
                if any(i in lt_j or len(lt_i) < len(lt_j)
                       for j, lt_j in self.lt.items()
                       if j not in mapping):
                    continue
                mapping[i] = len(mapping)
        retval = Graph(0)
        for i, lt_i in self.lt.items():
            retval.lt[mapping[i]] = {mapping[j]
                                     for j in lt_i}
        return retval

    def median_known(self):
        n = len(self.lt)
        for i, lt_i in self.lt.items():
            if len(lt_i) != n // 2:
                continue
            if sum(1
                   for j, lt_j in self.lt.items()
                   if i in lt_j) == n // 2:
                return True
        return False

    def __repr__(self):
        return("[{}]".format(", ".join("{}: {{{}}}".format(
            i,
            ", ".join(str(x) for x in lt_i))
                                       for i, lt_i in self.lt.items())))

    def hashkey(self):
        return tuple(sorted({k: tuple(sorted(v))
                             for k, v in self.lt.items()}.items()))

    def __hash__(self):
        return hash(self.hashkey())

    def __eq__(self, other):
        return self.hashkey() == other.hashkey()


@memoized
def mincomps(g):
    print("Calculating:", g)
    if g.median_known():
        return 0
    nodes = g.lt.keys()
    return 1 + min(max(mincomps(g.withedge(i, j)),
                       mincomps(g.withedge(j, i)))
                   for i in nodes
                   for j in nodes
                   if j > i and not g.compared(i, j))


g = Graph(7)
print(mincomps(g))

共有3个答案

杨经武
2023-03-14

散列必须有多好?我假设您不希望图形的完全序列化。散列很少保证没有第二个(但不同的)元素(图)计算为相同的散列。如果同构图(在不同的表示中)具有相同的散列,这对您非常重要,那么只使用在表示变化下不变的值。例如。:

  • 节点总数
  • (定向)连接的总数
  • 对于任何元组 (i,j) 到 (max(indegree), max(outdegree)) 的节点总数 (indegree, outdegree) = (i,j) (或对于不超过某个固定值 (m,n)的元组,则限制为

所有这些信息都可以在O(#nodes)中收集[假设图形存储正确]。将它们连接起来,就得到了一个散列。如果您愿意,可以对这些级联信息使用一些众所周知的哈希算法,如<code>sha</code>。没有额外的散列,它是一个连续的散列(它允许找到相似的图),如果选择的散列算法具有这些属性,则通过额外的散乱,它是统一的,大小是固定的。

事实上,注册任何添加或删除的连接已经足够好了。它可能会错过已更改的连接(< code>a -

这种方法是模块化的,可以根据需要进行扩展。包含的任何其他属性都将减少冲突次数,但会增加获取哈希值所需的工作量。更多想法:

  • 与上述相同,但具有二阶入度和出度。即<code>节点可到达的节点数-

您要求“唯一的哈希值”,显然我无法为您提供一个。但我认为术语“哈希”和“每个图形都是唯一的”是相互排斥的(当然不完全正确),并决定回答“哈希”部分而不是“唯一”部分。“唯一哈希”(完美哈希)基本上需要是图形的完整序列化(因为哈希中存储的信息量必须反映图形中的信息总量)。如果这真的是你想要的,只需定义一些节点的唯一顺序(例如,按自己的出度排序,然后是入度,然后是子级的出度,依此类推,直到顺序明确),并以任何方式序列化图形(使用上述排序中的位置作为节点的索引)。

当然,这要复杂得多。

孔磊
2023-03-14

有向无环图的同构仍然是GI-完全的。因此,目前没有已知的(最坏情况下的子指数)解决方案来保证两个同构的有向无环图将产生相同的散列。只有当不同图之间的映射是已知的时——例如,如果所有的顶点都有唯一的标签——才能有效地保证匹配的散列。

好的,让我们对少量顶点进行暴力处理。我们必须找到一个与输入中顶点的顺序无关的图的表示,从而保证同构图产生相同的表示。此外,这种表示必须确保没有两个非同构图产生相同的表示。

最简单的解决方法是为所有的n构造邻接矩阵!顶点的排列,并将邻接矩阵解释为n2位整数。然后我们可以选择最小或最大的数作为规范表示。这个数完全编码了图,因此确保了没有两个非同构的图产生相同的数——可以认为这个函数是完美的散列函数。因为我们在所有可能的顶点排列下选择最小或最大的数字来编码图,我们进一步确保同构的图产生相同的表示。

在11个顶点的情况下,这有多好或多坏?好吧,表示将有121位。我们可以将其减少11位,因为对角线表示循环将是非循环图中的所有零,并保留110位。从理论上讲,这个数字可以进一步减少。并非所有 2110 个剩余的图形都是非循环的,每个图形最多可能有 11 个!- 大约225 - 同构表示,但在实践中这可能很难做到。有谁知道如何计算具有n个顶点的不同有向无环图的数量?

找到此表示需要多长时间?天真的11岁!或39916800次迭代。这不是什么,可能已经不切实际,但我没有实现和测试它。但我们可能可以加快一点。如果我们通过从上到下、从左到右连接行来将邻接矩阵解释为整数,那么我们希望第一行的左侧有许多1(0),以获得一个大(小)数。因此,我们选择具有最大(最小)度的一个(或其中一个)顶点作为第一个顶点(独立度或出度取决于表示),然后选择在后续位置连接(未连接)到该顶点的顶点,以将1(零)带到左侧。

可能有更多的可能性来削减搜索空间,但我不确定是否有足够的可能性来实现这一点。也许有,或者也许其他人至少可以在这个想法的基础上建立一些东西。

傅阳炎
2023-03-14

为了有效地测试图同构,您将需要使用nauty。具体到Python,有包装器pynauty,但我不能证明它的质量(为了正确编译它,我必须对它的< code>setup.py做一些简单的修补)。如果这个包装器做的一切都是正确的,那么它为您感兴趣的用途简化了nauty很多,这只是一个散列< code > pynauty . certificate(some graph)的问题,对于同构图来说,这将是相同的值。

一些快速测试表明,pynty为每个图(具有相同数量的顶点)提供相同的证书。但这只是因为将图形转换为n的格式时包装器中的一个小问题。修复此问题后,它对我有用(我也使用了http://funkybee.narod.ru/graphs.htm的图形进行比较)。这是一个简短的补丁,它还考虑了setup.py中需要的修改:

diff -ur pynauty-0.5-orig/setup.py pynauty-0.5/setup.py
--- pynauty-0.5-orig/setup.py   2011-06-18 20:53:17.000000000 -0300
+++ pynauty-0.5/setup.py        2013-01-28 22:09:07.000000000 -0200
@@ -31,7 +31,9 @@

 ext_pynauty = Extension(
         name = MODULE + '._pynauty',
-        sources = [ pynauty_dir + '/' + 'pynauty.c', ],
+        sources = [ pynauty_dir + '/' + 'pynauty.c',
+            os.path.join(nauty_dir, 'schreier.c'),
+            os.path.join(nauty_dir, 'naurng.c')],
         depends = [ pynauty_dir + '/' + 'pynauty.h', ],
         extra_compile_args = [ '-O4' ],
         extra_objects = [ nauty_dir + '/' + 'nauty.o',
diff -ur pynauty-0.5-orig/src/pynauty.c pynauty-0.5/src/pynauty.c
--- pynauty-0.5-orig/src/pynauty.c      2011-03-03 23:34:15.000000000 -0300
+++ pynauty-0.5/src/pynauty.c   2013-01-29 00:38:36.000000000 -0200
@@ -320,7 +320,7 @@
     PyObject *adjlist;
     PyObject *p;

-    int i,j;
+    Py_ssize_t i, j;
     int adjlist_length;
     int x, y;
 类似资料:
  • 考虑以下无向非循环图: 如果我们定义“根”为A和E,有没有算法可以确定产生的有向无环图?: 我考虑过从根开始尝试某种DFS或BFS,但我不确定如何处理“等待”的需要,以查看另一个根是否可能到达给定的节点。

  • 在图论中,如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。 因为有向图中一个点经过两种路线到达另一个点未必形成环,因此有向无环图未必能转化成树,但任何有向树均为有向无环图。 一、简介 有向无环图是图论的重要概念,我们将首先介绍图的概念和定义,随后介绍有向图,再逐渐引至有向无环图(DAG)。值得一提的是,当DAG用于指代模型时一般指向贝叶斯网络。 一个图G

  • 输入: 生成的树: 输出: 规则: < li >输入代码总是会产生一个有向无环图 < li >每个节点都有一些wait_time值 < li >完整的图形遍历应该计算整个图形的总等待时间 < li >必须并行遍历所有独立节点(或者至少时间计算应该是这样) < li >如果两个不同节点的wait_time发生重叠,则将考虑最大值,遍历时间较短的节点将移动到下一个独立节点 < li >除了根和叶(可以

  • 问题内容: 我想在Python中实现HashMap。我想请用户输入。根据他的输入,我正在从HashMap中检索一些信息。如果用户输入HashMap的键,我想检索相应的值。 如何在Python中实现此功能? 问题答案: Python字典是一种内置的类型,支持键值对。 以及使用dict关键字: 要么:

  • 一、定义 边有向,无环。 英文名叫 Directed Acyclic Graph,缩写是 DAG。一个无环的有向图称做有向无环图。 在图论中,如果一个有向图无法从某个顶点出发经过若干条边回到该点,则这个图是一个有向无环图(DAG图)。 因为有向图中一个点经过两种路线到达另一个点未必形成环,因此有向无环图未必能转化成树,但任何有向树均为有向无环图。 使用有向无环图解题时,要先判断是否是有向无环题。如

  • 问题内容: 我需要一个像这样的树/有向无环图实现: 没有任何种类的排序。 该仅仅是围绕重点和可能的值(节点不必具有值集)的包装。 我需要链接到父母和孩子。 标准API或Commons等中有什么可以帮到我吗? 我不介意自己写它(我当然 也不 想问你们),我只是不想重新发明轮子。 问题答案: 似乎没有任何东西。上周,我问了一个类似的问题,并最终实现了自己的树。我的实现与您所建议的非常相似: 您将必须添