यदि सूची अजगर में किसी भी आइटम को साझा करती है तो परीक्षण करें


131

मैं जांचना चाहता हूं कि क्या एक सूची में कोई भी वस्तु दूसरी सूची में मौजूद है। मैं इसे केवल नीचे दिए गए कोड के साथ कर सकता हूं, लेकिन मुझे संदेह है कि ऐसा करने के लिए एक लाइब्रेरी फ़ंक्शन हो सकता है। यदि नहीं, तो क्या समान परिणाम प्राप्त करने का एक अधिक पायथोनिक तरीका है।

In [78]: a = [1, 2, 3, 4, 5]

In [79]: b = [8, 7, 6]

In [80]: c = [8, 7, 6, 5]

In [81]: def lists_overlap(a, b):
   ....:     for i in a:
   ....:         if i in b:
   ....:             return True
   ....:     return False
   ....: 

In [82]: lists_overlap(a, b)
Out[82]: False

In [83]: lists_overlap(a, c)
Out[83]: True

In [84]: def lists_overlap2(a, b):
   ....:     return len(set(a).intersection(set(b))) > 0
   ....: 

केवल अनुकूलन के बारे में सोच सकता हूं len(...) > 0क्योंकि bool(set([]))फाल्स की पैदावार होती है। और निश्चित रूप से अगर आप अपनी सूची को सेट के रूप में शुरू करते हैं तो आप सेट ओवरहेड को बचाएंगे।
msw


1
नोट आप अलग नहीं कर सकते हैं कि Trueसे 1और Falseसे 0। अन्य समाधानों के साथ भी यही not set([1]).isdisjoint([True])मिलता है True
दिमली

जवाबों:


313

संक्षिप्त उत्तर : उपयोग not set(a).isdisjoint(b), यह आमतौर पर सबसे तेज़ है।

दो सूचियों को सूचीबद्ध करने aऔर bकिसी भी आइटम को साझा करने के लिए चार सामान्य तरीके हैं । पहला विकल्प यह है कि दोनों को सेट करें और उनके प्रतिच्छेदन की जाँच करें, जैसे:

bool(set(a) & set(b))

क्योंकि पायथन में हैश टेबल का उपयोग करके सेट्स को संग्रहीत किया जाता है, उन्हें खोज रहा हैO(1) ( पायथन में ऑपरेटरों की जटिलता के बारे में अधिक जानकारी के लिए यहां देखें )। सैद्धांतिक रूप से, यह है O(n+m)के लिए औसतन nऔर mसूचियों में वस्तुओं aऔर b। लेकिन 1) इसे पहले सूचियों से बाहर सेट बनाना होगा, जो समय की एक नगण्य राशि ले सकता है, और 2) यह मानता है कि हैशिंग टकराव आपके डेटा के बीच विरल हैं।

इसे करने का दूसरा तरीका सूची में पुनरावृत्ति प्रदर्शन कर रहे एक जनरेटर अभिव्यक्ति का उपयोग कर रहा है, जैसे:

any(i in a for i in b)

यह इन-प्लेस को खोजने की अनुमति देता है, इसलिए मध्यस्थ चर के लिए कोई नई मेमोरी आवंटित नहीं की जाती है। यह पहली खोज पर भी निकल जाता है। लेकिन inऑपरेटर हमेशा O(n)सूचियों पर होता है ( यहां देखें )।

एक अन्य प्रस्तावित विकल्प सूची में से एक के माध्यम से एक हाइब्रिडो पुनरावृति है, दूसरे को एक सेट में परिवर्तित करें और इस सेट पर सदस्यता के लिए परीक्षण करें, जैसे:

a = set(a); any(i in a for i in b)

चौथा दृष्टिकोण उदाहरण isdisjoint()के लिए (जमे हुए) सेट ( यहां देखें) की विधि का लाभ उठाना है :

not set(a).isdisjoint(b)

यदि आपके द्वारा खोजे जाने वाले तत्व एक सरणी की शुरुआत के पास हैं (जैसे कि इसे क्रमबद्ध किया गया है), तो जनरेटर अभिव्यक्ति का पक्ष लिया जाता है, क्योंकि सेट चौराहे की विधि को मध्यस्थ चर के लिए नई मेमोरी आवंटित करनी होती है:

from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974

सूची आकार के कार्य में इस उदाहरण के लिए निष्पादन समय का एक ग्राफ यहां दिया गया है:

शुरुआत में साझा किए जाने पर परीक्षण निष्पादन का समय साझा करना

ध्यान दें कि दोनों कुल्हाड़ी लॉगरिदमिक हैं। यह जनरेटर अभिव्यक्ति के लिए सबसे अच्छा मामला दर्शाता है। जैसा कि देखा जा सकता है, isdisjoint()विधि बहुत छोटी सूची आकारों के लिए बेहतर है, जबकि बड़ी सूची आकारों के लिए जनरेटर की अभिव्यक्ति बेहतर है।

दूसरी ओर, जैसा कि हाइब्रिड और जनरेटर अभिव्यक्ति के लिए खोज शुरू होती है, अगर साझा तत्व व्यवस्थित रूप से सरणी के अंत में है (या दोनों सूची किसी भी मूल्य को साझा नहीं करता है), तो असहमति और सेट चौराहे दृष्टिकोण तब होते हैं जनरेटर की अभिव्यक्ति और हाइब्रिड दृष्टिकोण की तुलना में अधिक तेज़।

>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
13.739536046981812
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
0.08102107048034668

अंत में साझा किए जाने पर परीक्षण साझा करने का समय

यह ध्यान रखना दिलचस्प है कि जनरेटर की अभिव्यक्ति बड़े सूची आकारों के लिए धीमी है। यह पिछले आंकड़े के लिए 100000 के बजाय केवल 1000 पुनरावृत्ति के लिए है। जब कोई तत्व साझा नहीं किए जाते हैं, तो यह सेटअप अच्छी तरह से अनुमानित हो जाता है, और तिरस्कार और सेट चौराहे के दृष्टिकोण के लिए सबसे अच्छा मामला है।

यहां यादृच्छिक संख्याओं का उपयोग करके दो विश्लेषण किए गए हैं (एक तकनीक या किसी अन्य का पक्ष लेने के लिए सेटअप को हेराफेरी करने के बजाय):

साझा करने के उच्च अवसर के साथ बेतरतीब ढंग से उत्पन्न डेटा के लिए तत्व साझाकरण परीक्षण निष्पादन समय साझा करने के उच्च अवसर के साथ बेतरतीब ढंग से उत्पन्न डेटा के लिए तत्व साझाकरण परीक्षण निष्पादन समय

साझा करने का उच्च मौका: तत्वों को यादृच्छिक रूप से लिया जाता है [1, 2*len(a)]। साझा करने की कम संभावना: तत्वों को यादृच्छिक रूप से लिया जाता है [1, 1000*len(a)]

अब तक, यह विश्लेषण माना जाता है कि दोनों सूचियाँ एक ही आकार की हैं। विभिन्न आकारों की दो सूचियों के मामले में, उदाहरण के aलिए बहुत छोटा है, isdisjoint()हमेशा तेज होता है:

शुरुआत में साझा किए जाने पर दो अलग-अलग आकार की सूचियों पर तत्व साझाकरण परीक्षण निष्पादन समय अंत में साझा किए जाने पर दो अलग-अलग आकार की सूचियों पर तत्व साझाकरण परीक्षण निष्पादन समय

सुनिश्चित करें कि aसूची छोटी है, अन्यथा प्रदर्शन कम हो जाता है। इस प्रयोग में, aसूची आकार स्थिर था 5

संक्षेप में:

  • यदि सूचियाँ बहुत छोटी हैं (<10 तत्व), not set(a).isdisjoint(b)हमेशा सबसे तेज़ होती है।
  • यदि सूचियों के तत्वों को क्रमबद्ध किया जाता है या एक नियमित संरचना होती है जिसका आप लाभ उठा सकते हैं, तो जनरेटर अभिव्यक्ति any(i in a for i in b)बड़े सूची आकारों पर सबसे तेज़ है;
  • सेट चौराहे का परीक्षण करें not set(a).isdisjoint(b), जो हमेशा की तुलना में तेज होता है bool(set(a) & set(b))
  • हाइब्रिड "सूची के माध्यम से पुनरावृति, सेट पर परीक्षण" a = set(a); any(i in a for i in b)आम तौर पर अन्य तरीकों की तुलना में धीमी है।
  • जनरेटर अभिव्यक्ति और हाइब्रिड दो अन्य दृष्टिकोणों की तुलना में बहुत धीमा है जब यह तत्वों को साझा किए बिना सूचियों में आता है।

ज्यादातर मामलों में, isdisjoint()विधि का उपयोग करना सबसे अच्छा तरीका है क्योंकि जनरेटर अभिव्यक्ति को निष्पादित करने में अधिक समय लगेगा, क्योंकि यह बहुत अक्षम है जब कोई तत्व साझा नहीं किए जाते हैं।


8
यह वहां कुछ उपयोगी डेटा है, यह दर्शाता है कि बिग-ओ विश्लेषण सभी नहीं है और रनिंग टाइम के बारे में सभी तर्क समाप्त करते हैं।
स्टीव एलीसन

सबसे खराब स्थिति के बारे में क्या? anyपहले गैर-गलत मूल्य पर बाहर निकलता है। एक सूची का उपयोग करना जहां एकमात्र मिलान मूल्य अंत में है, हमें यह मिलता है: timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 13.739536046981812 timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,-0,-1)]", number=1000) 0.08102107048034668 ... और यह केवल 1000 पुनरावृत्तियों के साथ है।
RobM

2
जानकारी के लिए धन्यवाद @RobM। मैंने इसे दर्शाने और इस धागे में प्रस्तावित अन्य तकनीकों को ध्यान में रखने के लिए अपना उत्तर अपडेट किया है।
सोरवक्स

यह not set(a).isdisjoint(b)परीक्षण करना चाहिए कि क्या दो सूचियाँ किसी सदस्य को साझा करती हैं। यदि दो सूची सदस्य साझा नहीं करते हैं तो set(a).isdisjoint(b)रिटर्न । उत्तर संपादित किया जाना चाहिए?True
गुइलूचोन

1
सिर के लिए धन्यवाद, @ गिलुचोन, यह तय हो गया है।
सोरवक्स

25
def lists_overlap3(a, b):
    return bool(set(a) & set(b))

नोट: उपरोक्त मान लें कि आप उत्तर के रूप में एक बूलियन चाहते हैं। यदि आप सभी की जरूरत है एक ifबयान में उपयोग करने के लिए एक अभिव्यक्ति है , बस का उपयोग करेंif set(a) & set(b):


5
यह सबसे खराब स्थिति वाला O (n + m) है। हालाँकि, नीचे की ओर यह है कि यह एक नया सेट बनाता है, और जब कोई सामान्य तत्व जल्दी मिल जाता है, तो यह जमानत नहीं करता है।
मैथ्यू फ्लैशेन

1
मैं उत्सुक हूं कि ऐसा क्यों है O(n + m)। मेरा अनुमान है कि सेट को हैश-तालिकाओं का उपयोग करके लागू किया जाता है, और इस तरह inऑपरेटर O(1)समय में काम कर सकता है (पतित मामलों को छोड़कर)। क्या ये सही है? यदि हां, तो हैश टेबल्स में सबसे खराब स्थिति लुकअप परफॉर्मेंस की है O(n), तो क्या इसका मतलब यह है कि विपरीत स्थिति में भी इसका O(n * m)प्रदर्शन होगा ?
मार्क

1
@ चिह्न: सैद्धांतिक रूप से, आप सही हैं। व्यावहारिक रूप से, किसी को परवाह नहीं है; CPython स्रोत में ऑब्जेक्ट्स / dictobject.c में टिप्पणियों को पढ़ें (सेट केवल कुंजी, कोई मान नहीं के साथ बस dicts हैं) और देखें कि क्या आप उन कुंजियों की एक सूची उत्पन्न कर सकते हैं जो O (n) लुकअप प्रदर्शन का कारण बनेंगे।
जॉन माचिन

ठीक है, स्पष्ट करने के लिए धन्यवाद, मैं सोच रहा था कि क्या कुछ जादू चल रहा है :)। हालांकि मैं मानता हूं कि व्यावहारिक रूप से मुझे देखभाल करने की आवश्यकता नहीं है, यह कुंजी की एक सूची उत्पन्न करने के लिए तुच्छ है जो O(n)लुकअप प्रदर्शन का कारण बनेगा ;), pastebin.com/Kn3kAW7u बस lafs के लिए देखें ।
8-3 बजे

2
हाँ मुझे पता है। इसके अलावा मैंने सिर्फ उस स्रोत को पढ़ा है जो आपने मुझे इंगित किया है, जो गैर-बेतरतीब हैश फ़ंक्शन (जैसे अंतर्निहित एक) के मामले में और भी अधिक जादू करता है। मैं मान लिया यह randomishness आवश्यक है, जावा एक, इस तरह monstrousities में जो परिणाम की तरह stackoverflow.com/questions/2634690/... । मुझे खुद को याद दिलाते रहना चाहिए कि पायथन इज़ नॉट जावा (धन्यवाद देवता!)।
फादर जूल

10
def lists_overlap(a, b):
  sb = set(b)
  return any(el in sb for el in a)

यह asymptotically इष्टतम (सबसे खराब स्थिति O (n + m)) है, और इसकी वजह से चौराहे के दृष्टिकोण से बेहतर हो सकता है any शॉर्ट-सर्किटिंग है।

उदाहरण के लिए:

lists_overlap([3,4,5], [1,2,3])

जैसे ही यह मिलेगा, सच लौटेगा 3 in sb

संपादित करें: एक और भिन्नता (डेव किर्बी के साथ धन्यवाद):

def lists_overlap(a, b):
  sb = set(b)
  return any(itertools.imap(sb.__contains__, a))

यह imapजनरेटर की समझ के बजाय C पर लागू होने वाले पुनरावृत्त पर निर्भर करता है । यह sb.__contains__मैपिंग फ़ंक्शन के रूप में भी उपयोग करता है । मुझे नहीं पता कि इससे कितना प्रदर्शन अंतर पड़ता है। यह अभी भी शॉर्ट-सर्किट होगा।


1
चौराहे के दृष्टिकोण में छोरों सभी सी कोड में हैं; आपके दृष्टिकोण में एक लूप है जिसमें पायथन कोड शामिल है। बड़ा अज्ञात है कि क्या एक खाली चौराहे की संभावना है या संभावना नहीं है।
जॉन माचिन

2
आप यह भी उपयोग कर सकते हैं any(itertools.imap(sb.__contains__, a))जो अभी भी तेज होना चाहिए क्योंकि यह लंबोदा फ़ंक्शन का उपयोग करने से बचता है।
डेव किर्बी

धन्यवाद, @Dave :) मैं मानता हूँ कि लंबोदर को हटाना एक जीत है।
मैथ्यू फ्लेशेन

4

तुम भी anyसूची समझ के साथ उपयोग कर सकते हैं :

any([item in a for item in b])

6
आप कर सकते हैं, लेकिन समय O (n * m) है जबकि सेट चौराहे के दृष्टिकोण का समय O (n + m) है। आप इसे सूची बोध के बिना भी कर सकते हैं (खो सकते हैं []) और यह तेजी से चलेगा और कम मेमोरी का उपयोग करेगा, लेकिन समय अभी भी O (n * m) होगा।
जॉन मैकिन

1
हालांकि आपका बड़ा O विश्लेषण सत्य है, मुझे संदेह है कि n और m के छोटे मूल्यों के लिए अंतर्निहित हैशटेबल्स को बनाने में लगने वाला समय खेल में आ जाएगा। बिग ओ ने हैश की गणना करने में लगने वाले समय को नजरअंदाज कर दिया।
एंथोनी कॉनर्स

2
एक "हैशटेबल" का निर्माण परिशोधन O (n) है।
जॉन मैकिन

1
मुझे लगता है कि लेकिन आप जो लगातार फेंक रहे हैं वह बहुत बड़ा है। यह n के बड़े मूल्यों के लिए मायने नहीं रखता है, लेकिन यह छोटे लोगों के लिए करता है।
एंथनी कॉन्सलर

3

अजगर 2.6 या बाद में आप कर सकते हैं:

return not frozenset(a).isdisjoint(frozenset(b))

1
ऐसा लगता है कि किसी को पहले तर्क के रूप में सेट या फ्रोज़ेनसेट की आपूर्ति नहीं करनी है। मैंने एक स्ट्रिंग के साथ कोशिश की और यह काम किया (यानी: कोई भी चलने योग्य होगा)।
अकटौ

2

आप फ़ंक्शन / वा जनरेटर अभिव्यक्ति में निर्मित किसी भी का उपयोग कर सकते हैं:

def list_overlap(a,b): 
     return any(i for i in a if i in b)

जैसा कि जॉन और लाई ने बताया है कि यह गलत परिणाम देता है जब प्रत्येक के लिए दो सूचियों बूल (i) == गलत द्वारा साझा किया जाता है। यह होना चाहिए:

return any(i in b for i in a)

1
ले रयान की टिप्पणी को प्रवर्तित करना: किसी भी आइटम x के लिए गलत परिणाम देगा जो उस चौराहे पर है जहां bool(x)गलत है। लीन रयान के उदाहरण में, x 0. केवल फिक्स है any(True for i in a if i in b)जो पहले से देखे गए के रूप में बेहतर लिखा गया है any(i in b for i in a)
जॉन मैकिन

1
सुधार: गलत परिणाम दे देंगे जब सभी आइटम xचौराहे में ऐसी है कि कर रहे हैं bool(x)है False
जॉन मैकिन

1

यह प्रश्न बहुत पुराना है, लेकिन मैंने देखा कि जब लोग सेट बनाम सूचियों पर बहस कर रहे थे, तो किसी ने उन्हें एक साथ उपयोग करने के बारे में नहीं सोचा था। सोरावक्स के उदाहरण के बाद,

सूचियों के लिए सबसे खराब मामला:

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
100.91506409645081
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
19.746716022491455
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
0.092626094818115234

और सूचियों के लिए सबसे अच्छा मामला:

>>> timeit('bool(set(a) & set(b))',  setup="a=list(range(10000)); b=list(range(10000))", number=100000)
154.69790101051331
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
0.082653045654296875
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=list(range(10000))", number=100000)
0.08434605598449707

तो दो सूचियों के माध्यम से पुनरावृत्ति करने से भी तेज है, हालांकि यह देखने के लिए एक सूची है कि क्या यह एक सेट में है, जो समझ में आता है कि अगर कोई संख्या सेट में है, तो जाँच करें कि सूची के माध्यम से पुनरावृत्ति करते समय एक निरंतर समय लगता है, समय की लंबाई के लिए आनुपातिक होता है। सूचि।

इस प्रकार, मेरा निष्कर्ष यह है कि एक सूची के माध्यम से पुनरावृत्त करें, और जांचें कि क्या यह एक सेट में है


1
का उपयोग करते हुए isdisjoint(): एक (जमे हुए) सेट के रूप में @Toughy ने संकेत दिया पर विधि और भी बेहतर है timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)=> .00913715362548828
Aktau

1

यदि आपको परवाह नहीं है कि अतिव्यापी तत्व क्या हो सकता है, तो आप बस lenसंयुक्त सूची बनाम सेट के रूप में संयुक्त सूची की जांच कर सकते हैं । यदि अतिव्यापी तत्व हैं, तो सेट छोटा होगा:

len(set(a+b+c))==len(a+b+c) ओवरलैप न होने पर सही रिटर्न।


यदि पहला मान ओवरलैप हो जाता है, तब भी यह पूरी सूची को सेट में बदल देगा, चाहे कितना भी बड़ा हो।
पीटर वुड

1

मैं कार्यात्मक प्रोग्रामिंग शैली के साथ एक और एक फेंक दूंगा:

any(map(lambda x: x in a, b))

स्पष्टीकरण:

map(lambda x: x in a, b)

उन बूलियन्स की सूची देता है जहां तत्व bपाए जाते हैं a। उस सूची को फिर पास कर दिया जाता है any, जो Trueकिसी भी तत्व के होने पर वापस लौट जाता है True

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.