2011-12-29 41 views
10

有谁知道是否有一个Python内置的计算传递元组闭包?传递闭包python元组

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

感谢。

+0

这里的“传递”和“关闭”是什么意思? (1,3)和(2,4)的出现原理是什么?你的元组的长度总是一样吗?什么意思是“计算元组”? – eyquem 2011-12-29 21:19:16

+0

更多关于传递闭包[transitive_closure](http://xlinux.nist.gov/dads/HTML/transitiveClosure.html)。从本质上讲,原则是如果在元组的原始列表中,我们有两个形式为'(a,b)'和'(c,z)',并且'b'等于'c'的元组,则我们添加元组(' a,z)' 元组将始终有两个条目,因为它是一个二元关系。通过“计算元组”,我的意思是扩展元组的原始列表以成为传递闭包。 – Duke 2011-12-29 21:21:22

+0

谢谢。我完全不知道这个概念。 – eyquem 2011-12-29 21:25:23

回答

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)]) 
+0

这个作品,谢谢! – Duke 2011-12-29 21:49:59

+0

也许“关系”不是正确的名字;它只是为每个号码存储其他“可到达”号码,建立一个DAG。递归函数'build_closure'创建访问图的所有匹配(这个解决方案有很强的输入假设,更灵活(和复杂)可能更适合其他输入) [duh,评论已删除..留下此答案以供参考] – StefanoP 2011-12-29 22:00:27

+0

如果输入中有一个循环,这将运行到无限递归。 (我第一次误解了代码,不知怎的,以为你在迭代元素对而不是元组 - 在构建关系的同时解开单个元素的包装。) – 2011-12-29 22:01:01

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)])

+0

+1,非常优雅。不要介意我以前的评论:)顺便说一下,'new_relations'严格保持*关系*在集合论的说法中,或*边* *在图中。 – 2011-12-29 22:17:32

+0

我们在第二次调用的结果中不应该有'(2,2)'。 – Duke 2011-12-29 23:00:20

+2

@Duke yes你应该(2,2)=(2,1)*(1,2) – soulcheck 2011-12-29 23:12:37

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) 

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

+0

这也适用。 – Duke 2011-12-29 23:06:40

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 

时,有一个在关系的周期,即反身指出这将无法正常工作。

+1

+1即将发布的dfs解决方案以及:) – soulcheck 2011-12-29 22:41:16

+0

虽然它在周期上触发。 – soulcheck 2011-12-29 22:50:16

+0

@soulcheck:你是对的。记录下来;已经有很多更好的解决方案。 – 2011-12-29 22:52:23

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 
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实现中会遇到同样的问题。