Làm thế nào tôi có thể xác minh nếu một danh sách là tập hợp con của một danh sách khác?


184

Tôi cần xác minh xem một danh sách có phải là tập hợp con của một danh sách khác hay không - tất cả là một lợi nhuận boolean tôi tìm kiếm.

Kiểm tra sự bình đẳng trong danh sách nhỏ hơn sau một giao lộ là cách nhanh nhất để làm điều này? Hiệu suất là vô cùng quan trọng với số lượng bộ dữ liệu cần được so sánh.

Thêm thông tin chi tiết dựa trên các cuộc thảo luận:

  1. Một trong hai danh sách sẽ giống nhau cho nhiều bài kiểm tra? Nó làm như một trong số chúng là một bảng tra cứu tĩnh.

  2. Có cần phải là một danh sách? Nó không - bảng tra cứu tĩnh có thể là bất cứ thứ gì hoạt động tốt nhất. Cái động là một lệnh mà chúng ta trích xuất các phím để thực hiện tra cứu tĩnh.

Điều gì sẽ là giải pháp tối ưu cho kịch bản?


Bạn đề cập đến tốc độ, có lẽ numpy sẽ hữu ích, tùy thuộc vào việc sử dụng của bạn.
ninMonkey

2
Là các mục danh sách có thể băm?
wim

2
Nếu thứ tự là quan trọng, đây có thể là một khởi đầu tốt - StackOverflow - Cách tốt nhất để xác định xem một chuỗi có trong một chuỗi khác trong Python

Bạn có cần tập hợp con thích hợp, hoặc chúng có thể bằng nhau không?
törzsmókus

2
Tại sao không đặt (list_a) .issubset (set (list_b))?
SeF

Câu trả lời:


126

Hàm biểu diễn Python cung cấp cho điều này là set.issubset. Tuy nhiên, nó có một vài hạn chế khiến nó không rõ ràng nếu đó là câu trả lời cho câu hỏi của bạn.

Một danh sách có thể chứa các mục nhiều lần và có một thứ tự cụ thể. Một bộ không. Ngoài ra, các bộ chỉ hoạt động trên các đối tượng có thể băm .

Bạn đang hỏi về tập hợp con hoặc chuỗi con (có nghĩa là bạn sẽ muốn một thuật toán tìm kiếm chuỗi)? Một trong hai danh sách sẽ giống nhau cho nhiều bài kiểm tra? Các kiểu dữ liệu có trong danh sách là gì? Và đối với vấn đề đó, nó có cần phải là một danh sách không?

Bài đăng khác của bạn giao nhau với một dict và danh sách làm cho các loại rõ ràng hơn và đã nhận được đề xuất sử dụng các chế độ xem từ điển cho chức năng giống như thiết lập của chúng. Trong trường hợp đó, nó được biết là hoạt động vì các khóa từ điển hoạt động giống như một bộ (rất nhiều đến mức trước khi chúng ta có các bộ trong Python, chúng ta đã sử dụng từ điển). Người ta tự hỏi làm thế nào vấn đề trở nên ít cụ thể hơn trong ba giờ.


Tôi chỉ đề cập đến một tập hợp con và hiện tại chỉ hoạt động tốt - Cảm ơn. Tuy nhiên tôi tò mò về 2 câu hỏi ở đây. 1. Sẽ có một trong hai danh sách giống nhau cho nhiều bài kiểm tra? Nó làm như một trong số chúng là một bảng tra cứu tĩnh 2. Bạn có cần phải là một danh sách không? Nó không - bảng tra cứu tĩnh có thể là bất cứ thứ gì hoạt động tốt nhất. Cái động là một lệnh mà chúng ta trích xuất các phím để thực hiện tra cứu tĩnh. Liệu thực tế này có làm thay đổi giải pháp?
IUnknown

Không nhiều. Các khóa của từ điển giống như tập hợp và đã được sắp xếp trong bảng băm, và do đó sử dụng một bộ cho phần tĩnh sẽ không gây ra các biến chứng bổ sung. Về cơ bản, thực tế là một dict có nghĩa là bạn có thể không cần phải chuyển đổi phần tĩnh thành một tập hợp (bạn có thể kiểm tra tất cả (itertools.imap (dict.has_key, mylist)) với hiệu suất O (n)).
Yann Vernier

Tôi không hủy bỏ làm thế nào điều này (hoặc bất kỳ giải pháp nào khác dựa vào các bộ) có thể là câu trả lời được chấp nhận ở đây. Câu hỏi là về danh sách và tôi thẳng thắn nghĩ rằng tập hợp con trong "xác minh nếu một danh sách là tập hợp con khác" không được thực hiện theo nghĩa đen. Khi chuyển đổi thành tập hợp, mọi thông tin về các phần tử trùng lặp sẽ bị mất, tuy nhiên, nếu danh sách ban đầu có thể chứa những phần tử đó, điều quan trọng là phải kiểm tra xem chúng có xuất hiện trong danh sách thứ hai hay không để thực sự nói rằng có thể tìm thấy tất cả các phần tử của một danh sách bên trong khác Bộ không làm điều đó!
inVader

Các vấn đề bối cảnh; điều này đã được chấp nhận để giúp người hỏi và đã giải thích sự khác biệt. Chúng tôi đã nói với các ứng cử viên sẽ được đại diện như bộ, vì vậy đó là một nhiệm vụ được thiết lập. Trường hợp của bạn có thể khác và sự khác biệt mà bạn đề cập sẽ được giải quyết bằng cách sử dụng nhiều trang như bộ sưu tập.
Yann Vernier

140
>>> a = [1, 3, 5]
>>> b = [1, 3, 5, 8]
>>> c = [3, 5, 9]
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

>>> a = ['yes', 'no', 'hmm']
>>> b = ['yes', 'no', 'hmm', 'well']
>>> c = ['sorry', 'no', 'hmm']
>>> 
>>> set(a) <= set(b)
True
>>> set(c) <= set(b)
False

21
Điều này có vẻ tốt nhất và viết đơn giản nhất, nhưng nhanh nhất nên là set(a).issubset(b) vì trong trường hợp này bạn chỉ chuyển đổi athành thiết lập nhưng không b, giúp tiết kiệm thời gian. Bạn có thể sử dụng timeitđể so sánh thời gian tiêu thụ trong hai lệnh. Ví dụ: timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
Yulan Liu

8
@YulanLiu: Ghét phải chia nó cho bạn, nhưng điều đầu tiên issubsetlà kiểm tra xem đối số có phải là set/ frozensetvà nếu không, nó sẽ chuyển đổi nó thành tạm thời setđể so sánh, chạy séc, sau đó loại bỏ tạm thời set. Sự khác biệt về thời gian (nếu có) sẽ là một yếu tố của sự khác biệt nhỏ về chi phí tra cứu LEGB (tìm kiếm setlần thứ hai đắt hơn so với tra cứu thuộc tính trên hiện tại set), nhưng chủ yếu là rửa cho các đầu vào đủ lớn.
ShadowRanger

3
Nếu cả hai danh sách chứa cùng một giá trị, thì danh sách này sẽ trả về false, điều kiện nên được đặt (a) <= set (b) thay vào đó
ssi-anik

2
Làm thế nào câu trả lời này có thể đúng. Ông yêu cầu một danh sách không phải là một bộ. Chúng hoàn toàn khác nhau. Nếu a = [1, 3, 3, 5, 5] và b = [1, 3, 3, 3, 5]. Đặt lý thuyết là không phù hợp cho các bản sao.
Eamonn Kenny

1
Tôi cũng sẽ chỉ ra rằng nếu a = [1,3,5] và b = [1,3,5], tập (a) <set (b) sẽ trả về Sai. Bạn có thể thêm toán tử bằng để xử lý các trường hợp sau: tức là set (a) <= set (b).
Jon

37
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

all(x in two for x in one)

Giải thích: Trình tạo tạo booleans bằng cách lặp qua onekiểm tra danh sách nếu mục đó có trong danh sách two. all()trả lại Truenếu mỗi mục là trung thực, khác False.

Ngoài ra còn có một lợi thế là alltrả về Sai trong trường hợp đầu tiên của một phần tử bị thiếu thay vì phải xử lý mọi mục.


Tôi nghĩ để dễ đọc và rõ ràng về những gì bạn đang cố gắng đạt được, set(one).issubset(set(two))là một giải pháp tuyệt vời. Với giải pháp tôi đã đăng, bạn sẽ có thể sử dụng nó với bất kỳ đối tượng nào nếu chúng có các toán tử so sánh thích hợp được xác định.
voidnologo

4
Sử dụng một biểu thức trình tạo, không phải là một sự hiểu biết danh sách; cái trước sẽ cho phép allđoản mạch đúng cách, cái sau sẽ thực hiện tất cả các kiểm tra ngay cả khi nó rõ ràng từ lần kiểm tra đầu tiên rằng thử nghiệm sẽ thất bại. Chỉ cần thả dấu ngoặc vuông để có được all(x in two for x in one).
ShadowRanger

Tôi sai, hay bạn không thể sử dụng phương pháp này với người dân địa phương?
Homper

22

Giả sử các mục có thể băm

>>> from collections import Counter
>>> not Counter([1, 2]) - Counter([1])
False
>>> not Counter([1, 2]) - Counter([1, 2])
True
>>> not Counter([1, 2, 2]) - Counter([1, 2])
False

Nếu bạn không quan tâm đến các mục trùng lặp, ví dụ. [1, 2, 2][1, 2]sau đó chỉ cần sử dụng:

>>> set([1, 2, 2]).issubset([1, 2])
True

Kiểm tra sự bình đẳng trong danh sách nhỏ hơn sau một giao lộ là cách nhanh nhất để làm điều này?

.issubsetsẽ là cách nhanh nhất để làm điều đó. Kiểm tra độ dài trước khi kiểm tra issubsetsẽ không cải thiện tốc độ vì bạn vẫn có các mục O (N + M) để lặp lại và kiểm tra.


6

Một giải pháp nữa sẽ là sử dụng a intersection.

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one).intersection(set(two)) == set(one)

Giao điểm của các bộ sẽ chứa set one

(HOẶC LÀ)

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(one) & (set(two)) == set(one)

2
one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

set(x in two for x in one) == set([True])

Nếu list1 nằm trong danh sách 2:

  • (x in two for x in one)tạo ra một danh sách True.

  • khi chúng ta làm set(x in two for x in one)chỉ có một yếu tố (Đúng).


2

Lý thuyết tập hợp không phù hợp với danh sách vì các mục trùng lặp sẽ dẫn đến câu trả lời sai khi sử dụng lý thuyết tập hợp.

Ví dụ:

a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
set(b) > set(a)

Không có ý nghĩa. Vâng, nó đưa ra một câu trả lời sai nhưng điều này không đúng vì lý thuyết tập hợp chỉ là so sánh: 1,3,5 so với 1,3,4,5. Bạn phải bao gồm tất cả các bản sao.

Thay vào đó, bạn phải đếm từng lần xuất hiện của từng mục và làm lớn hơn bằng để kiểm tra. Điều này không phải là rất tốn kém, bởi vì nó không sử dụng các hoạt động O (N ^ 2) và không yêu cầu sắp xếp nhanh chóng.

#!/usr/bin/env python

from collections import Counter

def containedInFirst(a, b):
  a_count = Counter(a)
  b_count = Counter(b)
  for key in b_count:
    if a_count.has_key(key) == False:
      return False
    if b_count[key] > a_count[key]:
      return False
  return True


a = [1, 3, 3, 3, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

a = [1, 3, 3, 3, 4, 4, 5]
b = [1, 3, 3, 4, 5]
print "b in a: ", containedInFirst(a, b)

Sau đó chạy cái này bạn nhận được:

$ python contained.py 
b in a:  False
b in a:  True

0

Xin lỗi nếu tôi đến bữa tiệc muộn. ;)

Để kiểm tra xem một set Atập hợp con của set B, PythonA.issubset(B)A <= B. Nó setchỉ hoạt động và hoạt động tuyệt vời NHƯNG sự phức tạp của việc thực hiện nội bộ chưa được biết. Tham khảo: https://docs.python.org/2/l Library / sets.html # set-objects

Tôi đã đưa ra một thuật toán để kiểm tra xem có phải list Alà một tập hợp con list Bvới các nhận xét sau không.

  • Để giảm độ phức tạp của việc tìm tập hợp con, tôi thấy nó phù hợp với sortcả hai danh sách trước khi so sánh các phần tử để đủ điều kiện cho tập hợp con.
  • Nó đã giúp tôi breaknhững loopkhi giá trị của nguyên tố của danh sách thứ hai B[j]lớn hơn giá trị của nguyên tố của danh sách đầu tiên A[i].
  • last_index_jđược sử dụng để bắt đầu looplại list Bnơi nó rời đi. Nó giúp tránh bắt đầu so sánh từ đầu list B(nghĩa là, như bạn có thể đoán không cần thiết, để bắt đầu list Btừ index 0sau iterations).
  • Độ phức tạp sẽ là O(n ln n)mỗi để sắp xếp cả hai danh sách và O(n)để kiểm tra tập hợp con.
    O(n ln n) + O(n ln n) + O(n) = O(n ln n).

  • Mã có rất nhiều printbáo cáo để xem những gì đang xảy ra tại mỗi iterationcủa loop. Đây chỉ là để hiểu.

Kiểm tra nếu một danh sách là tập hợp con của danh sách khác

is_subset = True;

A = [9, 3, 11, 1, 7, 2];
B = [11, 4, 6, 2, 15, 1, 9, 8, 5, 3];

print(A, B);

# skip checking if list A has elements more than list B
if len(A) > len(B):
    is_subset = False;
else:
    # complexity of sorting using quicksort or merge sort: O(n ln n)
    # use best sorting algorithm available to minimize complexity
    A.sort();
    B.sort();

    print(A, B);

    # complexity: O(n^2)
    # for a in A:
    #   if a not in B:
    #       is_subset = False;
    #       break;

    # complexity: O(n)
    is_found = False;
    last_index_j = 0;

    for i in range(len(A)):
        for j in range(last_index_j, len(B)):
            is_found = False;

            print("i=" + str(i) + ", j=" + str(j) + ", " + str(A[i]) + "==" + str(B[j]) + "?");

            if B[j] <= A[i]:
                if A[i] == B[j]:
                    is_found = True;
                last_index_j = j;
            else:
                is_found = False;
                break;

            if is_found:
                print("Found: " + str(A[i]));
                last_index_j = last_index_j + 1;
                break;
            else:
                print("Not found: " + str(A[i]));

        if is_found == False:
            is_subset = False;
            break;

print("subset") if is_subset else print("not subset");

Đầu ra

[9, 3, 11, 1, 7, 2] [11, 4, 6, 2, 15, 1, 9, 8, 5, 3]
[1, 2, 3, 7, 9, 11] [1, 2, 3, 4, 5, 6, 8, 9, 11, 15]
i=0, j=0, 1==1?
Found: 1
i=1, j=1, 2==1?
Not found: 2
i=1, j=2, 2==2?
Found: 2
i=2, j=3, 3==3?
Found: 3
i=3, j=4, 7==4?
Not found: 7
i=3, j=5, 7==5?
Not found: 7
i=3, j=6, 7==6?
Not found: 7
i=3, j=7, 7==8?
not subset

Nếu bạn sắp xếp chúng, sẽ không còn lý do nào để sử dụng danh sách thay vì tập hợp
LtWorf

0

Dưới đây mã kiểm tra xem một tập đã cho có phải là "tập hợp con" của tập hợp khác không

 def is_proper_subset(set, superset):
     return all(x in superset for x in set) and len(set)<len(superset)

1
Tại sao lý tưởng của bạn cho tập hợp trống để phá vỡ quy tắc toán học đã thiết lập? Wikipedia: Tập rỗng {}, ký hiệu là, cũng là tập con của bất kỳ tập X đã cho nào. Nó cũng luôn là tập con đúng của bất kỳ tập nào trừ chính nó.
Yann Vernier

Cảm ơn @YannVernier Tôi đã sửa đổi để bao gồm các kiểm tra trống cho cả tập hợp con và superset để nó trả về false khi cả hai đều trống.
Leo Bastin

Nhưng tại sao bạn làm điều này? Để A là tập con của B đơn giản có nghĩa là A không chứa các mục không nằm trong B hoặc tương đương, tất cả các mục trong A cũng nằm trong B. Do đó, tập hợp trống là tập hợp con của tất cả các tập hợp, kể cả chính nó. Kiểm tra thêm của bạn khẳng định rằng nó không phải, và bạn khẳng định đây là lý tưởng nào đó, nhưng nó trái với thuật ngữ được thiết lập. Lợi thế là gì?
Yann Vernier

Cảm ơn @YannVernier Bây giờ mã kiểm tra xem một tập đã cho có phải là "tập hợp con" của tập hợp khác không.
Leo Bastin

Điều này cũng tệ như các câu trả lời dựa trên việc sử dụng các bộ . Trong khi nói về mặt toán học, một tập hợp là một tập hợp các phần tử riêng biệt, chúng ta có thể và không nên dựa vào giả định đó khi kiểm tra xem một danh sách này có phải là một phần của một phần tử khác không. Nếu danh sách ban đầu có chứa trùng lặp, chức năng của bạn vẫn có thể trả về True , ngay cả khi phần tử được đề cập chỉ xuất hiện trong danh sách thứ hai một lần. Tôi không nghĩ rằng đây là hành vi chính xác khi cố gắng so sánh các danh sách.
inVader

0

Trong python 3.5, bạn có thể thực hiện [*set()][index]để lấy phần tử. Đó là giải pháp chậm hơn nhiều so với các phương pháp khác.

one = [1, 2, 3]
two = [9, 8, 5, 3, 2, 1]

result = set(x in two for x in one)

[*result][0] == True

hoặc chỉ với len và thiết lập

len(set(a+b)) == len(set(a))

0

Đây là cách tôi biết nếu một danh sách là tập hợp con của một danh sách khác, trình tự quan trọng với tôi trong trường hợp của tôi.

def is_subset(list_long,list_short):
    short_length = len(list_short)
    subset_list = []
    for i in range(len(list_long)-short_length+1):
        subset_list.append(list_long[i:i+short_length])
    if list_short in subset_list:
        return True
    else: return False

0

Hầu hết các giải pháp xem xét rằng các danh sách không có bản sao. Trong trường hợp danh sách của bạn có trùng lặp, bạn có thể thử điều này:

def isSubList(subList,mlist):
    uniqueElements=set(subList)
    for e in uniqueElements:
        if subList.count(e) > mlist.count(e):
            return False     
    # It is sublist
    return True

Nó đảm bảo danh sách con không bao giờ có các yếu tố khác với danh sách hoặc số lượng lớn hơn của một yếu tố phổ biến.

lst=[1,2,2,3,4]
sl1=[2,2,3]
sl2=[2,2,2]
sl3=[2,5]

print(isSubList(sl1,lst)) # True
print(isSubList(sl2,lst)) # False
print(isSubList(sl3,lst)) # False

0

Vì không ai xem xét việc so sánh với chuỗi, đây là đề xuất của tôi.

Tất nhiên bạn có thể muốn kiểm tra xem đường ống ("|") không phải là một phần của danh sách và có thể tự động chọn một char khác, nhưng bạn đã có ý tưởng.

Sử dụng một chuỗi rỗng làm dấu phân cách không phải là một giải pháp vì các số có thể có nhiều chữ số ([12,3]! = [1,23])

def issublist(l1,l2):
    return '|'.join([str(i) for i in l1]) in '|'.join([str(i) for i in l2])

-1

Nếu bạn đang hỏi nếu một danh sách được "chứa" trong danh sách khác thì:

>>>if listA in listB: return True

Nếu bạn đang hỏi liệu mỗi phần tử trong listA có số lượng phần tử phù hợp bằng nhau trong listB hãy thử:

all(True if listA.count(item) <= listB.count(item) else False for item in listA)

Điều này không làm việc cho tôi. Trả về sai ngay cả khi listA == listB
cass

@cass Tôi chỉ thử nghiệm với chuỗi. Hãy thử điều này trên máy của bạn. pastebin.com/9whnDYq4
DevPlayer

Tôi đã đề cập đến phần "if listA trong listB: return True", không phải phần thứ hai.
cass

@cass Hãy xem xét: ['một', 'hai'] trong ['một', 'hai'] mang lại Sai. ['một', 'hai'] trong ['một', 'hai', 'ba'] mang lại Sai. ['một', 'hai'] trong [['một', 'hai'], 'ba'] mang lại Đúng. Vì vậy, có nếu listA == ListB thì listA trong listB sẽ luôn trả về Sai vì listA sẽ cần phải là thành phần danh sách trong listB. Có lẽ bạn đang nghĩ: listA trong listB có nghĩa là "Các mục trong listA được liệt kê là các mục trong listB. Đó không phải là ý nghĩa của listA trong listB
DevPlayer

@cass Ah, tôi thấy bài viết của tôi khó hiểu như thế nào. Bài viết gốc được yêu cầu kiểm tra listA là tập con của listB. Về mặt kỹ thuật, bài viết của tôi sai dựa trên câu hỏi của bài viết gốc. Để nó đúng, câu hỏi sẽ phải hỏi "nếu listA trong [item0, item2, listA, item3, listA,]". Không phải "các mục trong ['a', 'b', 'c'] trong ['d', 'c', 'f', 'a', 'b', 'a']".
DevPlayer

-2

Nếu a2 is subset of a1, sau đóLength of set(a1 + a2) == Length of set(a1)

a1 = [1, 2, 3, 4, 5]
a2 = [1, 2, 3]

len(set(a1)) == len(set(a1 + a2))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.