2011-12-29 43 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實現中會遇到同樣的問題。