TL; DR: O(1)
यदि आप अपने हैश फ़ंक्शन को समान रूप से हैश फ़ंक्शन के एक सार्वभौमिक परिवार से यादृच्छिक रूप से चुनते हैं, तो हश टेबल सबसे खराब समय की गारंटी देता है। अपेक्षित सबसे खराब स्थिति औसत मामले के समान नहीं है।
डिस्क्लेमर: मैं औपचारिक रूप से यह साबित नहीं करता है कि हैश टेबल हैं O(1)
, इसके लिए इस वीडियो पर एक नज़र डालें [ 1 ]। मैं भी हैश तालिकाओं के परिशोधन पहलुओं पर चर्चा नहीं करता। यह हैशिंग और टकराव के बारे में चर्चा के लिए रूढ़िवादी है।
मुझे इस विषय पर अन्य उत्तरों और टिप्पणियों में आश्चर्यजनक रूप से बहुत भ्रम की स्थिति दिखाई देती है, और इस लंबे उत्तर में उनमें से कुछ को सुधारने का प्रयास करेंगे।
सबसे खराब स्थिति के बारे में तर्क देना
विभिन्न प्रकार के सबसे खराब मामले विश्लेषण हैं। यहाँ अब तक सबसे अधिक जवाब देने वाला विश्लेषण सबसे खराब स्थिति नहीं है, बल्कि औसत मामला [ 2 ] है। औसत केस विश्लेषण अधिक व्यावहारिक हो जाता है। हो सकता है कि आपके एल्गोरिथ्म में एक सबसे खराब केस इनपुट हो, लेकिन वास्तव में अन्य सभी संभावित इनपुट के लिए अच्छी तरह से काम करता हो। बॉटमलाइन आपका रनटाइम डेटासेट पर निर्भर करता है आप चल रहे हैं।
get
हैश तालिका की विधि के निम्नलिखित छद्मकोश पर विचार करें । यहाँ मैं मान रहा हूं कि हम टकराव से टकराते हैं, इसलिए तालिका की प्रत्येक प्रविष्टि (key,value)
जोड़े की एक सूचीबद्ध सूची है । हम यह भी मानते हैं कि बाल्टी की संख्या m
निश्चित है O(n)
, लेकिन n
इनपुट में तत्वों की संख्या कहां है।
function get(a: Table with m buckets, k: Key being looked up)
bucket <- compute hash(k) modulo m
for each (key,value) in a[bucket]
return value if k == key
return not_found
जैसा कि अन्य उत्तर बताते हैं, यह औसत O(1)
और सबसे खराब स्थिति में चलता हैO(n)
। हम यहां चुनौती के द्वारा प्रमाण का थोड़ा स्केच बना सकते हैं। चुनौती इस प्रकार है:
(1) आप अपने हैश टेबल एल्गोरिथ्म को एक विरोधी को देते हैं।
(२) विरोधी इसका अध्ययन कर सकता है और जब तक वह चाहे तब तक तैयारी कर सकता है।
(३) अंत में विरोधी आपको n
अपनी तालिका में सम्मिलित करने के लिए आकार का एक इनपुट देता है ।
सवाल यह है कि प्रतिकूल इनपुट पर आपकी हैश टेबल कितनी तेज है?
चरण (1) से विरोधी आपके हैश फ़ंक्शन को जानता है; चरण के दौरान (2) विरोधी n
उसी के साथ तत्वों की एक सूची तैयार कर सकता है hash modulo m
, उदाहरण के लिए, तत्वों के एक समूह के बेतरतीब ढंग से कंप्यूटिंग; और फिर (3) वे आपको वह सूची दे सकते हैं। लेकिन लो और निहारना, चूंकि सभी n
तत्व एक ही बाल्टी में हैश करते हैं, आपके एल्गोरिथ्म को O(n)
उस बाल्टी में लिंक की गई सूची को पार करने में समय लगेगा । कोई फर्क नहीं पड़ता कि हम कितनी बार चुनौती का सामना करते हैं, विरोधी हमेशा जीतता है, और यह कि आपका एल्गोरिथ्म कितना बुरा है, सबसे खराब स्थिति O(n)
।
हेहिंग कैसे हे (1) है?
पिछली चुनौती में हमें फेंक दिया गया था कि विरोधी हमारे हैश फ़ंक्शन को अच्छी तरह से जानते थे, और उस ज्ञान का उपयोग सबसे खराब इनपुट को तैयार करने के लिए कर सकते थे। क्या होगा अगर हमेशा एक निश्चित हैश फ़ंक्शन का उपयोग करने के बजाय, हमारे पास वास्तव में हैश फ़ंक्शन का एक सेट होता है H
, जो कि एल्गोरिथ्म यादृच्छिक रूप से रनटाइम से चुन सकता है? यदि आप उत्सुक हैं, H
तो हैश फ़ंक्शन [ 3 ] का एक सार्वभौमिक परिवार कहा जाता है । ठीक है, चलो इस में कुछ यादृच्छिकता जोड़ने का प्रयास करें।
पहले मान लें कि हमारी हैश तालिका में एक बीज भी शामिल है r
, और r
निर्माण समय पर एक यादृच्छिक संख्या को सौंपा गया है। हम इसे एक बार असाइन करते हैं और फिर इसे उस हैश टेबल उदाहरण के लिए तय किया जाता है। अब हम अपने स्यूडोकोड पर दोबारा गौर करते हैं।
function get(a: Table with m buckets and seed r, k: Key being looked up)
rHash <- H[r]
bucket <- compute rHash(k) modulo m
for each (key,value) in a[bucket]
return value if k == key
return not_found
यदि हम चुनौती को एक और बार आज़माते हैं: चरण 1 (1) से तो विरोधी हमारे पास मौजूद सभी हैश कार्यों को जान सकता है H
, लेकिन अब हम जिस विशिष्ट हैश फ़ंक्शन का उपयोग करते हैं, वह निर्भर करता है r
। मूल्य r
हमारी संरचना के लिए निजी है, विरोधी इसे रनटाइम पर निरीक्षण नहीं कर सकता है, और न ही समय से पहले इसकी भविष्यवाणी कर सकता है, इसलिए वह ऐसी सूची को व्यक्त नहीं कर सकता है जो हमारे लिए हमेशा खराब है। मान लेते हैं कि चरण (2) में विरोधी यादृच्छिक रूप से एक कार्य hash
को चुनता है H
, फिर वह n
टकरावों की एक सूची तैयार करता है hash modulo m
, और चरण (3) के लिए भेजता है, उंगलियों को पार करता है कि रनटाइम H[r]
में वही होगा जो hash
उन्होंने चुना था।
यह प्रतिकूल के लिए एक गंभीर शर्त है, जिस सूची को वह तैयार करता है, उसके तहत टकराता है hash
, लेकिन किसी अन्य हैश फ़ंक्शन के तहत बस एक यादृच्छिक इनपुट होगा H
। यदि वह इस शर्त को जीतता है तो हमारा रन टाइम O(n)
पहले की तरह सबसे खराब स्थिति में होगा , लेकिन अगर वह हार जाता है तो अच्छी तरह से हमें सिर्फ एक यादृच्छिक इनपुट दिया जा रहा है जो औसत O(1)
समय लेता है । और वास्तव में अधिकांश बार विरोधी हार जाएगा, वह केवल एक बार हर |H|
चुनौतियों को जीतता है , और हम |H|
बहुत बड़े हो सकते हैं ।
इस परिणाम का विरोध पिछले एल्गोरिथम से करें, जहां विरोधी ने हमेशा चुनौती जीती थी। यहां थोड़ा हाथ लगाना, लेकिन चूंकि अधिकांश समय विपक्षी विफल हो जाएगा, और यह उन सभी संभावित रणनीतियों के लिए सच है जो विरोधी कोशिश कर सकते हैं, यह इस प्रकार है कि हालांकि सबसे खराब मामला है O(n)
, वास्तव में सबसे खराब स्थिति है O(1)
।
फिर, यह एक औपचारिक प्रमाण नहीं है। इस सबसे खराब स्थिति विश्लेषण से हमें जो गारंटी मिलती है, वह यह है कि हमारा रन टाइम अब किसी विशेष इनपुट से स्वतंत्र है । यह वास्तव में एक यादृच्छिक गारंटी है, औसत मामले के विश्लेषण के विपरीत जहां हमने दिखाया कि एक प्रेरित विरोधी आसानी से खराब इनपुट को शिल्प कर सकता है।