सेट () कैसे लागू किया जाता है?


151

मैंने देखा है कि लोग कहते हैं कि setअजगर में वस्तुओं की ओ (1) सदस्यता-जाँच होती है। इसे अनुमति देने के लिए उन्हें आंतरिक रूप से कैसे लागू किया जाता है? यह किस तरह की डेटा संरचना का उपयोग करता है? उस कार्यान्वयन के अन्य क्या निहितार्थ हैं?

यहां हर उत्तर वास्तव में ज्ञानवर्धक था, लेकिन मैं केवल एक को स्वीकार कर सकता हूं, इसलिए मैं अपने मूल प्रश्न के निकटतम उत्तर के साथ जाऊंगा। सभी जानकारी के लिए धन्यवाद!

जवाबों:


139

इस सूत्र के अनुसार :

वास्तव में, सीपीथॉन के सेटों को डमी मूल्यों (सेट के सदस्यों की कुंजी) के साथ शब्दकोशों के रूप में लागू किया जाता है, कुछ अनुकूलन (ओं) के साथ जो मूल्यों की इस कमी का फायदा उठाते हैं।

तो मूल setरूप से अपने अंतर्निहित डेटा संरचना के रूप में एक हैशटेबल का उपयोग करता है। यह ओ (1) सदस्यता जाँच की व्याख्या करता है, क्योंकि हैशटेब में एक वस्तु की तलाश एक ओ (1) ऑपरेशन है, औसतन।

यदि आप इच्छुक हैं, तो आप सेट के लिए सीपीथॉन स्रोत कोड भी ब्राउज़ कर सकते हैं , जो कि अचिम डौमा के अनुसार , ज्यादातर dictकार्यान्वयन से कट-एंड-पेस्ट है ।


18
IIRC, मूल setकार्यान्वयन वास्तव में dict डमी मूल्यों के साथ था , और यह बाद में अनुकूलित हो गया।
dan04

1
बड़ा हे सबसे खराब स्थिति नहीं है? यदि आप एक उदाहरण पा सकते हैं जहां समय O (n) है तो वह O (n) है। मुझे अभी उन सभी ट्यूटोरियल से कुछ भी समझ नहीं आ रहा है।
क्लाउडीयू क्रेन्गा

4
नहीं, औसत मामला O (1) है, लेकिन सबसे खराब स्थिति हैश टेबल लुकअप के लिए O (N) है।
जस्टिन एथियर

4
@ClaudiuCreanga यह एक पुरानी टिप्पणी है, लेकिन सिर्फ स्पष्ट करने के लिए: big-O संकेतन आपको चीजों की वृद्धि दर पर ऊपरी सीमा बताता है, लेकिन आप औसत मामले के प्रदर्शन के विकास को ऊपरी कर सकते हैं और आप सबसे खराब स्थिति के विकास को अलग कर सकते हैं प्रदर्शन।
कर्क बॉय

79

जब लोग कहते हैं कि सेट में O (1) सदस्यता-जाँच है, तो वे औसत मामले के बारे में बात कर रहे हैं । में सबसे खराब मामले (जब सभी टुकड़ों में बंटी मूल्यों टकराने) सदस्यता-चेकिंग हे (एन) है। समय जटिलता पर अजगर विकी देखें ।

विकिपीडिया लेख कहते हैं सबसे अच्छा मामले में एक हैश तालिका आकार है यह नहीं है कि के लिए समय जटिलता O(1 + k/n)। यह परिणाम सीधे पायथन सेट पर लागू नहीं होता है क्योंकि पायथन सेट एक हैश तालिका का उपयोग करता है जो आकार बदलता है।

विकिपीडिया लेख पर थोड़ा आगे कहा गया है कि औसत मामले के लिए, और एक साधारण वर्दी हैशिंग फ़ंक्शन को संभालने के लिए, समय जटिलता है O(1/(1-k/n)), जहां k/nएक निरंतरता द्वारा बाध्य किया जा सकता है c<1

Big-O केवल n → to के रूप में स्पर्शोन्मुख व्यवहार को संदर्भित करता है। चूँकि k / n एक स्थिर, c <1 से घिरा हो सकता है, n से स्वतंत्र ,

O(1/(1-k/n))इससे बड़ा कोई नहीं = के O(1/(1-c))बराबर है ।O(constant)O(1)

इसलिए, एक समान सरल हैशिंग को मानते हुए, औसतन , पायथन सेटों की सदस्यता-जाँच है O(1)


14

मुझे लगता है कि इसकी सामान्य गलती, setलुकअप (या उस मामले के लिए हैशटेबल) O (1) नहीं है।
विकिपीडिया से

सरलतम मॉडल में, हैश फ़ंक्शन पूरी तरह से अनिर्दिष्ट है और तालिका आकार नहीं देती है। हैश फ़ंक्शन के सर्वोत्तम संभव विकल्प के लिए, ओपन एड्रेसिंग के साथ आकार n की एक तालिका में कोई टक्कर नहीं है और n तत्वों को रखती है, जिसमें सफल लुकअप के लिए एक ही तुलना है, और चेन और k कुंजी के साथ आकार n की तालिका में न्यूनतम अधिकतम है लुकअप के लिए (0, kn) टकराव और O (1 + k / n) तुलना। हैश फ़ंक्शन के सबसे खराब विकल्प के लिए, प्रत्येक प्रविष्टि टकराव का कारण बनती है, और हैश तालिकाओं को रैखिक खोज के लिए पतित कर देती है, एक प्रविष्टि के लिए ion (k) amortized तुलना और प्रति k कश्मीर तुलना के साथ।

संबंधित: क्या एक जावा हैशमैप वास्तव में हे (1) है?


4
लेकिन वे आइटम देखने के लिए लगातार समय लेते हैं: अजगर-एम टाइमटाइम -s "s = set (रेंज (10))" "5 इन s" 10000000 लूप्स, सर्वश्रेष्ठ 3: 0.0642 usec प्रति लूप <-> अजगर - m timeit -s "s = set (रेंज (10000000))" "5 इन s" 10000000 लूप्स, सर्वश्रेष्ठ 3: 0.0634 usec प्रति लूप ... और यह सबसे बड़ा सेट है जो मेमोरीर को नहीं फेंकता है
Jochen Bifel

2
@ THC4k आप सभी ने साबित किया है कि एक्स को देखना निरंतर समय में किया जाता है, लेकिन इसका मतलब यह नहीं है कि एक्स + वाई को देखने का समय उतना ही समय लगेगा जो ओ (1) है।
Shay Erlichmen

3
@intuited: यह होता है, लेकिन ऊपर दिया गया परीक्षण यह साबित नहीं करता है कि आप "5" को देख सकते हैं उसी समय में आप "485398" को देख सकते हैं, या कोई अन्य संख्या जो भयानक टक्कर वाले स्थान पर हो सकती है। यह एक ही समय में एक अलग-अलग आकार के हैश में एक ही तत्व को देखने के बारे में नहीं है (वास्तव में, यह बिल्कुल आवश्यक नहीं है), बल्कि यह इस बारे में है कि क्या आप वर्तमान तालिका में समान समय में प्रत्येक प्रविष्टि तक पहुंच सकते हैं - कुछ चीजें जो हैश टेबल के लिए मूल रूप से असंभव है क्योंकि आम तौर पर हमेशा टकराव होगा।
निक बैस्टिन

3
दूसरे शब्दों में, लुकअप करने का समय संग्रहीत मूल्यों की संख्या पर निर्भर करता है, क्योंकि इससे टकराव की संभावना बढ़ जाती है।
intuited

3
@intuited: नहीं, यह गलत है। जब संग्रहीत मूल्यों की संख्या बढ़ जाती है, तो पायथन स्वचालित रूप से हैशटेबल के आकार में वृद्धि करेगा, और टकराव की दर लगभग स्थिर रहती है। समान रूप से वितरित O (1) हैश एल्गोरिथम मान लें, तो हैशटेबल लुकअप O (1) amortized है । आप वीडियो प्रस्तुति "द माइटी डिक्शनरी" python.mirocommunity.org/video/1591/…
रेयान

13

हम सभी के पास स्रोत तक आसान पहुंच है , जहां टिप्पणी पूर्ववर्ती set_lookkey()कहती है:

/* set object implementation
 Written and maintained by Raymond D. Hettinger <python@rcn.com>
 Derived from Lib/sets.py and Objects/dictobject.c.
 The basic lookup function used by all operations.
 This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
 The initial probe index is computed as hash mod the table size.
 Subsequent probe indices are computed as explained in Objects/dictobject.c.
 To improve cache locality, each probe inspects a series of consecutive
 nearby entries before moving on to probes elsewhere in memory.  This leaves
 us with a hybrid of linear probing and open addressing.  The linear probing
 reduces the cost of hash collisions because consecutive memory accesses
 tend to be much cheaper than scattered probes.  After LINEAR_PROBES steps,
 we then use open addressing with the upper bits from the hash value.  This
 helps break-up long chains of collisions.
 All arithmetic on hash should ignore overflow.
 Unlike the dictionary implementation, the lookkey function can return
 NULL if the rich comparison returns an error.
*/


...
#ifndef LINEAR_PROBES
#define LINEAR_PROBES 9
#endif

/* This must be >= 1 */
#define PERTURB_SHIFT 5

static setentry *
set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash)  
{
...

2
यह उत्तर C सिंटैक्स हाइलाइटिंग से लाभान्वित होगा । टिप्पणी का हाइलाइटिंग पायथन सिंटैक्स वास्तव में बहुत बुरा लग रहा है।
user202729

टिप्पणी के बारे में "यह हमें रैखिक जांच और खुले पते के एक संकर के साथ छोड़ देता है", क्या रेखीय जांच खुले पते में एक तरह के टकराव के संकल्प की जांच नहीं है, जैसा कि en.wikipedia.org/wiki/Open_addressing में वर्णित है ? इसलिए, रैखिक जांच ओपन एड्रेसिंग का एक उपप्रकार है और टिप्पणी का कोई मतलब नहीं है।
एलन इवेंजलिस्ता

2

थोड़ा और अधिक के अंतर पर जोर set'sदेने के लिए dict's, यहां setobject.cटिप्पणी अनुभागों का एक अंश दिया गया है, जो स्पष्ट रूप से सेट के विरूद्ध dicts के मुख्य अंतर को स्पष्ट करता है।

सेट के लिए मामलों का उपयोग उन शब्दकोशों से काफी भिन्न होता है जहाँ देखा-देखी चाबियां मौजूद होने की अधिक संभावना है। इसके विपरीत, सेट मुख्य रूप से सदस्यता परीक्षण के बारे में होते हैं जहां किसी तत्व की उपस्थिति पहले से ज्ञात नहीं होती है। तदनुसार, सेट कार्यान्वयन को पाया और न पाया गया दोनों मामलों के लिए अनुकूलित करने की आवश्यकता है।

स्रोत गितुब पर

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