Bir listenin diğerinin alt kümesi olup olmadığını nasıl doğrulayabilirim?


185

Bir liste başka bir alt küme olup olmadığını doğrulamak gerekir - bir boolean dönüş tüm aradığım.

Bir kavşaktan sonra küçük listede eşitliği test etmek bunu yapmanın en hızlı yolu mudur? Karşılaştırılması gereken veri kümelerinin sayısı düşünüldüğünde performans çok önemlidir.

Tartışmalara dayalı olarak başka gerçekler eklemek:

  1. Listelerden herhangi biri birçok test için aynı mı olacak? Bunlardan biri statik bir arama tablosu olarak yapar.

  2. Liste olması gerekiyor mu? Olmaz - statik arama tablosu en iyi performansı gösteren herhangi bir şey olabilir. Dinamik olan, statik bir arama yapmak için anahtarları çıkardığımız bir diksiyondur.

Senaryo göz önüne alındığında en uygun çözüm ne olurdu?


Kullanımınıza bağlı olarak hızdan, belki de numpy'nin faydalı olacağını belirtiyorsunuz.
ninMonkey

2
Liste öğeleri yıkanabilir mi?
wim

2
Sipariş önemliyse, bu iyi bir başlangıç ​​olabilir - StackOverflow - Bir

uygun alt kümeye mi ihtiyacınız var yoksa eşit olabilir mi?
törzsmókus

2
Neden ayarlanmadı (list_a) .issubset (ayarlandı (list_b))?
SeF

Yanıtlar:


127

Python'un sağladığı performans fonksiyonu budur set.issubset. Bununla birlikte, sorunuzun cevabı olup olmadığını netleştiren birkaç kısıtlama vardır.

Liste birden çok kez öğe içerebilir ve belirli bir sıraya sahiptir. Bir küme yapmaz. Ayrıca, kümeler yalnızca yıkanabilir nesneler üzerinde çalışır .

Alt küme veya alt sekans mı soruyorsunuz (yani bir dize arama algoritması isteyeceksiniz)? Listelerden herhangi biri birçok test için aynı mı olacak? Listedeki veri türleri nelerdir? Ve bu konu için bir liste olması gerekiyor mu?

Diğer yayınınız bir dikteyle kesişiyor ve liste türleri daha net hale getirdi ve set benzeri işlevleri için sözlük anahtar görünümlerini kullanma önerisi aldı. Bu durumda işe yaradığı biliniyor çünkü sözlük anahtarları bir küme gibi davranıyor (öyle ki Python'da setler olmadan önce sözlükler kullandık). Biri, sorunun üç saat içinde nasıl daha az spesifik hale geldiğini merak ediyor.


Sadece bir alt kümeye atıfta bulunuyorum ve issubset gayet iyi çalışıyor - Teşekkürler. Ancak burada 2 soru merak ediyorum. 1. Listelerden herhangi biri birçok test için aynı mı olacak? Bunlardan biri statik bir arama tablosu 2. gibi yapar Bir liste olması gerekiyor mu? Olmaz - statik arama tablosu en iyi performansı gösteren herhangi bir şey olabilir. Dinamik olan, statik bir arama yapmak için anahtarları çıkardığımız bir diksiyondur. Bu gerçek çözümü değiştirecek mi?
bilinmeyen

Fazla değil. Bir sözlüğün anahtarları set benzeri ve zaten bir karma tabloda düzenlenmiştir ve bu nedenle statik kısım için bir set kullanılması ek komplikasyonlara neden olmaz. Temel olarak, bir diksiyon olması, statik parçayı bir sete dönüştürmeniz gerekmeyebileceği anlamına gelir (O ​​(n) performansıyla tümünü (itertools.imap (dict.has_key, mylist)) kontrol edebilirsiniz).
Yann Vernier

Bunun (veya setlere dayanan başka bir çözümün) burada kabul edilen cevap olabileceğini anlamıyorum. Soru listeler hakkında ve açıkçası "bir listenin diğerinin bir alt kümesi olup olmadığını doğrula" alt kümesinin tam anlamıyla alınmayacağını düşünüyorum. Setlere dönüştürüldükten sonra, yinelenen öğelerle ilgili herhangi bir bilgi kaybolur, ancak ilk liste bunları içeriyorsa, bir listedeki tüm öğelerin bulunabileceğini gerçekten söylemek için ikinci listede görünüp görünmediğini kontrol etmek de önemli olabilir. diğerinin içinde. Setler bunu yapmaz!
Invader

Bağlam önemlidir; bu askerlere yardım ettiği için kabul edildi ve ayrımı açıkladı. Bize adayların setler halinde temsil edilebilecekleri söylendi, bu yüzden bu belirlenmiş bir görevdi. Durumunuz farklı olabilir ve bahsettiğiniz fark koleksiyonlar gibi çoklu ayarlar kullanılarak çözülebilir.
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
Bu en güzel görünüyor ve en basit yazıyor, ancak en hızlı olması gerekir, set(a).issubset(b) çünkü bu durumda sadece asete dönüştürürsiniz , ancak bzaman kazandırmaz. timeitİki komutta harcanan zamanı karşılaştırmak için kullanabilirsiniz . Örneğin timeit.repeat('set(a)<set(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000) ve timeit.repeat('set(a).issubset(b)', 'a = [1,3,5]; b = [1,3,5,7]', number=1000)
Yulan Liu

8
@YulanLiu: Sana kırmaktan nefret ediyorum, ama ilk şey issubset, argümanın bir set/ olup olmadığını kontrol etmek frozensetve eğer değilse, setkarşılaştırma için geçici birset biçime dönüştürür , kontrolü çalıştırır, ardından geçici . Zamanlama farklılıkları (eğer varsa), LEGB arama maliyetlerindeki küçük farklılıkların bir faktörü olacaktır ( setikinci kez bulmak , mevcut bir özellik öznitelik aramalarından daha pahalıdır set), ancak çoğunlukla yeterince büyük girdiler için bir yıkamadır.
ShadowRanger

3
Her iki liste de aynı değerleri içeriyorsa, bu yanlış değer döndürürse, koşul (a) <= set (b) olarak ayarlanmalıdır
ssi-anik

2
Bu cevap nasıl doğru olabilir. Set değil bir liste istedi. Tamamen farklılar. Ya a = [1, 3, 3, 5, 5] ve b = [1, 3, 3, 3, 5] ise. Küme teorisi kopyalar için uygun değildir.
Eamonn Kenny

1
Ayrıca a = [1,3,5] ve b = [1,3,5] olursa, set (a) <set (b) 'nin False değerini döndüreceğini de belirtmek isterim. Şu durumları işlemek için eşittir operatörünü ekleyebilirsiniz: yani 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)

Açıklama: Üreticinin one, öğenin listede olup olmadığını kontrol ederek liste arasında geçiş yaparak boolean oluşturma two. her öğe doğruysa all()döner True, başka False.

Ayrıca, allher öğeyi işlemek yerine, eksik öğenin ilk örneğinde False değerini döndüren bir avantaj da vardır .


Bence okunabilirlik ve elde etmeye çalıştığınız şeyin açık olması set(one).issubset(set(two))harika bir çözüm. Gönderdiğim çözümle, uygun karşılaştırma işleçleri tanımlanmışsa, herhangi bir nesne ile kullanabilmelisiniz.
16:52, voidnologo

4
Liste kavrama değil bir üreteç ifadesi kullanın; birincisi alldoğru şekilde kısa devreye izin verir , ikincisi testin başarısız olacağı ilk kontrolden anlaşılsa bile tüm kontrolleri yapar. Almak için köşeli parantezleri düşürmeniz yeterlidir all(x in two for x in one).
ShadowRanger

Yanlış mıyım yoksa bu yöntemi yerlilerle birlikte kullanamazsınız?
Homper

22

Öğelerin yıkanabilir olduğu varsayılarak

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

Örneğin yinelenen öğeleri umursamıyorsanız. [1, 2, 2]ve [1, 2]sonra sadece şunu kullanın:

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

Bir kavşaktan sonra küçük listede eşitliği test etmek bunu yapmanın en hızlı yolu mudur?

.issubsetbunu yapmanın en hızlı yolu olacak. Testten önce uzunluğun kontrol edilmesi issubsethızı artırmayacaktır, çünkü tekrarlamak ve kontrol etmek için hala O (N + M) öğeleriniz vardır.


7

Bir çözüm daha kullanmak a intersection.

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

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

Kümelerin kesişimi, set one

(VEYA)

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

List1 liste 2'de ise:

  • (x in two for x in one)bir liste oluşturur True.

  • yaptığımız zaman set(x in two for x in one)sadece bir öğe vardır (Doğru).


2

Yinelenenler küme teorisini kullanarak yanlış yanıtlara neden olacağından küme teorisi listeler için uygun değildir.

Örneğin:

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

anlamı yok. Evet, yanlış bir cevap veriyor, ancak set teorisi sadece karşılaştırdığı için bu doğru değil: 1,3,5 ve 1,3,4,5. Tüm kopyaları eklemelisiniz.

Bunun yerine, her bir öğenin oluşumunu saymalı ve kontrol etmek için eşit olandan daha fazlasını yapmalısınız. Bu çok pahalı değildir, çünkü O (N ^ 2) işlemlerini kullanmaz ve hızlı sıralama gerektirmez.

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

Sonra bunu çalıştırırken elde edersiniz:

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

1

Hiç kimse iki dizeyi karşılaştırmayı düşünmediğinden, benim teklifim.

Elbette borunun ("|") her iki listenin bir parçası olmadığını ve belki de otomatik olarak başka bir karakter seçip seçmediğini kontrol etmek isteyebilirsiniz, ama fikri anladınız.

Sayıların birkaç basamağı olabileceğinden ayırıcı olarak boş bir dize kullanmak çözüm değildir ([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

Partiye geç kalırsam affedersiniz. ;)

Biri olmadığını kontrol etmek için set Abir alt kümesidir set B, Pythonvardır A.issubset(B)ve A <= B. Bu çalışır setancak ve büyük işler AMA iç düzenlenmesinin karmaşıklığı bilinmemektedir. Referans: https://docs.python.org/2/library/sets.html#set-objects

Aşağıdaki açıklamalar ile list Abir alt küme olup olmadığını kontrol etmek için bir algoritma geldi list B.

  • Alt kümeyi bulma karmaşıklığını azaltmak sortiçin, alt kümeye hak kazanmak için öğeleri karşılaştırmadan önce her iki listeye de uygun buluyorum .
  • Bu yardımcı oldu ikinci listenin elemanın değeri zaman ilk listenin elemanın değerinden daha büyüktür .breakloopB[j]A[i]
  • last_index_jbaşlatmak için kullanılır loopüzerinde list Bson kaldığı yerden. Karşılaştırmaların başlangıcından başlamasından kaçınmaya yardımcı olur list B(gereksiz tahmin edebileceğiniz gibi , daha sonra başlamak list Biçin ).index 0iterations
  • O(n ln n)Her iki listeyi sıralamak ve O(n)altkümeyi kontrol etmek için karmaşıklık her biri olacaktır .
    O(n ln n) + O(n ln n) + O(n) = O(n ln n).

  • Kod çok var printher birinde neler olduğunu görmek için tabloların iterationarasında loop. Bunlar sadece anlamak içindir.

Bir listenin başka bir listenin alt kümesi olup olmadığını kontrol edin

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

Çıktı

[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

Bunları sıralarsanız, bir küme yerine bir liste kullanmak için artık bir neden yoktur…
LtWorf

0

Aşağıdaki kod, belirli bir kümenin başka bir kümenin "uygun alt kümesi" olup olmadığını kontrol eder

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


Teşekkürler @ YannVernier Hem alt küme hem de üst küme için boş denetimler içerecek şekilde değiştirilmiş, böylece her ikisi de boş olduğunda yanlış döndürür.
Leo Bastin

Ama bunu neden yapıyorsun? A'nın B'nin bir alt kümesi olması, A'nın B'de olmayan veya eşdeğer olarak hiçbir öğe içermediği anlamına gelir, A'daki tüm öğeler B'de de bulunur. Boş küme, kendisi de dahil olmak üzere tüm kümelerin bir alt kümesidir . Ekstra kontrolleriniz bunun olmadığını iddia ediyor ve bunun bir şekilde ideal olduğunu iddia ediyorsunuz, ancak yerleşik terminolojinin aksine. Avantajı nedir?
Yann Vernier

Teşekkürler @YannVernier Şimdi kod verilen bir kümenin başka bir kümenin "uygun bir alt kümesi" olup olmadığını kontrol eder.
Leo Bastin

Bu, setlerin kullanımına dayanan cevaplar kadar kötü . Matematiksel olarak konuşurken, bir küme farklı unsurların bir koleksiyonudur, bir listenin diğerinin parçası olup olmadığını kontrol ederken bu varsayımlara güvenebiliriz ve güvenmemeliyiz. İlk liste yinelenen içeriyorsa, söz konusu öğe ikinci listede yalnızca bir kez mevcut olsa bile işleviniz yine de True değerini döndürebilir . Listeleri karşılaştırmaya çalışırken bunun doğru davranış olduğunu düşünmüyorum.
Invader

0

Python 3.5 ' [*set()][index]de öğeyi almak için bir yapabilirsiniz . Diğer yöntemlere göre çok daha yavaş bir çözümdür.

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

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

[*result][0] == True

veya sadece len ve set ile

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

0

Bir listenin diğerinin bir alt kümesi olup olmadığını nasıl bildiğimi, sıralaması benim durumumda benim için önemlidir.

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

Çözümlerin çoğu, listelerin yinelemeleri olmadığını düşünüyor. Listelerinizde kopyalar olması durumunda şunları deneyebilirsiniz:

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

Alt listenin hiçbir zaman listeden farklı öğelere veya daha büyük miktarda ortak öğeye sahip olmamasını sağlar.

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

Bir listenin başka bir listede "yer alıp almadığını" soruyorsanız:

>>>if listA in listB: return True

ListA'daki her öğenin listB'de eşit sayıda eşleşen öğeye sahip olup olmadığını soruyorsanız şunları deneyin:

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

Bu benim için işe yaramıyor. İade yanlış olsa bile listA == ListB
Cass

@cass Yalnızca dizelerle test ettim. Bunu makinenizde deneyin. pastebin.com/9whnDYq4
DevPlayer

İkinci bölüme değil, "listB'deki listA: True döndür" bölümüne atıfta bulunuyordum.
cass

@cass Şunu düşünün: ['one', 'two'] içindeki ['one', 'two'] False değerini verir. ['one', 'two', 'three'] içindeki ['one', 'two'] False değerini verir. [['one', 'two'], 'three'] içindeki ['one', 'two'] True değerini verir. ListA == ListB ise listB'deki listA her zaman False değerini döndürür, çünkü listA'nın listB içinde bir liste öğesi olması gerekir. Belki de şöyle düşünüyorsunuz: listB'deki listA, "ListA'daki öğeler listB'deki öğeler olarak listeleniyor mu? Bu listB'deki listA'nın anlamı değil
DevPlayer

@cass Ah, görevimin nasıl karıştığını görüyorum. Orijinal gönderi listA'nın listB'nin bir alt kümesi olup olmadığını sınamasını istedi. Teknik olarak yazım orijinal yazının sorusuna dayanarak yanlış. Doğru olması için sorunun "[item0, item2, listA, item3, listA,] içindeki listA olup olmadığı" sorusunu sorması gerekirdi. "['D', 'c', 'f', 'a', 'b', 'a'] 'daki [' a ',' b ',' c '] öğeleri değil".
DevPlayer

-2

Eğer a2 is subset of a1,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))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.