2010-10-01 20 views
7

Tôi có ba bộ:Python thiết ngã tư vấn

s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] # true, 16 and 14 
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] # false 

Tôi muốn có một chức năng đó sẽ trở lại True nếu mỗi bộ trong danh sách giao cắt với ít nhất một bộ khác trong danh sách. Có một built-in cho điều này hoặc một danh sách đơn giản hiểu?

Trả lời

2

Đó là một chút chi tiết nhưng tôi nghĩ đó là một giải pháp khá hiệu quả. Nó tận dụng lợi thế của thực tế là khi hai bộ giao nhau, chúng ta có thể đánh dấu cả hai đều được kết nối. Nó làm điều này bằng cách giữ một danh sách các lá cờ miễn là danh sách các bộ. khi đặt i và đặt j giao nhau, nó đặt cờ cho cả hai. Sau đó nó lặp qua danh sách các bộ và chỉ cố gắng tìm một giao lộ cho các tập chưa được giao nhau. Sau khi đọc các bình luận, tôi nghĩ đây là những gì @Victor đang nói đến.

s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] # true, 16 and 14 
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] # false 


def connected(sets): 
    L = len(sets) 

    if not L: return True 
    if L == 1: return False 

    passed = [False] * L 
    i = 0 
    while True: 
     while passed[i]: 
      i += 1 
      if i == L: 
       return True 

     for j, s in enumerate(sets): 
      if j == i: continue 
      if sets[i] & s: 
       passed[i] = passed[j] = True 
       break 
     else: 
      return False 


print connected(s0) 
print connected(s1) 

Tôi đã quyết định rằng danh sách bộ trống rỗng được kết nối (Nếu bạn tạo thành phần của danh sách, tôi có thể tạo thành phần mà nó cắt;). Danh sách chỉ có một phần tử bị ngắt kết nối một cách tầm thường. Đó là một dòng để thay đổi trong cả hai trường hợp nếu bạn không đồng ý.

+0

nhiều câu trả lời hay, tôi đang sử dụng 2.5.2 ngay bây giờ vì vậy tôi đã kết thúc với giải pháp này –

13
all(any(a & b for a in s if a is not b) for b in s) 
+2

Thanh lịch, nhưng tôi muốn 2 vòng thẳng và một truy cập để tránh so sánh mỗi hai mục 2 lần, nó sẽ tăng tốc độ lên ít nhất là bởi một yếu tố của hai –

+0

@ jchl: thực sự thú vị và thêm vào kiến ​​thức python của tôi – pyfunc

+0

Cuộc gọi 'bool()' là dư thừa. – kennytm

0

Chiến lược này là không có khả năng được hiệu quả như @ đề nghị Victor, nhưng có thể hiệu quả hơn jchl's answer do tăng sử dụng bộ số học (union).

s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] 
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] 

def freeze(list_of_sets): 
    """Transform a list of sets into a frozenset of frozensets.""" 
    return frozenset(frozenset(set_) for set_ in list_of_sets) 

def all_sets_have_relatives(set_of_sets): 
    """Check if all sets have another set that they intersect with. 

    >>> all_sets_have_relatives(s0) # true, 16 and 14 
    True 
    >>> all_sets_have_relatives(s1) # false 
    False 
    """ 
    set_of_sets = freeze(set_of_sets) 
    def has_relative(set_): 
     return set_ & frozenset.union(*(set_of_sets - set((set_,)))) 
    return all(has_relative(set) for set in set_of_sets) 
+1

Tại sao tất cả các frozensets? – jchl

+0

@jchl: tập hợp không thể băm, vì vậy chúng không thể là phần tử trong tập hợp. Tôi không chắc liệu có nhiều điểm khiến cho cấp cao nhất được đặt ở mức cao nhất hay không, nhưng dường như nó có thể nhanh hơn một tập bình thường, và nó phù hợp với logic. – intuited

+0

@jchl: Ahh, tôi thấy những gì bạn đang nói về bây giờ. Có vẻ như tôi có một số vấn đề khó tiêu về tinh thần khi tái cấu trúc và sau đó đăng mã đó. "Tuy nhiên, doctest đã vượt qua!" Dù sao, nó đã được sửa rồi. – intuited

0

Điều này có thể mang lại hiệu suất tốt hơn tùy thuộc vào việc phân phối các bộ.

def all_intersect(s): 
    count = 0 
    for x, a in enumerate(s): 
     for y, b in enumerate(s): 
     if a & b and x!=y: 
      count += 1 
      break 
    return count == len(s) 
+2

1. Bạn không có nghĩa là 'break' thay vì' tiếp tục'? 2. Bạn có chắc chắn về 'count = -1' ?? 3. Mất dòng thứ 2 và thay thế 3 dòng cuối cùng bằng 'return count == len (s)'. 4. Sẽ không 'x! = Y và a & b' tốt hơn? 5. Bạn không thoát sớm nếu tập hợp không có bạn bè - cơ sở cho "có thể cung cấp hiệu suất tốt hơn" là gì? Tốt hơn cái gì? –

+1

Xin chào, xin chào, ... mã của bạn là BROKEN. 'count' trở thành số lượng các giao điểm được thiết lập không theo cặp trống, ít hơn 1. Đây không phải là thứ mà OP muốn. Bởi ACCIDENT nó đưa ra câu trả lời đúng cho hai trường hợp kiểm tra của OP. Hãy thử cái này: s2 = [set ([16, 9, 2, 10]), đặt ([16, 22, 14, 15]), đặt ([14, 2])] ... nó sẽ trả về Đúng nhưng số của bạn là 6-1 == 5 và vì vậy mã của bạn trả về Sai. –

+0

@John, +1, cảm ơn vì đã chỉ ra tất cả các lỗi. Tôi đã chỉnh sửa để thực hiện tất cả các chỉnh sửa cần thiết. 4. Tôi ưa thích 'a & b' trên' x! = Y' vì cái sau thường đúng hơn. Điều này có tệ không? 5. Vâng, đúng thế. Điều này có thể cho hiệu suất tốt hơn khi so sánh với những người thực hiện kiểm tra giao lộ với tất cả các bộ. – dheerosaur

2

Dưới đây là một hiệu quả hơn (nếu phức tạp hơn nhiều) giải pháp, mà thực hiện một số tuyến tính của nút giao thông và một số đoàn thể của trật tự O (n * log (n)), trong đó n là chiều dài của s :

def f(s): 
    import math 
    j = int(math.log(len(s) - 1, 2)) + 1 
    unions = [set()] * (j + 1) 
    for i, a in enumerate(s): 
     unions[:j] = [set.union(set(), *s[i+2**k:i+2**(k+1)]) for k in range(j)] 
     if not (a & set.union(*unions)): 
      return False 
     j = int(math.log(i^(i + 1), 2)) 
     unions[j] = set.union(a, *unions[:j]) 
    return True 

Lưu ý rằng giải pháp này chỉ hoạt động trên Python> = 2.6.

+0

Hiệu suất này chậm hơn đáng kể so với câu trả lời trước của bạn. trong khi câu trả lời trước đó của bạn trung bình 4,7 msecs, (trên đầu vào ngẫu nhiên lớn) cái này mất 250. Các công đoàn là những gì đang giết bạn (với việc thực hiện này) và trực giác. Về mặt lý thuyết hấp dẫn nhưng đắt tiền. – aaronasterling

+0

Thú vị. Tôi nghĩ rằng đó là yếu tố không đổi lớn do tất cả các mã Python (so với các giải pháp trước đó có thể được đánh giá gần như tất cả trong C). Ngoài ra, giải pháp đầu tiên của tôi sẽ hoạt động tốt nếu cuộc gọi 'any()' có thể tắt thường xuyên, trong khi giải pháp này hoạt động tốt trong mọi trường hợp. Giải pháp này hoạt động tốt hơn (9 vs 24 giây) so với đầu tiên trên đầu vào này: '[set ((i,)) cho i trong phạm vi (N)] + [set (range (N))]' với 'N = 10000 ', và tôi nghi ngờ sẽ thực hiện tốt hơn với thậm chí còn lớn hơn' N'. – jchl

+0

Tôi vừa nhận ra rằng giải pháp này phức tạp hơn nhiều so với cần thiết, và vẫn không tối ưu.Giải pháp thứ ba của tôi tốt hơn nhiều vì cả hai lý do. – jchl

5

Dưới đây là một giải pháp rất đơn giản mà rất hiệu quả cho đầu vào lớn:

def g(s): 
    import collections 
    count = collections.defaultdict(int) 
    for a in s: 
     for x in a: 
      count[x] += 1 
    return all(any(count[x] > 1 for x in a) for a in s) 
+0

bạn chỉ có thể sử dụng 'count.setdefault (x, 0) + = 1' thay vì câu lệnh' if/else'. – aaronasterling

+1

Không, bạn không thể. 'count.setdefault (x, 0)' không phải là một giá trị. – jchl

+1

+1 bởi vì nó phá vỡ công cụ giao cắt được đặt rõ ràng và đơn giản hóa vấn đề. Thật không may, tôi không nghĩ rằng bạn sẽ nhận được đủ upvotes. BTW, hoặc 'collections.Counter', hoặc' count = collections.defaultdict (int) 'và' count [x] + = 1' – tzot

1

Như thường lệ tôi muốn cung cấp cho các giải pháp không thể tránh khỏi itertools ;-)

from itertools import combinations, groupby 
from operator import itemgetter 


def any_intersects(sets): 
    # we are doing stuff with combinations of sets 
    combined = combinations(sets,2) 
    # group these combinations by their first set 
    grouped = (g for k,g in groupby(combined, key=itemgetter(0))) 
    # are any intersections in each group 
    intersected = (any((a&b) for a,b in group) for group in grouped) 
    return all(intersected) 


s0 = [set([16,9,2,10]), set([16,14,22,15]), set([14,7])] 
s1 = [set([16,9,2,10]), set([16,14,22,15]), set([7,8])] 
print any_intersects(s0) # True 
print any_intersects(s1) # False 

Điều này thực sự lười biếng và sẽ chỉ thực hiện các giao lộ được yêu cầu. Nó cũng có thể là một oneliner rất khó hiểu và không thể đọc ;-)

+0

đã kết hợp? Làm thế nào về "kết hợp"? ;-) – martineau

+0

Heh, một số danh sách từ điển trực tuyến "đã kết hợp", nhưng "kết hợp" có âm thanh tốt hơn ;-) –

+0

Trên phản ánh thêm, "combo" có lẽ sẽ mang tính mô tả hơn - và do đó thậm chí còn tốt hơn. – martineau

1

Để trả lời câu hỏi của bạn, không, không có một danh sách được xây dựng trong hoặc đơn giản hiểu rằng những gì bạn muốn. Đây là một giải pháp khác dựa trên itertools rất hiệu quả - đáng kinh ngạc gấp hai lần câu trả lời itertools của @ THC4k sử dụng groupby() trong các thử nghiệm định thời bằng cách sử dụng đầu vào mẫu của bạn. Nó có thể được tối ưu hóa thêm một chút, nhưng rất dễ đọc như đã trình bày. Giống như @AaronMcSmooth, tôi tự ý quyết định những gì cần trả lại khi không có hoặc chỉ có một bộ trong danh sách đầu vào.

from itertools import combinations 

def all_intersect(sets): 
    N = len(sets) 
    if not N: return True 
    if N == 1: return False 

    intersected = [False] * N 
    for i,j in combinations(xrange(N), 2): 
     if not intersected[i] or not intersected[j]: 
      if sets[i] & sets[j]: 
       intersected[i] = intersected[j] = True 
    return all(intersected) 
Các vấn đề liên quan