Bagaimana saya bisa memverifikasi jika satu daftar adalah bagian dari yang lain?


185

Saya perlu memverifikasi apakah daftar adalah subset dari yang lain - pengembalian boolean yang saya cari.

Apakah menguji kesetaraan pada daftar yang lebih kecil setelah persimpangan merupakan cara tercepat untuk melakukan ini? Kinerja sangat penting mengingat jumlah dataset yang perlu dibandingkan.

Menambahkan fakta lebih lanjut berdasarkan diskusi:

  1. Apakah salah satu daftar akan sama untuk banyak tes? Itu karena salah satu dari mereka adalah tabel pencarian statis.

  2. Apakah itu perlu daftar? Tidak - tabel pencarian statis dapat berupa apa saja yang berkinerja terbaik. Yang dinamis adalah dict dari mana kita mengekstrak kunci untuk melakukan pencarian statis.

Apa solusi optimal yang diberikan skenario?


Anda menyebutkan kecepatan, mungkin numpy akan bermanfaat, tergantung penggunaan Anda.
ninMonkey

2
Apakah item daftar hashable?
wim


apakah Anda memerlukan subset yang tepat, atau dapatkah mereka setara?
törzsmókus

2
Mengapa tidak disetel (list_a) .issubset (set (list_b))?
SeF

Jawaban:


127

Fungsi performansi yang disediakan oleh Python untuk ini adalah set.issubset. Namun ada beberapa batasan yang membuatnya tidak jelas apakah itu jawaban untuk pertanyaan Anda.

Daftar dapat berisi item beberapa kali dan memiliki urutan tertentu. Satu set tidak. Selain itu, set hanya berfungsi pada objek hashable .

Apakah Anda bertanya tentang subset atau urutan (yang berarti Anda ingin algoritma pencarian string)? Apakah salah satu daftar akan sama untuk banyak tes? Apa tipe data yang ada dalam daftar? Dan untuk itu, apakah perlu daftar?

Posting Anda yang lain memotong dict dan daftar membuat jenis lebih jelas dan memang mendapat rekomendasi untuk menggunakan tampilan kunci kamus untuk fungsionalitas seperti set mereka. Dalam kasus itu diketahui berfungsi karena kunci kamus berperilaku seperti satu set (sangat banyak sehingga sebelum kita memiliki set di Python kami menggunakan kamus). Orang bertanya-tanya bagaimana masalah menjadi kurang spesifik dalam tiga jam.


Saya mengacu pada subset saja dan issubset berfungsi dengan baik - Terima kasih. Namun saya ingin tahu tentang 2 pertanyaan di sini. 1. Akankah salah satu daftar sama untuk banyak tes? Itu karena salah satu dari mereka adalah tabel pencarian statis 2.Apakah itu perlu daftar? Tidak - tabel pencarian statis dapat berupa apa saja yang berkinerja terbaik. Yang dinamis adalah dict dari mana kita mengekstrak kunci untuk melakukan pencarian statis. Apakah fakta ini akan mengubah solusinya?
IUnknown

Tidak banyak. Kunci-kunci kamus diatur seperti dan sudah diatur dalam tabel hash, dan oleh karena itu menggunakan set untuk bagian statis tidak akan menyebabkan komplikasi tambahan. Pada dasarnya fakta bahwa satu adalah dict berarti Anda mungkin tidak perlu mengubah bagian statis menjadi set (Anda bisa memeriksa semua (itertools.imap (dict.has_key, mylist)) dengan O (n) kinerja).
Yann Vernier

Saya tidak memahami bagaimana ini (atau solusi lain yang mengandalkan set) dapat menjadi jawaban yang diterima di sini. Pertanyaannya adalah tentang daftar dan saya terus terang berpikir bahwa himpunan bagian dalam "verifikasi jika satu daftar adalah himpunan bagian yang lain" tidak harus dipahami secara harfiah. Setelah konversi ke set, semua informasi tentang elemen duplikat hilang, namun, jika daftar awal dapat memuatnya, mungkin penting untuk memeriksa apakah mereka muncul di daftar kedua juga untuk benar-benar mengatakan bahwa semua elemen dari satu daftar dapat ditemukan dalam yang lain. Set jangan lakukan itu!
inVader

Hal-hal konteks; ini diterima karena membantu si penanya, dan memang menjelaskan perbedaannya. Kami diberitahu bahwa para kandidat akan diwakili sebagai set, jadi itu adalah tugas yang ditetapkan. Kasing Anda mungkin berbeda, dan perbedaan yang Anda sebutkan akan diselesaikan menggunakan multiset seperti collections.Counter.
Yann Vernier

141
>>> 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
Ini terlihat paling bagus dan menulis paling sederhana, tetapi yang tercepat seharusnya set(a).issubset(b) karena dalam hal ini Anda hanya mengkonversi ake set tetapi tidak b, yang menghemat waktu. Anda dapat menggunakan timeituntuk membandingkan waktu yang digunakan dalam dua perintah. Misalnya, timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000) dan timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
Yulan Liu

8
@ YulanLiu: Benci untuk menyampaikannya kepada Anda, tetapi hal pertama yang issubsetdilakukan adalah memeriksa apakah argumennya adalah set/ frozenset, dan jika tidak, ia mengubahnya menjadi sementara setuntuk perbandingan, menjalankan cek, lalu membuang sementara set. Perbedaan waktu (jika ada) akan menjadi faktor perbedaan kecil dalam biaya pencarian LEGB (menemukan setyang kedua kali lebih mahal daripada pencarian atribut pada yang sudah ada set), tetapi sebagian besar merupakan pencucian untuk input yang cukup besar.
ShadowRanger

3
Jika kedua daftar berisi nilai yang sama, maka yang ini akan mengembalikan false, syaratnya harus ditetapkan (a) <= set (b) sebagai gantinya
ssi-anik

2
Bagaimana jawaban ini benar. Dia meminta daftar bukan satu set. Mereka sangat berbeda. Bagaimana jika a = [1, 3, 3, 5, 5] dan b = [1, 3, 3, 3, 5]. Teori himpunan tidak sesuai untuk duplikat.
Eamonn Kenny

1
Saya juga akan menunjukkan bahwa jika a = [1,3,5] dan b = [1,3,5], set (a) <set (b) akan mengembalikan False. Anda dapat menambahkan operator sama dengan untuk menangani kasus-kasus ini: yaitu 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)

Penjelasan: Generator membuat booleans dengan mengulang daftar onememeriksa apakah item itu ada dalam daftar two. all()kembali Truejika setiap item benar, selain itu False.

Ada juga keuntungan yang allmengembalikan False pada elemen pertama elemen yang hilang daripada harus memproses setiap item.


Saya pikir untuk keterbacaan dan secara eksplisit apa yang ingin Anda capai, set(one).issubset(set(two))adalah solusi yang bagus. Dengan solusi yang saya posting Anda harus dapat menggunakannya dengan objek apa pun jika mereka memiliki operator pembanding yang tepat.
voidnologo

4
Gunakan ekspresi generator, bukan pemahaman daftar; yang pertama akan memungkinkan allhubungan pendek dengan benar, yang terakhir akan melakukan semua pemeriksaan bahkan jika akan jelas dari pemeriksaan pertama bahwa tes akan gagal. Jatuhkan kurung kotak untuk mendapatkan all(x in two for x in one).
ShadowRanger

Apakah saya salah, atau Anda tidak dapat menggunakan metode ini dengan penduduk setempat?
Homper

22

Dengan asumsi barang-barang itu bisa dipecahkan

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

Jika Anda tidak peduli dengan item duplikat misalnya. [1, 2, 2]dan [1, 2]kemudian hanya menggunakan:

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

Apakah menguji kesetaraan pada daftar yang lebih kecil setelah persimpangan merupakan cara tercepat untuk melakukan ini?

.issubsetakan menjadi cara tercepat untuk melakukannya. Memeriksa panjang sebelum pengujian issubsettidak akan meningkatkan kecepatan karena Anda masih memiliki item O (N + M) untuk diulangi dan diperiksa.


6

Satu lagi solusi adalah menggunakan a intersection.

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

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

Perpotongan set akan berisi set one

(ATAU)

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

Jika list1 ada dalam daftar 2:

  • (x in two for x in one)menghasilkan daftar True.

  • ketika kita melakukan set(x in two for x in one)hanya memiliki satu elemen (Benar).


2

Teori himpunan tidak sesuai untuk daftar karena duplikat akan menghasilkan jawaban yang salah menggunakan teori himpunan.

Sebagai contoh:

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

tidak ada artinya. Ya, itu memberikan jawaban yang salah tetapi ini tidak benar karena teori himpunan hanya membandingkan: 1,3,5 vs 1,3,4,5. Anda harus memasukkan semua duplikat.

Alih-alih, Anda harus menghitung setiap kemunculan setiap item dan melakukan yang lebih besar dari yang sama untuk memeriksanya. Ini tidak terlalu mahal, karena tidak menggunakan operasi O (N ^ 2) dan tidak memerlukan penyortiran cepat.

#!/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)

Kemudian menjalankan ini Anda dapatkan:

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

1

Karena tidak ada yang mempertimbangkan membandingkan dua string, inilah proposal saya.

Anda tentu saja ingin memeriksa apakah pipa ("|") bukan bagian dari salah satu daftar dan mungkin memilih secara otomatis char lain, tetapi Anda mendapatkan idenya.

Menggunakan string kosong sebagai pemisah bukanlah solusi karena angka dapat memiliki beberapa digit ([12,3]! = [1,23])

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

0

Maafkan saya jika saya terlambat ke pesta. ;)

Untuk memeriksa apakah set Aada subset dari set B, Pythonmemiliki A.issubset(B)dan A <= B. Ini sethanya berfungsi dan berfungsi dengan baik, tetapi kerumitan implementasi internal tidak diketahui. Referensi: https://docs.python.org/2/library/sets.html#set-objects

Saya datang dengan sebuah algoritma untuk memeriksa apakah list Aada himpunan bagian list Bdengan komentar berikut.

  • Untuk mengurangi kerumitan menemukan subset, saya merasa cocok untuk sortkedua daftar terlebih dahulu sebelum membandingkan elemen untuk memenuhi syarat untuk subset.
  • Ini membantu saya untuk breakyang loopketika nilai elemen daftar kedua B[j]lebih besar dari nilai elemen daftar pertama A[i].
  • last_index_jdigunakan untuk memulai loopdari list Bmana terakhir kali ditinggalkan. Ini membantu menghindari memulai perbandingan dari awal list B(yang, seperti yang Anda duga tidak perlu, untuk memulai list Bdari yang index 0berikutnya iterations.)
  • Kompleksitas akan menjadi O(n ln n)masing-masing untuk mengurutkan kedua daftar dan O(n)untuk memeriksa subset.
    O(n ln n) + O(n ln n) + O(n) = O(n ln n).

  • Kode memiliki banyak printpernyataan untuk melihat apa yang terjadi di masing iteration- masing loop. Ini dimaksudkan hanya untuk pengertian.

Periksa apakah satu daftar adalah himpunan bagian dari daftar lain

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");

Keluaran

[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

Jika Anda mengurutkannya, tidak ada lagi alasan untuk menggunakan daftar alih-alih satu set ...
LtWorf

0

Kode di bawah ini memeriksa apakah suatu himpunan merupakan "himpunan bagian yang tepat" dari himpunan lain

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


Terima kasih @ YannVernier Saya telah memodifikasi untuk memasukkan cek kosong untuk subset dan superset sehingga mengembalikan false ketika keduanya kosong.
Leo Bastin

Tetapi mengapa Anda melakukan ini? Untuk A menjadi himpunan bagian dari B berarti A tidak mengandung item yang tidak dalam B, atau ekuivalen, semua item dalam A juga dalam B. Set kosong oleh karena itu himpunan bagian dari semua set, termasuk dirinya sendiri. Pemeriksaan tambahan Anda menyatakan bahwa itu tidak benar, dan Anda menyatakan ini entah bagaimana ideal, tetapi itu bertentangan dengan terminologi yang sudah ada. Apa untungnya?
Yann Vernier

Terima kasih @YannVernier Sekarang kode memeriksa apakah set yang diberikan adalah "subset yang tepat" dari set lain.
Leo Bastin

Ini seburuk jawaban yang mengandalkan penggunaan set . Sementara berbicara secara matematis, himpunan adalah kumpulan elemen yang berbeda, kita dapat dan tidak harus bergantung pada asumsi itu ketika memeriksa apakah satu daftar adalah bagian dari yang lain. Jika daftar awal berisi duplikat, fungsi Anda masih dapat mengembalikan True , bahkan jika elemen tersebut hanya ada sekali dalam daftar kedua. Saya tidak berpikir ini adalah perilaku yang benar ketika mencoba membandingkan daftar.
inVader

0

Dalam python 3.5 Anda bisa melakukan [*set()][index]untuk mendapatkan elemen. Ini adalah solusi yang jauh lebih lambat daripada metode lain.

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

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

[*result][0] == True

atau hanya dengan len dan set

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

0

Berikut adalah bagaimana saya tahu jika satu daftar adalah bagian dari yang lain, urutannya penting bagi saya dalam kasus saya.

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

Sebagian besar solusi menganggap bahwa daftar tidak memiliki duplikat. Jika daftar Anda memiliki duplikat, Anda dapat mencoba ini:

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

Ini memastikan bahwa sublist tidak pernah memiliki elemen yang berbeda dari daftar atau elemen umum dalam jumlah yang lebih besar.

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

-1

Jika Anda bertanya apakah satu daftar "terkandung" di daftar lain, maka:

>>>if listA in listB: return True

Jika Anda bertanya apakah setiap elemen dalam listA memiliki jumlah elemen pencocokan yang sama di listB coba:

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

Ini tidak berhasil untuk saya. Pengembalian palsu bahkan jika Lista == listB
cass

@ Carcass Saya hanya diuji dengan string. Coba ini di mesin Anda. pastebin.com/9whnDYq4
DevPlayer

Saya mengacu pada bagian "if listA di listB: return True", bukan bagian kedua.
cass

@cass Pertimbangkan: ['satu', 'dua'] di ['satu', 'dua'] menghasilkan False. ['satu', 'dua'] dalam ['satu', 'dua', 'tiga'] menghasilkan Palsu. ['satu', 'dua'] dalam [['satu', 'dua'], 'tiga'] menghasilkan Benar. Jadi ya jika listA == ListB maka listA di listB akan selalu mengembalikan False karena listA perlu menjadi elemen daftar dalam listB. Mungkin Anda berpikir: listA dalam listB berarti "Apakah item dalam listA terdaftar sebagai item dalam listB. Itu bukan arti dari listA dalam listB
DevPlayer

@ Carcass Ah, saya melihat bagaimana posting saya membingungkan. Posting asli diminta untuk menguji listA menjadi subset dari listB. Secara teknis posting saya salah berdasarkan pertanyaan posting asli. Agar benar, pertanyaan harus ditanyakan "jika listA di [item0, item2, listA, item3, listA,]". Bukan "item dalam ['a', 'b', 'c'] di ['d', 'c', 'f', 'a', 'b', 'a']".
DevPlayer

-2

Jika a2 is subset of a1demikianLength 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))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.