子图枚举

时间:2011-05-12 21:10:42

标签: algorithm graph-algorithm

什么是枚举父图的所有子图的有效算法。在我的特定情况下,父图是一个分子图,因此它将被连接,通常包含少于100个顶点。

编辑:我只对连接的子图感兴趣。

2 个答案:

答案 0 :(得分:4)

  

什么是枚举父图的所有子图的有效算法。在我的特定情况下,父图是一个分子图,因此它将被连接,通常包含少于100个顶点。

与数学子图的比较:

你可以给每个元素一个从0到N的数字,然后将每个子图枚举为长度为N的任何二进制数。你根本不需要扫描图形。

如果你真正想要的是具有不同属性(完全连接等)的子图,那么你需要更新你的问题。正如评论家指出的那样,2 ^ 100非常大,所以你绝对不希望(如上所述)枚举数学上正确但物理上无聊的断开子图。假设每秒有10亿次枚举,至少需要40万亿年来计算所有这些内容,它真的会占用你。

<强>连接 - 子 - 发电机:

如果您希望某种枚举在某个指标下保留子图的DAG属性,例如(1,2,3) - &gt;(2,3) - &gt;(2),(1,2,3) - &gt;(1,2) - &gt;(2),你只需要一个可以生成所有CONNECTED子图作为迭代器的算法(产生每个元素)。这可以通过一次递归地移除单个元素(可选地从“边界”),检查剩余的元素集是否在缓存中(否则添加它),产生它并递归来实现。如果你的分子非常链状,只有很少的循环,这种方法很好。例如,如果你的元素是N个元素的五角星,那么它只有大约(100/5)^ 5 = 320万个结果(不到一秒)。但是,如果你开始添加一个以上的戒指,例如芳香族化合物和其他物质,你可能会陷入困境。

e.g。在python中

class Graph(object):
    def __init__(self, vertices):
        self.vertices = frozenset(vertices)
        # add edge logic here and to methods, etc. etc.

    def subgraphs(self):
        cache = set()
        def helper(graph):
            yield graph
            for element in graph:
                if {{REMOVING ELEMENT WOULD DISCONNECT GRAPH}}:
                    # you fill in above function; easy if
                    # there is 0 or 1 ring in molecule
                    # (keep track if molecule has ring, e.g.
                    #  self.numRings, maybe even more data)
                    # if you know there are 0 rings the operation
                    #  takes O(1) time
                    continue
                subgraph = Graph(graph.vertices-{element})
                if not subgraph in cache:
                    cache.add(subgraph)
                    for s in helper(subgraph):
                        yield s
        for graph in helper(self):
            yield graph

    def __eq__(self, other):
        return self.vertices == other.vertices
    def __hash__(self):
        return hash(self.vertices)
    def __iter__(self):
        return iter(self.vertices)
    def __repr__(self):
        return 'Graph(%s)' % repr(set(self.vertices))

演示:

G = Graph({1,2,3,4,5})

for subgraph in G.subgraphs():
    print(subgraph)

结果:

Graph({1, 2, 3, 4, 5})                                                                                                                                                                                                                                              
Graph({2, 3, 4, 5})
Graph({3, 4, 5})
Graph({4, 5})
Graph({5})
Graph(set())
Graph({4})
Graph({3, 5})
Graph({3})
Graph({3, 4})
Graph({2, 4, 5})
Graph({2, 5})
Graph({2})
Graph({2, 4})
Graph({2, 3, 5})
Graph({2, 3})
Graph({2, 3, 4})
Graph({1, 3, 4, 5})
Graph({1, 4, 5})
Graph({1, 5})
Graph({1})
Graph({1, 4})
Graph({1, 3, 5})
Graph({1, 3})
Graph({1, 3, 4})
Graph({1, 2, 4, 5})
Graph({1, 2, 5})
Graph({1, 2})
Graph({1, 2, 4})
Graph({1, 2, 3, 5})
Graph({1, 2, 3})
Graph({1, 2, 3, 4})

答案 1 :(得分:4)

此问题在this question的已接受答案中有更好的答案。它避免了在@ninjagecko的回答中标记为“你填写以上函数”的计算复杂步骤。它可以有效地处理有多个环的化合物。

有关详细信息,请参阅链接的问题,但这是摘要。 (N(v)表示顶点v的邻居集合。在“选择顶点”步骤中,您可以选择任意任意顶点。)

GenerateConnectedSubgraphs(verticesNotYetConsidered, subsetSoFar, neighbors):
    if subsetSoFar is empty:
        let candidates = verticesNotYetConsidered
    else
        let candidates = verticesNotYetConsidered intersect neighbors
    if candidates is empty:
        yield subsetSoFar
    else:
        choose a vertex v from candidates
        GenerateConnectedSubgraphs(verticesNotYetConsidered - {v},
                                   subsetSoFar,
                                   neighbors)
        GenerateConnectedSubgraphs(verticesNotYetConsidered - {v},
                                   subsetSoFar union {v},
                                   neighbors union N(v))