传递闭包python元组

时间:2011-12-29 21:08:14

标签: python closures

有没有人知道是否有用于计算元组传递闭包的python内置程序?

我有(1,2),(2,3),(3,4)形式的元组,我试图获得(1,2),(2,3),(3,4),(1,3)(2,4)

感谢。

7 个答案:

答案 0 :(得分:10)

传递闭包没有内置功能。

虽然它们很容易实现。

这是我的看法:

def transitive_closure(a):
    closure = set(a)
    while True:
        new_relations = set((x,w) for x,y in closure for q,w in closure if q == y)

        closure_until_now = closure | new_relations

        if closure_until_now == closure:
            break

        closure = closure_until_now

    return closure

呼叫: transitive_closure([(1,2),(2,3),(3,4)])

结果: set([(1, 2), (1, 3), (1, 4), (2, 3), (3, 4), (2, 4)])

呼叫: transitive_closure([(1,2),(2,1)])

结果: set([(1, 2), (1, 1), (2, 1), (2, 2)])

答案 1 :(得分:4)

快速尝试:

def transitive_closure(elements):
    elements = set([(x,y) if x < y else (y,x) for x,y in elements])

    relations = {}
    for x,y in elements:
        if x not in relations:
            relations[x] = []
        relations[x].append(y)

    closure = set()
    def build_closure(n):
        def f(k):
            for y in relations.get(k, []):
                closure.add((n, y))
                f(y)
        f(n)

    for k in relations.keys():
        build_closure(k)

    return closure

执行它,我们会得到

In [3]: transitive_closure([(1,2),(2,3),(3,4)])
Out[3]: set([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])

答案 2 :(得分:4)

我们可以通过从当前“端点”重复获取“图形边缘”的并集直到找不到新的端点,从给定的“起始节点”执行“闭合”操作。我们需要最多(节点数 - 1)次,因为这是路径的最大长度。 (这样做可以避免在存在循环的情况下陷入无限递归;在一般情况下会浪费迭代,但是避免检查我们是否完成,即在给定的迭代中没有进行任何更改。)

from collections import defaultdict

def transitive_closure(elements):
    edges = defaultdict(set)
    # map from first element of input tuples to "reachable" second elements
    for x, y in elements: edges[x].add(y)

    for _ in range(len(elements) - 1):
        edges = defaultdict(set, (
            (k, v.union(*(edges[i] for i in v)))
            for (k, v) in edges.items()
        ))

    return set((k, i) for (k, v) in edges.items() for i in v)

(我实际测试了一次;))

答案 3 :(得分:2)

次优但概念上简单的解决方案:

def transitive_closure(a):
    closure = set()
    for x, _ in a:
        closure |= set((x, y) for y in dfs(x, a))
    return closure

def dfs(x, a):
    """Yields single elements from a in depth-first order, starting from x"""
    for y in [y for w, y in a if w == x]:
        yield y
        for z in dfs(y, a):
            yield z

当关系中存在一个循环时,这将不起作用,即反射点。

答案 4 :(得分:2)

这里的一个与@soulcheck中的一个基本相同,它适用于邻接列表而不是边缘列表:

def inplace_transitive_closure(g):
    """g is an adjacency list graph implemented as a dict of sets"""
    done = False
    while not done:
        done = True
        for v0, v1s in g.items():
            old_len = len(v1s)
            for v2s in [g[v1] for v1 in v1s]:
                v1s |= v2s
            done = done and len(v1s) == old_len

答案 5 :(得分:0)

如果您有很多tupels(超过5000),您可能需要考虑使用scipy代码进行矩阵幂(另请参阅http://www.ics.uci.edu/~irani/w15-6B/BoardNotes/MatrixMultiplication.pdf

from scipy.sparse import csr_matrix as csr

M     = csr( ([True for tup in tups],([tup[0] for tup in tups],[tup[1] for tup in tups])) )
M_    = M**n #this is the actual computation
temp  = M_.nonzero()
tups_ = [(temp[0][i],temp[1][i]) for i in xrange(len(temp[0]))]

在最好的情况下,如果您对关系/图表有所了解,可以明智地选择n - 这是最长路径的长度。否则你必须选择M.shape[0],这可能会在你脸上爆炸。

这个绕道也有其局限性,特别是你应该确定闭包不会太大(连接性不是太强),但是在python实现中你会遇到同样的问题。

答案 6 :(得分:0)

您可以从这些元组创建一个图,然后从创建的图中使用连接组件算法。 Networkx 是支持连接组件算法的库。