2013-01-16 36 views
6

Cách dễ nhất và thanh lịch nhất để kiểm tra xem phần tử có tồn tại trong hai danh sách nhất định không. Ví dụ, tôi có hai danh sách như sau?so sánh nếu một phần tử tồn tại trong hai danh sách

>>>a, b = ['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w'] 

Bây giờ trong danh sách đã cho ở trên, tôi muốn kiểm tra xem có yếu tố nào tồn tại trong cả hai danh sách. Hiện tại tôi đang thực hiện như sau

def elm_exists(list_a, list_b): 
    for elm in list_a: 
     if elm in list_b: 
      return True 
    return False 

là có cách nào thanh lịch hơn để thực hiện việc này?

+2

Are các yếu tố duy nhất trong mỗi danh sách? Nếu vậy bạn có thể làm điều này khá đơn giản bằng cách sử dụng 'bộ'. –

+0

Có, các phần tử trong danh sách là duy nhất. – Amyth

+1

@Mike: Chờ ... tại sao bạn không thể làm điều này với các bộ ngay cả khi các phần tử _weren't_ duy nhất? Bạn mất thông tin rằng một phần tử tồn tại nhiều lần, nhưng nếu tất cả những gì bạn quan tâm là yếu tố tồn tại, bạn không cần thông tin đó. (Và, nếu bạn đã làm, bạn luôn có thể chỉ sử dụng một 'Counter' thay vì một' bộ' để giữ lại nó.) – abarnert

Trả lời

8

yếu tố kiểm tra của ab với điều này:

set(a).intersection(b) 

dụ:

In [44]: nk=set(a).intersection(b) 

In [45]: for x in a: 
    ...:  if x in nk: 
    ...:   print x, 'present in b' 
    ...:  else: 
    ...:   print x, 'absent in b' 
    ...:   
a absent in b 
b absent in b 
g present in b 
r absent in b 

thêm:

In [47]: timeit(set(a) & set(b)) 
1000000 loops, best of 3: 940 ns per loop 

In [48]: timeit(set(a).intersection(b)) 
1000000 loops, best of 3: 854 ns per loop 

In [49]: timeit([x for x in a if x in b]) 
1000000 loops, best of 3: 1 us per loop 

do đó sử dụng set(a).intersection(b).

+0

@JonClements, Cảm ơn, rất thanh lịch! – Amyth

+0

Tôi không chắc chắn có nhiều điểm trong thời gian kết quả với danh sách này ngắn. Nếu bạn chỉ giảm độ dài xuống 3 và 4 thay vì 4 và 5, đủ để làm cho các so sánh 'list' nhanh hơn phiên bản' set', ít nhất là trên máy tính của tôi, nhưng tôi không nghĩ rằng thực sự có nghĩa là làm một ' O (NM) 'thuật toán là tốt hơn và' O (N + M) 'một! – abarnert

+1

Xem câu trả lời đã chỉnh sửa của tôi. Nó phụ thuộc vào kích thước của tập dữ liệu của bạn, _and_ tần suất va chạm. Điều đáng ra phải rõ ràng… nhưng cho tới khi tôi cố gắng hiểu được các con số. Dù sao, trong hầu hết các trường hợp, cả hai phiên bản này và biểu thức máy phát của tôi trên 'set (a)' đều nhanh hơn, và câu hỏi ban đầu của OP là về sự thanh lịch hơn là hiệu suất, vì vậy… Tôi nghĩ rằng 'giao lộ' thanh lịch hơn có thể đọc được hơn '&', và biểu thức 'any… in… for' thậm chí còn nhiều hơn thế, nhưng YMMV. – abarnert

6

này hoạt động:

>>> l1,l2=['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w'] 
>>> list(set(l1)&set(l2)) 
['g'] 
1
def elm_exists(lista, listb): 
    return bool(set(lista) & set(listb)) 

Chức năng bool sẽ trở lại True cho tất cả các container không trống, và False cho những sản phẩm nào. Giao điểm được thiết lập (&) sẽ trả về một tập hợp các phần tử chung của hai bộ. Lưu ý rằng các bộ sẽ xóa mọi bản sao.

Hoặc, bạn có thể sử dụng set(lista).intersection(listb) trong chức năng bool.

+0

'set (listb)' bên trong giao lộ là dư thừa vì '.intersection' nhận bất kỳ (các) lần lặp nào làm đối số –

+0

Ngoài ra, bạn không cần' len' ở đây, vì 'bool (len (x))' là đảm bảo trả về cùng một thứ như 'bool (x)', đơn giản hơn và hiệu quả hơn. – abarnert

+0

Cảm ơn JonClements và abarnert, những thay đổi đã được thực hiện – Volatility

0
>>> y = [1,23,3] 
>>> z = [3,432] 
>>> (3 in y) and (3 in z) 
True 
3

Bạn không cần chuyển đổi cả hai list s thành set s, chỉ một. Tôi nghĩ rằng bỏ qua các chuyển đổi không cần thiết làm cho nó dễ đọc hơn và thanh lịch.

Vì vậy, một trong hai:

set(a).intersection(b) 

Hoặc:

s = set(a) 
any(e in s for e in b) 

Sau này có lợi thế là ngắn mạch càng sớm càng nó tìm thấy một trận đấu, và tốt hơn thể hiện logic, và trở về True hoặc False thay vì không phải là một falsey hoặc falsey set, nhưng đó là hai dòng thay vì một, nếu điều đó làm phiền bạn. Tôi không có ý tưởng cho dù lợi thế này hủy bỏ chi phí đặt vòng lặp bên trong một biểu thức máy phát điện thay vì bên trong một hàm C.

Kết quả hoạt động với list s này nhỏ gần như vô nghĩa, vì vậy hãy thử điều này:

In [373]: a=[random.choice(string.ascii_lowercase) for _ in range(10000)] 
In [374]: b=[random.choice(string.ascii_lowercase) for _ in range(10000)] 
In [375]: %timeit(set(a)) 
10000 loops, best of 3: 180 us per loop 
In [376]: s=set(a) # since all versions need to do this 
In [391]: %timeit(s & set(b)) 
1000000 loops, best of 3: 178 us per loop 
In [392]: %timeit(s.intersection(b)) 
1000000 loops, best of 3: 247 us per loop 
In [393]: %timeit(discard(e in s for e in b)) 
1000000 loops, best of 3: 550 ns per loop 
In [394]: %timeit(any(e in s for e in b)) 
1000000 loops, best of 3: 749 ns per loop 
In [395]: %timeit(any(e in a for e in b)) 
1000000 loops, best of 3: 1.42 us per loop 

Để đưa những con số tất cả ở quy mô nano giây, thêm trở lại trong chi phí của set(a) rằng tất cả nhưng cuối cùng cần, và so sánh các thử nghiệm giống nhau từ ba phiên bản Python (CPython cổ phiếu của Apple 2.7.2, python.org CPython 3.3.0, Homebrew PyPy 1.9.0/2.7.2, tất cả các bản Mac 64 bit):

    CP272 CP330 PyPy 
s & set(b)  358000 316000 180500 
s.intersection(b) 427000 459000 180900 
discard(genexp) 180550 157341 90094 
any(genexp)  180749 157334 90208 
any(list-genexp) 1420 686 960 

Bây giờ tôi nghĩ về điều này, điều này có ý nghĩa tổng thể. Tỷ lệ nhận được một vụ va chạm khá sớm là rất cao, do đó, chi phí chuyển đổi toàn bộ điều để một bộ thống trị tất cả mọi thứ.

Điều đó có nghĩa là chúng tôi cần một thử nghiệm mới, với 10.000 giá trị duy nhất. Hãy lặp lại bài kiểm tra, với điều này:

In [29]: a, b = list(range(10000)), list(range(10000)) 
In [30]: random.shuffle(a) 
In [31]: random.shuffle(b) 

        CP272 CP330 PyPy 
s & set(b)  1277000 1168000 1141000 
s.intersection(b) 1165000 1117000 2520000 
discard(genexp) 1699000 1271000 770000 
any(genexp)  389800 344543 320807 
any(list-genexp) 62000 10400 1520 

Đây là hợp lý hơn. Và họ vẫn có ý nghĩa. Nếu bạn so sánh cùng 10000 yếu tố ngẫu nhiên xáo trộn, bạn phải đi bao xa? Không đủ xa để làm cho chi phí của set -xác định một trong hai danh sách đáng làm, ít hơn nhiều cả hai!

Vì vậy, chúng ta hãy thử một trường hợp có các không trận đấu:

In [43]: a=list(range(10000, 20000)) 


        CP272  CP330  PyPy 
s & set(b)   751000 770000 733000 
s.intersection(b) 466000 530000 1920000 
discard(genexp)  1246000 985000 749000 
any(genexp)   1269000 966000 893000 
any(list-genexp) 185000000 176000000 5870000 

Tôi không có ý tưởng như thế nào PyPy đã làm người cuối cùng quá nhanh, nhưng khác hơn thế, không ngạc nhiên ở đây.

Vì vậy, cái nào là tốt nhất?

Rõ ràng, nếu bạn mong đợi rất nhiều va chạm, bạn muốn tránh làm bộ bất cứ khi nào có thể - nhưng nếu bạn mong đợi ít va chạm, bạn muốn tạo ít nhất một bộ. Nếu bạn không có ý tưởng, tôi nghĩ rằng đặt cược an toàn nhất là any(genexp) - tồi tệ nhất là ít hơn 3x là tốt nhất, và nếu có cơ hội tỷ lệ va chạm cao, nó sẽ nhanh hơn rất nhiều. Nhưng bạn có thể nhìn vào những con số và xem cho chính mình.

Hoặc, tốt hơn, tất nhiên, dành thời gian cho tất cả dữ liệu thử nghiệm thực mà bạn mong đợi gặp phải.

+0

Công việc hay (và +1), nhưng OP yêu cầu "dễ nhất" và "thanh lịch nhất", không phải là nhanh nhất! Là người dùng Numpy, hàm đầu tiên xảy ra với tôi là 'np.any (np.in1d ​​(x, y))'. Tôi tò mò muốn xem cách so sánh đó. Tuy nhiên, bằng chứng của bạn hỗ trợ quy tắc chung ... chi phí chuyển đổi dữ liệu thường vượt quá chi phí của thử nghiệm, ngoại trừ khi số liệu rất lớn. – cxrodgers

+0

@cxrodgers: Sự bắt đầu câu trả lời của tôi là về "dễ đọc" và "thanh lịch", và đó là tất cả những gì có trong phiên bản gốc, và tôi nghĩ đó vẫn là phần quan trọng nhất của câu trả lời. Tất cả những thứ khác được thêm vào sau, sau những tranh luận không thể tránh khỏi về những gì nhanh nhất; lý do duy nhất nó quá nhiều _longer_ hơn phần quan trọng là bạn có thể đơn giản hóa đơn giản, nhưng bạn không thể duy trì các bài kiểm tra hiệu năng đơn giản. – abarnert

0

Đây là một giải pháp khác,

>>> c = [filter(lambda x: x in b, sublist) for sublist in a] 
>>> filter (lambda a: a != '', c) 
['g'] 
Các vấn đề liên quan