हैश टेबल कैसे काम करता है?


494

मैं एक स्पष्टीकरण की तलाश कर रहा हूँ कि एक हैश टेबल कैसे काम करता है - मेरे जैसे एक साधारण व्यक्ति के लिए सादे अंग्रेजी में!

उदाहरण के लिए, मुझे पता है कि यह कुंजी लेता है, हैश की गणना करता है (मैं एक स्पष्टीकरण की तलाश कर रहा हूं कि कैसे) और फिर काम करने के लिए कुछ प्रकार के मोडुलो का प्रदर्शन करता है जहां यह सरणी में निहित है जहां मूल्य संग्रहीत किया जाता है, लेकिन यह वह जगह है जहां मेरा ज्ञान रुक जाता है ।

क्या कोई इस प्रक्रिया को स्पष्ट कर सकता है?

संपादित करें: मैं विशेष रूप से यह नहीं पूछ रहा कि हैश कोड की गणना कैसे की जाती है, लेकिन हैश तालिका कैसे काम करती है, इसका सामान्य अवलोकन।


4
हाल ही में, मैंने यह ( en.algoritmy.net/article/50101/Hash-table ) लेख में हैश टेबल्स और उनकी रणनीतियों पर उच्चारण के साथ डेटा को स्टोर और लुकअप करने के लिए कई तरीकों का वर्णन किया है (अलग-अलग चेनिंग, रैखिक रेखांकन, डबल हैशिंग) )
पुरुषजपौक २

1
आप किसी सरणी के विस्तारित संस्करण के रूप में एक हैश तालिका के बारे में सोच सकते हैं, यह सिर्फ लगातार पूर्णांक कुंजी तक सीमित नहीं है।
user253751

1
: यहाँ एक और एक है intelligentjava.wordpress.com/2016/10/19/...
Nesvarbu

जवाबों:


913

यहां आम आदमी की शर्तों में एक स्पष्टीकरण दिया गया है।

मान लेते हैं कि आप पुस्तकों के साथ एक पुस्तकालय को भरना चाहते हैं और न केवल उन्हें वहां सामान देना चाहते हैं, बल्कि आप उन्हें आसानी से फिर से खोजने में सक्षम होना चाहते हैं जब आपको उनकी आवश्यकता होती है।

इसलिए, आप यह तय करते हैं कि यदि वह व्यक्ति जो किताब पढ़ना चाहता है, वह पुस्तक का शीर्षक और बूट करने के लिए सटीक शीर्षक जानता है, तो आपको यह सब करना चाहिए। शीर्षक के साथ, लाइब्रेरियन की सहायता से, व्यक्ति को आसानी से और जल्दी से पुस्तक खोजने में सक्षम होना चाहिए।

तो, आप ऐसा कैसे कर सकते हैं? ठीक है, स्पष्ट रूप से आप प्रत्येक पुस्तक को रखने के लिए किसी प्रकार की सूची रख सकते हैं, लेकिन फिर आपको लाइब्रेरी खोजने में भी यही समस्या है, आपको सूची को खोजने की आवश्यकता है। दी गई, सूची छोटी और खोज करने में आसान होगी, लेकिन फिर भी आप लाइब्रेरी के एक छोर से दूसरे (या सूची) में क्रमिक रूप से खोज नहीं करना चाहते हैं।

आप कुछ ऐसा चाहते हैं, जो पुस्तक के शीर्षक के साथ, आपको एक ही बार में सही स्थान दे सके, इसलिए आपको बस सही शेल्फ पर टहलना है, और पुस्तक को चुनना है।

लेकिन यह कैसे किया जा सकता है? जब आप पुस्तकालय भरते हैं तो बहुत सारे पूर्वाभास के साथ, जब आप पुस्तकालय भरते हैं तो बहुत काम करते हैं।

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

इस कार्यक्रम की सुंदरता यह है कि बाद में, जब कोई व्यक्ति पुस्तक को पढ़ने के लिए वापस आता है, तो आप कार्यक्रम के माध्यम से शीर्षक को एक बार फिर से खिलाते हैं, और उसी शेल्फ नंबर और स्लॉट संख्या को वापस प्राप्त करते हैं जो आपको मूल रूप से दिया गया था, और यह है जहां किताब स्थित है।

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

सादगी के लिए, मान लें कि यह प्रत्येक अक्षर और प्रतीक को एक संख्या में परिवर्तित करता है और उन सभी को पूरा करता है। वास्तव में, यह उससे कहीं अधिक जटिल है, लेकिन चलो इसे अभी के लिए छोड़ दें।

इस तरह के एक एल्गोरिथ्म की सुंदरता यह है कि यदि आप एक ही इनपुट को बार-बार इसमें फीड करते हैं, तो यह हर बार एक ही नंबर को थूकता रहेगा।

ठीक है, तो यह मूल रूप से एक हैश तालिका कैसे काम करती है।

तकनीकी सामान इस प्रकार है।

सबसे पहले, संख्या का आकार है। आमतौर पर, इस तरह के हैश एल्गोरिथ्म का उत्पादन कुछ बड़ी संख्या की सीमा के अंदर होता है, जो आमतौर पर आपके टेबल में मौजूद स्पेस से बहुत बड़ा होता है। उदाहरण के लिए, मान लें कि हमारे पास पुस्तकालय में ठीक एक मिलियन पुस्तकों के लिए जगह है। हैश गणना का उत्पादन 0 से एक बिलियन की सीमा में हो सकता है जो बहुत अधिक है।

तो हम क्या करे? हम मापांक गणना नामक कुछ का उपयोग करते हैं, जो मूल रूप से कहता है कि यदि आप उस संख्या की गणना करते हैं जो आप चाहते थे (यानी एक अरब संख्या) लेकिन बहुत छोटी सीमा के अंदर रहना चाहते थे, तो हर बार जब आप उस छोटी सीमा की सीमा पर पहुंचते हैं तो आप वापस शुरू हो जाते हैं 0, लेकिन आपको इस बात का ध्यान रखना होगा कि आप कितने बड़े अनुक्रम में आए हैं।

कहें कि हैश एल्गोरिथम का उत्पादन 0 से 20 की सीमा में है और आपको किसी विशेष शीर्षक से मान 17 मिलता है। यदि लाइब्रेरी का आकार केवल 7 पुस्तकें हैं, तो आप 1, 2, 3, 4, 5, 6, और जब आप 7 में मिलते हैं, तो आप 0. पर वापस शुरू करते हैं। चूंकि हमें 17 बार गिनने की आवश्यकता है, हमारे पास 1 है, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, और अंतिम संख्या 3 है।

बेशक मापांक गणना उस तरह से नहीं की जाती है, यह विभाजन और शेष के साथ की जाती है। 17 को 7 से विभाजित करने का शेष 3 है (7 2 बार 17 में 14 पर जाता है और 17 और 14 के बीच का अंतर 3 है)।

इस प्रकार, आपने पुस्तक को स्लॉट संख्या 3 में रखा।

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

विभिन्न टकराव से निपटने के तरीके मौजूद हैं, जिसमें तालिका में एक और स्थान ( डबल हैशिंग ) प्राप्त करने के लिए डेटा को अभी तक चलाने के लिए , या बस आपके द्वारा दिए गए स्थान के करीब एक स्थान खोजने के लिए (यानी पिछली पुस्तक के ठीक बगल में स्लॉट है। यह भी रैखिक जांच के रूप में जाना जाता था )। इसका मतलब यह होगा कि जब आप बाद में किताब खोजने की कोशिश करेंगे तो आपके पास कुछ खुदाई होगी, लेकिन यह अभी भी लाइब्रेरी के एक छोर पर शुरू होने से बेहतर है।

अंत में, कुछ बिंदु पर, आप लाइब्रेरी में लाइब्रेरी की अनुमति से अधिक किताबें रख सकते हैं। दूसरे शब्दों में, आपको एक बड़ा पुस्तकालय बनाने की आवश्यकता है। चूंकि लाइब्रेरी में सटीक स्थान की गणना लाइब्रेरी के सटीक और वर्तमान आकार का उपयोग करके की गई थी, इसलिए इसका अनुसरण किया जाता है कि यदि आप लाइब्रेरी का आकार बदल लेते हैं, तो हो सकता है कि आप सभी पुस्तकों के लिए नए स्पॉट खोजने के लिए समाप्त हो जाएं, क्योंकि गणना उनके स्पॉट खोजने के लिए की गई थी। बदल गया है।

मुझे आशा है कि यह स्पष्टीकरण बाल्टी और कार्यों की तुलना में पृथ्वी से थोड़ा अधिक नीचे था :)


इतनी बढ़िया व्याख्या के लिए धन्यवाद। क्या आप जानते हैं कि मैं 4.x .Net फ्रेमवर्क में इसे कैसे लागू किया जाता है, इसके बारे में अधिक तकनीकी जानकारी प्राप्त कर सकता हूं।
जॉनी_डी

नहीं, यह सिर्फ एक संख्या है। आप बस प्रत्येक शेल्फ और स्लॉट को 0 या 1 से शुरू कर रहे हैं और उस शेल्फ पर प्रत्येक स्लॉट के लिए 1 बढ़ा रहे हैं, फिर अगले शेल्फ पर नंबर जारी रखें।
लास वी। कार्लसन

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

1
@ काइलडेलनै: बंद हैशिंग के लिए नहीं (जहां वैकल्पिक बाल्टी खोजने से टकराव होते हैं, जिसका मतलब है कि मेमोरी का उपयोग तय है लेकिन आप बाल्टी में खोज करने में अधिक समय बिताते हैं)। के लिए चेनिंग उर्फ खुला हैशिंग एक रोग मामले में (भयानक हैश फंक्शन या आदानों जानबूझ कर कुछ विरोधी / हैकर द्वारा टकराने से गढ़ी) आप के साथ सबसे अधिक हैश बाल्टी खाली लग सकती है, लेकिन कुल स्मृति उपयोग बदतर नहीं है - बस में अधिक सुझावों के बजाय नल उपयोगी डेटा में अनुक्रमण।
टोनी डेलरो

3
@KyleDelaney: आपकी टिप्पणियों की सूचना पाने के लिए "@Tony" की आवश्यकता है। लगता है कि आप जंजीर के बारे में सोच रहे हैं: कहते हैं कि हमारे पास तीन मूल्य नोड हैं A{ptrA, valueA}, B{ptrB, valueB}, C{ptrC, valueC}, और तीन बाल्टी के साथ एक हैश तालिका है [ptr1, ptr2, ptr3]। चाहे सम्मिलित करते समय टकराव हो, स्मृति उपयोग तय है। आपके पास कोई टक्कर नहीं हो सकती है: A{NULL, valueA} B{NULL, valueB} C{NULL, valueC}और [&A, &B, &C], या सभी टकराव A{&B, valueA} B{&C, valueB}, C{NULL, valueC}और [NULL, &A, NULL]: NULL बाल्टियाँ "बर्बाद" हैं? किंदा, थोथा नहीं। एक ही कुल मेमोरी का उपयोग किया।
टोनी डेलरो

104

उपयोग और लिंगो:

  1. हैश टेबल का उपयोग डेटा (या रिकॉर्ड) को जल्दी से स्टोर करने और पुनः प्राप्त करने के लिए किया जाता है।
  2. हैश कीज़ का उपयोग करके रिकॉर्ड को बाल्टी में संग्रहित किया जाता है
  3. हैश कीज़ की गणना रिकॉर्ड के भीतर निहित मूल्य ( कुंजी मान) के लिए हैशिंग एल्गोरिथ्म लागू करके की जाती है । यह चुना हुआ मूल्य सभी रिकॉर्ड के लिए एक सामान्य मूल्य होना चाहिए।
  4. प्रत्येक बाल्टी में कई रिकॉर्ड हो सकते हैं जो एक विशेष क्रम में व्यवस्थित होते हैं।

वास्तविक विश्व उदाहरण:

हैश एंड कंपनी , 1803 में स्थापित और किसी भी कंप्यूटर प्रौद्योगिकी की कमी के लिए अपने लगभग 30,000 ग्राहकों के लिए विस्तृत जानकारी (रिकॉर्ड) रखने के लिए कुल 300 फाइलिंग कैबिनेट थे। प्रत्येक फ़ाइल फ़ोल्डर को स्पष्ट रूप से उसके क्लाइंट नंबर, 0 से 29,999 तक एक अद्वितीय संख्या के साथ पहचाना गया था।

उस समय के फाइल क्लर्कों को काम करने वाले कर्मचारियों के लिए क्लाइंट रिकॉर्ड जल्दी से लाने और स्टोर करने की आवश्यकता थी। कर्मचारियों ने तय किया था कि अपने रिकॉर्ड को संग्रहीत करने और पुनः प्राप्त करने के लिए एक हैशिंग पद्धति का उपयोग करना अधिक कुशल होगा।

क्लाइंट रिकॉर्ड दर्ज करने के लिए, क्लर्क फाइलिंग फ़ोल्डर पर लिखे गए अद्वितीय क्लाइंट नंबर का उपयोग करेगा। इस ग्राहक संख्या का उपयोग करते हुए, वे फाइलिंग कैबिनेट को पहचानने के लिए हैश कुंजी को 300 से व्यवस्थित करेंगे, जब वे फाइलिंग कैबिनेट को खोलते हैं तो उन्हें पता चलता है कि इसमें क्लाइंट संख्या द्वारा आदेशित कई फ़ोल्डर्स समाहित हैं। सही स्थान की पहचान करने के बाद, वे बस इसे अंदर खिसका देंगे।

एक ग्राहक रिकॉर्ड को पुनः प्राप्त करने के लिए, क्लर्कों को दाखिल करना कागज की एक पर्ची पर एक क्लाइंट नंबर दिया जाएगा। इस विशिष्ट ग्राहक संख्या ( हैश कुंजी ) का उपयोग करते हुए , वे यह निर्धारित करने के लिए इसे 300 से संशोधित करेंगे कि किस फाइलिंग कैबिनेट में क्लाइंट फ़ोल्डर था। जब उन्होंने फाइलिंग कैबिनेट खोला, तो उन्हें पता चला कि इसमें क्लाइंट नंबर द्वारा ऑर्डर किए गए कई फ़ोल्डर्स हैं। रिकॉर्ड के माध्यम से खोज करने पर वे जल्दी से क्लाइंट फ़ोल्डर को ढूंढ लेंगे और इसे पुनः प्राप्त करेंगे।

हमारे वास्तविक दुनिया उदाहरण में, हमारे बाल्टी हैं फाइलिंग कैबिनेट और हमारे रिकॉर्ड कर रहे हैं फ़ाइल फ़ोल्डरों


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

जैसा कि साइमन ने उल्लेख किया है कि मेरा मानना है कि यह बहुत महत्वपूर्ण है कि हैशिंग हिस्सा एक बड़े स्थान को बदलना है (मनमाने ढंग से लंबाई, आमतौर पर तार, आदि) और इसे एक छोटे स्थान (ज्ञात आकार का, आमतौर पर संख्याओं) के लिए मैप करना है। यह याद रखना बहुत महत्वपूर्ण है!

तो ऊपर के उदाहरण में, 30,000 संभावित ग्राहक या तो एक छोटी सी जगह पर मैप किए जाते हैं।


इसमें मुख्य विचार यह है कि अपने पूरे डेटा को सेगमेंट में विभाजित करें ताकि वास्तविक खोज को गति मिल सके जो आमतौर पर समय लेने वाली होती है। ऊपर हमारे उदाहरण में, 300 फाइलिंग कैबिनेट में से प्रत्येक में (सांख्यिकीय रूप से) लगभग 100 रिकॉर्ड होंगे। 100 रिकॉर्ड के माध्यम से खोज करना (आदेश की परवाह किए बिना) 30,000 से निपटने की तुलना में बहुत तेज है।

आपने देखा होगा कि कुछ वास्तव में पहले से ही ऐसा करते हैं। लेकिन एक हैश कुंजी उत्पन्न करने के लिए एक हैशिंग पद्धति को तैयार करने के बजाय, वे ज्यादातर मामलों में अंतिम नाम के पहले अक्षर का उपयोग करेंगे। इसलिए यदि आपके पास 26 फाइलिंग कैबिनेट्स हैं जिनमें से प्रत्येक में A से Z तक एक अक्षर है, तो आपने सिद्धांत रूप से आपके डेटा को खंडित किया है और फाइलिंग और पुनर्प्राप्ति प्रक्रिया को बढ़ाया है।

उम्मीद है की यह मदद करेगा,

Jeach!


2
आप एक विशिष्ट प्रकार की हैश टेबल टक्कर परिहार रणनीति का वर्णन करते हैं, जिसे "ओपन एड्रेसिंग" या "बंद एड्रेसिंग" (हाँ, दुखद लेकिन सच) या "चैनिंग" कहा जाता है। एक और प्रकार है जो सूची बाल्टियों का उपयोग नहीं करता है, बल्कि आइटम "इनलाइन" को संग्रहीत करता है।
कोनराड रुडोल्फ

2
उत्कृष्ट विवरण। प्रत्येक फाइलिंग कैबिनेट को छोड़कर, औसतन, 100रिकॉर्ड्स (30k रिकॉर्ड्स / 300 अलमारियाँ = 100) के बारे में होगा। एक संपादन के लायक हो सकता है।
रयान टक

@TonyD, इस साइट को sha-1 ऑनलाइन पर जाएँ और इसके लिए SHA-1 हैश उत्पन्न करें TonyDजो आप पाठ क्षेत्र में टाइप करते हैं। आप किसी चीज़ के उत्पन्न मूल्य के साथ समाप्त होंगे जो दिखता है e5dc41578f88877b333c8b31634cf77e4911ed8c। यह 160-बिट्स (20-बाइट्स) की एक बड़ी हेक्साडेसिमल संख्या से अधिक कुछ नहीं है। फिर आप इसका उपयोग यह निर्धारित करने के लिए कर सकते हैं कि आपके रिकॉर्ड को संग्रहीत करने के लिए कौन सी बाल्टी (एक सीमित मात्रा में) का उपयोग किया जाएगा।
Jeach

@TonyD, मुझे यकीन नहीं है कि "हैश की" शब्द को परस्पर विरोधी मामले में कहा जाता है? यदि हां, तो कृपया दो या अधिक स्थानों को इंगित करें। या आप यह कह रहे हैं कि "हम" शब्द का उपयोग "हैश की" करते हैं जबकि अन्य साइट्स जैसे कि विकिपीडिया "हैश मान, हैश कोड, हैश सोम्स, या बस हैश" का उपयोग करता है? यदि ऐसा है, तो जब तक इस्तेमाल किया जाने वाला शब्द किसी समूह या संगठन के अनुरूप होता है। प्रोग्रामर अक्सर "कुंजी" शब्द का उपयोग करते हैं। मैं व्यक्तिगत रूप से तर्क दूंगा कि एक और अच्छा विकल्प "हैश वैल्यू" होगा। लेकिन मैं "हैश कोड, हैश राशि या बस हैश" का उपयोग कर बाहर शासन करेगा। एल्गोरिथ्म पर ध्यान दें और शब्दों पर नहीं!
21

2
@TonyD, मैंने टेक्स्ट को " 300 के द्वारा हैश कुंजी मॉड्यूल करेगा" को बदल दिया है , उम्मीद है कि यह सभी के लिए क्लीनर और स्पष्ट होगा। धन्यवाद!
Jeach

64

यह सिद्धांत का एक बहुत गहरा क्षेत्र है, लेकिन मूल रूपरेखा सरल है।

अनिवार्य रूप से, एक हैश फ़ंक्शन केवल एक फ़ंक्शन है जो चीजों को एक स्थान से लेता है (मनमाना लंबाई के तार) और उन्हें अनुक्रमण (अहस्ताक्षरित पूर्णांक, कहते हैं) के लिए उपयोगी स्थान पर मैप करता है।

यदि आपके पास हैश के लिए केवल एक छोटी सी जगह है, तो आप उन चीजों को पूर्णांक के रूप में व्याख्या करने के साथ दूर हो सकते हैं, और आप कर रहे हैं (जैसे 4 बाइट स्ट्रिंग्स)

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

तात्पर्य यह है कि आप चाहते हैं कि इसका परिणाम समान न हो, और आप शायद यह भी चाहते हैं कि हैश फ़ंक्शन तेजी से हो।

इन दो गुणों (और कुछ अन्य) को संतुलित करते हुए कई लोगों को व्यस्त रखा गया है!

व्यवहार में आपको आमतौर पर एक ऐसा फ़ंक्शन ढूंढने में सक्षम होना चाहिए जो आपके एप्लिकेशन के लिए अच्छी तरह से काम करने के लिए जाना जाता है और जिसका उपयोग करें।

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

तो जैसा कि आप लंबे समय तक चलते हैं, आपके हैशटेबल (सरणी) में प्रत्येक प्रविष्टि या तो खाली है, या एक प्रविष्टि, या प्रविष्टियों की एक सूची है। पुनः प्राप्त करना सरणी में अनुक्रमण के रूप में एक सरल है, और या तो मान लौटाता है, या मूल्यों की सूची चलना और सही वापस लौटना।

बेशक आप आमतौर पर ऐसा नहीं कर सकते, यह बहुत अधिक स्मृति बर्बाद करता है। तो आप एक विरल सरणी के आधार पर सब कुछ करते हैं (जहां केवल प्रविष्टियां वही हैं जो आप वास्तव में उपयोग करते हैं, बाकी सब स्पष्ट रूप से शून्य है)।

इस काम को बेहतर बनाने के लिए बहुत सारी योजनाएँ और तरकीबें हैं, लेकिन यह मूल बातें हैं।


1
क्षमा करें, मुझे पता है कि यह एक पुराना प्रश्न / उत्तर है, लेकिन मैं इस अंतिम बिंदु को समझने की कोशिश कर रहा हूं। एक हैश तालिका में O (1) समय जटिलता है। हालांकि, एक बार जब आप एक विरल सरणी का उपयोग करते हैं, तो क्या आपको अपने मूल्य को खोजने के लिए एक द्विआधारी खोज करने की आवश्यकता नहीं है? उस समय क्या जटिलता ओ (लॉग एन) नहीं बन जाती है?
हर्बर्न्सन

@herbrandson: नहीं ... एक विरल सरणी का अर्थ है कि अपेक्षाकृत कुछ सूचकांकों को मूल्यों के साथ आबाद किया गया है - आप अभी भी अपनी कुंजी से गणना की गई हैश मान के लिए विशिष्ट सरणी तत्व पर सीधे अनुक्रमण कर सकते हैं; अभी भी, विरल सरणी कार्यान्वयन साइमन का वर्णन केवल बहुत ही सीमित परिस्थितियों में समझदार है: जब बाल्टी आकार स्मृति पृष्ठ आकार के क्रम के होते हैं (बनाम int1-1000 1000 की चाबियाँ और 4k पृष्ठ = अधिकांश पृष्ठ स्पर्श किए गए), और कब ओएस सभी -० पृष्ठों को कुशलतापूर्वक व्यवहार करता है (इसलिए सभी-अप्रयुक्त-बाल्टी पृष्ठों को बैकिंग मेमोरी की आवश्यकता नहीं होती है), जब पता स्थान बहुतायत से होता है ....
टोनी डेलरो

@TonyDelroy - यह सच है कि यह ओवरसिम्प्लीफिकेशन है, लेकिन विचार यह था कि वे क्या हैं और क्यों नहीं, एक व्यावहारिक कार्यान्वयन है। उत्तरार्द्ध का विवरण अधिक बारीक है, क्योंकि आप अपने विस्तार में सिर हिलाते हैं।
सिमोन

48

उत्तर के बहुत सारे, लेकिन उनमें से कोई भी बहुत दृश्य नहीं है , और हैश तालिकाओं को कल्पना करने पर आसानी से "क्लिक" कर सकते हैं।

हैश टेबल को अक्सर लिंक्ड लिस्ट के सरणियों के रूप में लागू किया जाता है। यदि हम लोगों के नामों को संग्रहीत करने वाली एक तालिका की कल्पना करते हैं, तो कुछ सम्मिलन के बाद इसे नीचे के रूप में स्मृति में रखा जा सकता है, जहां- ()संलग्न संख्या पाठ / नाम के हैश मान हैं।

bucket#  bucket content / linked list

[0]      --> "sue"(780) --> null
[1]      null
[2]      --> "fred"(42) --> "bill"(9282) --> "jane"(42) --> null
[3]      --> "mary"(73) --> null
[4]      null
[5]      --> "masayuki"(75) --> "sarwar"(105) --> null
[6]      --> "margaret"(2626) --> null
[7]      null
[8]      --> "bob"(308) --> null
[9]      null

कुछ बिंदु:

  • सरणी प्रविष्टियों (सूचकांकों [0], [1]...) में से प्रत्येक को एक बाल्टी के रूप में जाना जाता है , और इस उदाहरण में (उर्फ तत्वों , मूल्यों की उक्त - संभवतः रिक्त सूची ) को शुरू करता है - लोगों के नाम )
  • प्रत्येक मूल्य (जैसे "fred"हैश के साथ 42) बाल्टी से जुड़ा हुआ है [hash % number_of_buckets]जैसे 42 % 10 == [2]; %है सापेक्ष ऑपरेटर शेष जब बाल्टी की संख्या से विभाजित -
  • कई डेटा मान टकरा सकते हैं और एक ही बाल्टी से लिंक किए जा सकते हैं , सबसे अधिक बार क्योंकि उनके हैश मान modulo ऑपरेशन (जैसे 42 % 10 == [2], और 9282 % 10 == [2]) के बाद टकराते हैं , लेकिन कभी-कभी क्योंकि हैश मान समान होते हैं (जैसे "fred"और "jane"दोनों को हैश से 42ऊपर दिखाया गया है)
    • अधिकांश हैश टेबल टकराव को संभालते हैं - थोड़े कम प्रदर्शन के साथ लेकिन कोई कार्यात्मक भ्रम नहीं है - हैशेड-बाल्टी में लिंक की गई सूची में पहले से ही प्रत्येक मूल्य के लिए मांगे जा रहे मूल्य के पूर्ण मूल्य (यहां पाठ) की तुलना करके।

लिंक्ड सूची की लंबाई लोड कारक से संबंधित है, मूल्यों की संख्या नहीं

यदि तालिका का आकार बढ़ता है, तो ऊपर दी गई हैश तालिकाएँ अपने आप को आकार देने के लिए प्रवृत्त होती हैं (यानी एक बड़ी सारणी बनाने के लिए, वहाँ से नई / अपडेट की गई सूचियाँ बनाएँ, पुरानी सरणी को हटा दें) बाल्टी के मानों का अनुपात बनाए रखने के लिए (उर्फ लोड) कारक ) 0.5 से 1.0 सीमा में कहीं।

हंस नीचे एक टिप्पणी में अन्य लोड कारकों के लिए वास्तविक सूत्र देता है, लेकिन सांकेतिक मूल्यों के लिए: लोड कारक 1 और एक क्रिप्टोग्राफिक ताकत हैश फ़ंक्शन के साथ, 1 / ई (~ 36.8%) बाल्टियां खाली हो जाएंगी, एक और 1 / ई (~ 36.8%) में एक तत्व है, 1 / (2e) या ~ 18.4% दो तत्व, 1 / (3! E) लगभग 6.1% तीन तत्व, 1 / (4! E) या ~ 1.5% चार तत्व, 1 /! (5! E) ~ .3% में पाँच आदि होते हैं .. - गैर-खाली बाल्टियों से औसत श्रृंखला की लंबाई ~ 1.58 है चाहे कितनी भी सारिणी हो (अर्थात 100 तत्व और 100 बाल्टियाँ हों या 100 मिलियन) तत्व और 100 मिलियन बकेट), जिसके कारण हम कहते हैं कि लुकअप / इंसर्ट / इरेज़ (1) निरंतर समय ऑपरेशन हैं।

कैसे एक हैश तालिका मूल्यों को कुंजी के साथ जोड़ सकती है

ऊपर वर्णित के रूप में एक हैश तालिका कार्यान्वयन को देखते हुए, हम एक मान प्रकार बनाने की कल्पना कर सकते हैं struct Value { string name; int age; };, और समानता तुलना और हैश फ़ंक्शन जो केवल nameफ़ील्ड को देखते हैं (उम्र की अनदेखी कर रहे हैं), और फिर कुछ अद्भुत होता है: हम तालिका में Valueरिकॉर्ड की तरह स्टोर कर सकते हैं {"sue", 63}, फिर बाद में उसकी आयु ज्ञात किए बिना "मुकदमा" खोजें, संग्रहीत मूल्य ढूंढें और पुनर्प्राप्त करें या उसकी आयु भी अपडेट करें
- जन्मदिन मुबारक हो मुकदमा - जो दिलचस्प रूप से हैश मान को परिवर्तित नहीं करता है, इसलिए हमें दूसरे पर मुकदमा दर्ज करने की आवश्यकता नहीं है बाल्टी।

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

यह इस उदाहरण से पहले के विपरीत है, जहां हमने "sue" जैसे असतत मूल्यों को संग्रहीत किया है, जिसे आप अपनी कुंजी के रूप में सोच सकते हैं: इस तरह के उपयोग को हैश सेट के रूप में जाना जाता है ।

हैश टेबल को लागू करने के अन्य तरीके हैं

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


हैश फ़ंक्शन पर कुछ शब्द

मजबूत हैशिंग ...

एक सामान्य उद्देश्य, सबसे खराब स्थिति टक्कर-कम से कम हैश फ़ंक्शन का काम हैश टेबल बाल्टी के चारों ओर कीज़ को प्रभावी ढंग से यादृच्छिक रूप से स्प्रे करना है, जबकि हमेशा एक ही कुंजी के लिए समान हैश मान उत्पन्न होता है। यहां तक ​​कि कुंजी में कहीं भी एक बिट बदलने से आदर्श रूप से - बेतरतीब ढंग से - परिणामी हैश मान में लगभग आधे बिट्स को फ्लिप करें।

यह आम तौर पर गणित के लिए बहुत ही ऑर्केस्ट्रेटेड है जो मेरे लिए बहुत जटिल है। मैं एक आसान तरीके से उल्लेख करूंगा - सबसे अधिक स्केलेबल या कैश फ्रेंडली नहीं, बल्कि स्वाभाविक रूप से सुरुचिपूर्ण (जैसे एक समय पैड के साथ एन्क्रिप्शन!) - जैसा कि मुझे लगता है कि यह घर को ऊपर वर्णित वांछनीय गुणों को चलाने में मदद करता है। कहते हैं कि आप 64-बिट doubleएस हैशिंग थे - आप 256 यादृच्छिक संख्याओं (नीचे कोड) में से प्रत्येक में 8 टेबल बना सकते हैं, फिर प्रत्येक 8-बिट / 1-बाइट स्लाइस का उपयोग doubleएक अलग तालिका में सूचकांक करने के लिए एक अलग तालिका में, XORing यादृच्छिक संख्या जो आप देखते हैं। इस दृष्टिकोण के साथ, यह देखना आसान है कि एक doubleअलग यादृच्छिक संख्या में परिणामों में कहीं भी (द्विआधारी अंक अर्थ में) एक टेबल में से एक में देखा जा रहा है, और एक पूरी तरह से असंबद्ध अंतिम मूल्य।

// note caveats above: cache unfriendly (SLOW) but strong hashing...
size_t random[8][256] = { ...random data... };
const char* p = (const char*)&my_double;
size_t hash = random[0][p[0]] ^ random[1][p[1]] ^ ... ^ random[7][p[7]];

कमज़ोर लेकिन तेज़-तेज़ हैशिंग ...

कई पुस्तकालयों के हैशिंग फ़ंक्शन अपरिवर्तित (एक तुच्छ या पहचान हैश फ़ंक्शन के रूप में जाना जाता है) के माध्यम से पूर्णांक पास करते हैं ; यह ऊपर वर्णित मजबूत हैशिंग से अन्य चरम है। एक पहचान हैश बेहद हैसबसे खराब मामलों में टकराव की संभावना है, लेकिन उम्मीद है कि पूर्णांक कुंजियों के काफी सामान्य मामले में जो वृद्धि (शायद कुछ अंतराल के साथ) हो, वे क्रमिक बाल्टी में मैप करेंगे जो यादृच्छिक हैशिंग पत्तियों (हमारे ~ 36.8) से कम खाली हैं लोड फैक्टर 1 पर% पहले उल्लेख किया गया है, जिससे कम टकराने वाले तत्वों की तुलना में कम टकराव और लंबे समय तक जुड़ी हुई सूचियाँ यादृच्छिक मैपिंग द्वारा हासिल की गई हैं। एक मजबूत हैश उत्पन्न करने में लगने वाले समय को बचाने के लिए भी यह बहुत अच्छा है, और यदि कुंजी को क्रम में देखा जाता है तो वे पास में बाल्टी में मिल जाएंगे, कैश हिट में सुधार होगा। जब चाबियां अच्छी तरह से नहीं बढ़ती हैं, तो आशा है कि वे यादृच्छिक रूप से पर्याप्त होंगी और उन्हें बाल्टी में अपने प्लेसमेंट को पूरी तरह से यादृच्छिक करने के लिए एक मजबूत हैश फ़ंक्शन की आवश्यकता नहीं होगी।


6
मुझे केवल कहने की अनुमति दें: शानदार उत्तर।
CRThaze

@ टोनी डेलरो अद्भुत जवाब के लिए धन्यवाद। मेरे मन में अभी भी एक खुला बिंदु है। आप कहते हैं कि भले ही 100 मिलियन बकेट हों, लुकअप टाइम O (1) होगा जिसमें लोड फैक्टर 1 होगा और क्रिप्टोग्राफिक स्ट्रेंथ हैपर फंक्शन होगा। लेकिन 100 मिलियन में सही बाल्टी खोजने के बारे में क्या? यहां तक ​​कि अगर हमारे पास सभी बाल्टियाँ हैं, तो क्या यह O (log100.000.000) नहीं है? बाल्टी ओ (1) कैसे हो सकती है?
सेल्मैन

@ सेल्समैन: आपका प्रश्न यह बताने के लिए कई विवरण प्रदान नहीं करता है कि आपको क्यों लगता है कि यह O (log100,000,000) हो सकता है, लेकिन आप कहते हैं "भले ही हमारे पास सभी बाल्टी सॉर्ट हो" - ध्यान रखें कि हैश टेबल बकेट में मान कर रहे हैं कभी नहीं जो मूल्य में प्रकट होता है, जिसमें बाल्टी कुंजी को हैश फंक्शन लगाने से निर्धारित होता है: सामान्य अर्थ में "हल कर"। यह सोचकर कि हे (log100,000,000) का तात्पर्य है कि आप छंटनी की गई बाल्टियों के माध्यम से एक द्विआधारी खोज करने की कल्पना करते हैं, लेकिन ऐसा नहीं है कि हैशिंग कैसे काम करती है। हो सकता है कि कुछ अन्य उत्तर पढ़ें और देखें कि क्या यह अधिक समझ में आने लगता है।
टोनी डेलारॉय

@TonyDelroy वास्तव में, "सॉर्ट की गई बाल्टियाँ" सबसे अच्छी स्थिति हैं जो मैं कल्पना करता हूं। इसलिए हे (log100,000,000)। लेकिन अगर यह मामला नहीं है, तो आवेदन लाखों लोगों के बीच संबंधित बाल्टी कैसे खोज सकता है? क्या हैश फ़ंक्शन किसी तरह मेमोरी लोकेशन उत्पन्न करता है?
सेल्मैन

1
@selman: क्योंकि कंप्यूटर मेमोरी लगातार समय "यादृच्छिक अभिगम" की अनुमति देती है: यदि आप एक मेमोरी एड्रेस की गणना कर सकते हैं, तो आप ऐरे के अन्य हिस्सों में मेमोरी एक्सेस किए बिना मेमोरी कंटेंट को पुनः प्राप्त कर सकते हैं। इसलिए, चाहे आप पहली बाल्टी, आखिरी बाल्टी, या बीच में कहीं भी एक बाल्टी का उपयोग करते हैं, इसमें समान प्रदर्शन विशेषताएँ होंगी (शिथिल रूप से, सीपीयू L1 / L2 / L3 / कैशिंग प्रभाव के अधीन, समय की समान मात्रा लें) वे केवल आपको हाल ही में एक्सेस की गई या संयोगवश पास की बाल्टियों को फिर से उपयोग करने में मदद करने के लिए काम करते हैं, और बड़े-ओ विश्लेषण के लिए नजरअंदाज किया जा सकता है)।
टोनी डेलारॉय

24

आप लोग इसे पूरी तरह से समझाने के बहुत करीब हैं, लेकिन एक दो बातें याद आ रही हैं। हैशटेबल सिर्फ एक सरणी है। प्रत्येक स्लॉट में सरणी में ही कुछ होगा। कम से कम आप इस स्लॉट में हैशवल्यू या मूल्य को संग्रहीत करेंगे। इसके अतिरिक्त आप इस स्लॉट पर टकराए गए मानों की लिंक / जंजीर सूची को भी संग्रहीत कर सकते हैं, या आप खुले पते की विधि का उपयोग कर सकते हैं। आप किसी अन्य डेटा को पॉइंटर या पॉइंटर्स भी स्टोर कर सकते हैं जिसे आप इस स्लॉट से बाहर निकालना चाहते हैं।

यह ध्यान रखना महत्वपूर्ण है कि हैशवेल्यू आमतौर पर उस स्लॉट को इंगित नहीं करता है जिसमें मूल्य रखा जाना है। उदाहरण के लिए, हैशवेल एक नकारात्मक पूर्णांक मान हो सकता है। स्पष्ट रूप से एक ऋणात्मक संख्या एक सरणी स्थान को इंगित नहीं कर सकती है। इसके अतिरिक्त, हैश मान उपलब्ध स्लॉट्स की तुलना में कई गुना बड़ा होगा। इस प्रकार एक और गणना के लिए हैशटेबल द्वारा यह पता लगाने की जरूरत है कि मूल्य किस स्लॉट में जाना चाहिए। यह एक मापांक गणित के संचालन के साथ किया जाता है जैसे:

uint slotIndex = hashValue % hashTableSize;

यह मान वह स्लॉट है जिसमें मूल्य जाएगा। खुले संबोधन में, यदि स्लॉट पहले से ही किसी अन्य हैशवल्यू और / या अन्य डेटा से भरा है, तो अगले स्लॉट को खोजने के लिए एक बार फिर से मापांक ऑपरेशन चलाया जाएगा:

slotIndex = (remainder + 1) % hashTableSize;

मुझे लगता है कि स्लॉट इंडेक्स का निर्धारण करने के लिए अन्य अधिक उन्नत तरीके हो सकते हैं, लेकिन यह आम मैंने देखा है ... किसी भी अन्य में दिलचस्पी होगी जो बेहतर प्रदर्शन करते हैं।

मापांक विधि के साथ, यदि आपके पास कहने के लिए आकार 1000 की एक तालिका है, तो 1 और 1000 के बीच कोई भी हैशवेल संबंधित स्लॉट में जाएगा। किसी भी नकारात्मक मूल्य, और 1000 से अधिक किसी भी मूल्य संभावित टकराव स्लॉट मूल्यों हो जाएगा। ऐसा होने की संभावना आपके हैशिंग विधि दोनों पर निर्भर करती है, साथ ही आप कुल कितने आइटम हैश तालिका में जोड़ते हैं। आमतौर पर, हैशटेबल का आकार बनाने के लिए यह सबसे अच्छा अभ्यास है कि इसमें जोड़े गए मूल्यों की कुल संख्या इसके आकार के लगभग 70% के बराबर है। यदि आपका हैश फ़ंक्शन समान वितरण का अच्छा काम करता है, तो आप आम तौर पर बिना बाल्टी / स्लॉट टकराव के बहुत कम मुठभेड़ करेंगे और यह लुकअप और राइटिंग ऑपरेशन दोनों के लिए बहुत जल्दी प्रदर्शन करेगा। यदि जोड़ने के लिए मूल्यों की कुल संख्या पहले से ज्ञात नहीं है, तो जो भी साधनों का उपयोग करके एक अच्छा मार्गदर्शक बनाएं,

मुझे आशा है कि इससे सहायता मिली है।

पुनश्च - C # में GetHashCode()विधि बहुत धीमी है और मैंने जिन स्थितियों का परीक्षण किया है, उनके तहत वास्तविक मूल्य टकराव में परिणाम होता है। कुछ असली मौज-मस्ती के लिए, अपना स्वयं का हैशफैक्शन बनाएं और इसे उस विशेष डेटा पर टकराने की कोशिश करें जिस पर आप हैशिंग हैं, गेटहैशकोड की तुलना में तेजी से चलाएं, और काफी समान रूप से वितरण करें। मैंने इंट साइज़ हैशकोड मानों के बजाय लंबे समय का उपयोग करके यह किया है और यह 0 की टक्कर के साथ हैशटेबल में 32 मिलियन तक हैशवेल्स पर काफी अच्छी तरह से काम किया है। दुर्भाग्य से मैं कोड को साझा नहीं कर सकता क्योंकि यह मेरे नियोक्ता से संबंधित है ... लेकिन मैं यह बता सकता हूं कि यह कुछ डेटा डोमेन के लिए संभव है। जब आप इसे प्राप्त कर सकते हैं, हैशटेबल बहुत तेज़ है। :)


मुझे पता है कि पद बहुत पुराना है, लेकिन क्या कोई समझा सकता है कि (शेष + 1) का मतलब यहां है
हरि

3
@ हरि remainderमूल मॉडुलो गणना के परिणाम को संदर्भित करता है, और अगला उपलब्ध स्लॉट खोजने के लिए हम इसमें 1 जोड़ते हैं।
x4nd3r

"सरणी में प्रत्येक स्लॉट में कुछ न कुछ होगा। कम से कम आप इस स्लॉट में हैशवेल या मान को संग्रहीत करेंगे।" - "स्लॉट" (बकेट) के लिए यह सामान्य है कि कोई भी मूल्य न रखे; खुले संबोधन कार्यान्वयन अक्सर NULL या पॉइंटर को एक लिंक्ड सूची में पहले नोड में संग्रहीत करते हैं - स्लॉट या बाल्टी में सीधे कोई मूल्य नहीं। "किसी भी अन्य में रुचि होगी" - "+1" जो आप स्पष्ट करते हैं उसे रेखीय जांच , अक्सर बेहतर प्रदर्शन करने वाला: द्विघात जांच कहा जाता है"आम तौर पर कोई बाल्टी / स्लॉट टक्कर के लिए बहुत कम मुठभेड़" - @ 70% क्षमता, ~ 12% स्लॉट w / 2 मान, ~ 3% 3 ....
टोनी डेलरो

"मैंने इंट साइज़ हैशकोड मानों के बजाय लंबे समय का उपयोग करके किया है और यह 0 टकराने वाले हैशटेबल में 32 मिलियन तक हैशवेल्स पर काफी अच्छी तरह से काम किया है।" - यह केवल सामान्य मामले में संभव नहीं है, जहां बाल्टियों की संख्या की तुलना में चाबियों के मूल्य बहुत बड़ी रेंज में प्रभावी रूप से यादृच्छिक हैं। ध्यान दें कि अलग-अलग हैश मान होना अक्सर आसान होता है (और longहैश मानों की बात का अर्थ है कि आपने क्या हासिल किया है), लेकिन यह सुनिश्चित करना कि वे मॉड /% ऑपरेशन के बाद हैश तालिका में टकराए नहीं हैं (सामान्य स्थिति में नहीं है) )।
टोनी डेलरो

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

17

यह मेरी समझ में कैसे काम करता है:

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

मान लीजिए कि आपके पास 200 ऑब्जेक्ट हैं, लेकिन उनमें से केवल 15 में हैश कोड हैं जो 'बी' अक्षर से शुरू होते हैं। हैश टेबल को केवल 200 ऑब्जेक्ट्स के बजाय 'B' बकेट में 15 ऑब्जेक्ट्स को देखने और खोजने की आवश्यकता होगी।

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


13

छोटा एवं सुन्दर:

एक हैश तालिका एक सरणी को लपेटता है, इसे कॉल करने देता है internalArray। आइटम को इस तरह से सरणी में डाला जाता है:

let insert key value =
    internalArray[hash(key) % internalArray.Length] <- (key, value)
    //oversimplified for educational purposes

कभी-कभी दो कुंजी सरणी में एक ही सूचकांक के लिए हैश होगी, और आप दोनों मान रखना चाहते हैं। मैं एक ही सूचकांक में दोनों मूल्यों को संग्रहीत करना पसंद करता हूं, जो कि internalArrayलिंक की गई सूचियों का एक सरणी बनाकर कोड करना सरल है :

let insert key value =
    internalArray[hash(key) % internalArray.Length].AddLast(key, value)

इसलिए, यदि मैं अपनी हैश तालिका से कोई आइटम पुनर्प्राप्त करना चाहता था, तो मैं लिख सकता था:

let get key =
    let linkedList = internalArray[hash(key) % internalArray.Length]
    for (testKey, value) in linkedList
        if (testKey = key) then return value
    return null

डिलीट ऑपरेशन्स लिखना जितना आसान है। जैसा कि आप बता सकते हैं, आवेषण, लुकअप और लिंक्ड लिस्ट की हमारी सरणी से हटाना लगभग O (1) है।

जब हमारी आंतरिक क्षमता बहुत अधिक हो जाती है, तो लगभग 85% क्षमता पर, हम आंतरिक सरणी का आकार बदल सकते हैं और सभी वस्तुओं को पुराने सरणी से नए सरणी में स्थानांतरित कर सकते हैं।


11

यह उससे भी सरल है।

हैशटेबल वैक्टर की एक सरणी (आमतौर पर विरल एक) से अधिक कुछ नहीं है जिसमें कुंजी / मूल्य जोड़े होते हैं। इस सरणी का अधिकतम आकार आमतौर पर हैशटेबल में संग्रहीत किए जा रहे डेटा के प्रकार के लिए संभावित मानों के सेट में आइटम की संख्या से छोटा है।

हैश एल्गोरिथ्म का उपयोग उस सरणी में एक इंडेक्स उत्पन्न करने के लिए किया जाता है जो उस आइटम के मूल्यों के आधार पर होता है जिसे सरणी में संग्रहीत किया जाएगा।

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

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


10

आप चीजों का एक गुच्छा, और एक सरणी लेते हैं।

प्रत्येक चीज़ के लिए, आप इसके लिए एक सूचकांक बनाते हैं, जिसे हैश कहते हैं। हैश के बारे में महत्वपूर्ण बात यह है कि यह बहुत बिखरा हुआ है; आप दो समान चीजों को समान हैश नहीं चाहते हैं।

आपने अपनी चीजों को हैश द्वारा इंगित स्थिति में सरणी में डाल दिया। किसी दिए गए हैश में एक से अधिक चीजें हवा हो सकती हैं, इसलिए आप एरे में चीजों को स्टोर करते हैं या कुछ और उपयुक्त है, जिसे हम आम तौर पर बाल्टी कहते हैं।

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

जब आपकी हैशिंग अच्छी तरह से काम कर रही है और आपकी सरणी काफी बड़ी है, तो सरणी में किसी भी विशेष सूचकांक में केवल कुछ चीजें ही होंगी, इसलिए आपको बहुत अधिक देखने की जरूरत नहीं होगी।

बोनस अंकों के लिए, इसे बनाएं ताकि जब आपकी हैश टेबल एक्सेस हो जाए, तो यह बाल्टी की शुरुआत में पाई गई चीज़ (यदि कोई हो) को स्थानांतरित करती है, तो अगली बार यह पहली चीज़ है।


1
अंतिम बिंदु के लिए धन्यवाद जिसका उल्लेख हर किसी ने किया है
संदीप राजू प्रभाकर

4

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

जैसा कि साइमन ने समझाया था, हैश फ़ंक्शन का उपयोग एक बड़े स्थान से एक छोटी सी जगह पर मैप करने के लिए किया जाता है। हमारे उदाहरण के लिए एक हैश फ़ंक्शन का एक सरल, भोली कार्यान्वयन स्ट्रिंग का पहला अक्षर ले सकता है, और इसे एक पूर्णांक में मैप कर सकता है, इसलिए "मगरमच्छ" का एक हैश कोड 0 है, "मधुमक्खी" का एक हैश कोड है 1, " ज़ेबरा "25 होगा, आदि।

अगला हमारे पास 26 बाल्टियों की एक सरणी है (जावा में ArrayLists हो सकती है), और हम उस बाल्टी में आइटम डालते हैं जो हमारी कुंजी के हैश कोड से मेल खाती है। यदि हमारे पास एक से अधिक आइटम हैं, जिनके पास एक कुंजी है जो एक ही अक्षर से शुरू होती है, तो उनके पास एक ही हैश कोड होगा, इसलिए सभी उस हैश कोड के लिए बाल्टी में जाएंगे, ताकि बाल्टी में एक रैखिक खोज की जा सके। एक विशेष आइटम खोजें।

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


3

यहाँ एक और तरीका है इसे देखो।

मुझे लगता है कि आप एक सरणी ए की अवधारणा को समझते हैं। यह कुछ ऐसा है जो अनुक्रमण के संचालन का समर्थन करता है, जहां आप Ith तत्व, ए [I] को एक चरण में प्राप्त कर सकते हैं, चाहे कितना भी बड़ा हो।

इसलिए, उदाहरण के लिए, यदि आप ऐसे लोगों के समूह के बारे में जानकारी संग्रहीत करना चाहते हैं, जो सभी अलग-अलग उम्र के होते हैं, तो एक सरल तरीका यह होगा कि एक ऐसा सरणी हो जो काफी बड़ा हो, और प्रत्येक व्यक्ति की आयु को एक सूचकांक के रूप में उपयोग करें। इस तरह, आप किसी भी व्यक्ति की जानकारी के लिए एक-चरण का उपयोग कर सकते हैं।

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

यही मूल विचार है। उम्र का उपयोग करने के बजाय, मूल्यों का अच्छा प्रसार करने वाले व्यक्ति के किसी भी कार्य का उपयोग किया जा सकता है। यह हैश फ़ंक्शन है। जैसे आप व्यक्ति के नाम के ASCII प्रतिनिधित्व के हर तीसरे हिस्से को ले सकते हैं, किसी क्रम में तले हुए। यह सब मायने रखता है कि आप नहीं चाहते कि बहुत से लोग एक ही बाल्टी में हैश करें, क्योंकि गति छोटी बची हुई बाल्टी पर निर्भर करती है।


2

हैश की गणना कैसे की जाती है यह आमतौर पर हैशटेबल पर निर्भर नहीं करता है, बल्कि इसमें जोड़े गए आइटम पर निर्भर करता है। फ्रेमवर्क / बेस क्लास लाइब्रेरी जैसे कि .net और जावा में, प्रत्येक ऑब्जेक्ट में एक GetHashCode () (या समान) विधि होती है, जो इस ऑब्जेक्ट के लिए हैश कोड लौटाती है। आदर्श हैश कोड एल्गोरिथ्म और सटीक कार्यान्वयन ऑब्जेक्ट द्वारा दर्शाए गए डेटा पर निर्भर करता है।


2

एक हैश तालिका पूरी तरह से इस तथ्य पर काम करती है कि व्यावहारिक अभिकलन यादृच्छिक अभिगम मशीन मॉडल का अनुसरण करता है अर्थात स्मृति में किसी भी पते पर मूल्य ओ (1) समय या निरंतर समय में पहुँचा जा सकता है।

इसलिए, अगर मेरे पास कुंजियों का एक ब्रह्मांड है (सभी संभावित कुंजियों का सेट जो मैं किसी एप्लिकेशन में उपयोग कर सकता हूं, जैसे छात्र के लिए रोल नंबर। यदि यह 4 अंक है तो यह ब्रह्मांड 1 से 9999 तक की संख्या का एक सेट है), और ए जिस तरह से मैं अपने सिस्टम में मेमोरी आवंटित कर सकता हूं, आकार की संख्या के एक निश्चित सेट पर उन्हें मैप करने का तरीका, सैद्धांतिक रूप से मेरा हैश टेबल तैयार है।

आम तौर पर, अनुप्रयोगों में कुंजियों के ब्रह्मांड का आकार उन तत्वों की संख्या की तुलना में बहुत बड़ा होता है जिन्हें मैं हैश टेबल में जोड़ना चाहता हूं (मैं 1 जीबी मेमोरी को हैश से बर्बाद नहीं करना चाहता, कहते हैं, 10000 या 100000 पूर्णांक मान क्योंकि वे 32 हैं बाइनरी रिप्रेंटियन में थोड़ा लंबा)। इसलिए, हम इस हैशिंग का उपयोग करते हैं। यह एक प्रकार के "गणितीय" ऑपरेशन का मिश्रण है, जो मेरे बड़े ब्रह्मांड को उन मूल्यों के एक छोटे से समूह में मैप करता है जिन्हें मैं स्मृति में रख सकता हूं। व्यावहारिक मामलों में, अक्सर हैश टेबल का स्थान समान "ऑर्डर" (बिग-ओ) के रूप में होता है (तत्वों की संख्या * प्रत्येक तत्व का आकार), इसलिए, हम बहुत अधिक मेमोरी बर्बाद नहीं करते हैं।

अब, छोटे सेट के लिए मैप किया गया एक बड़ा सेट, मैपिंग कई-से-एक होना चाहिए। इसलिए, विभिन्न कुंजियों को एक ही स्थान आवंटित किया जाएगा (?? उचित नहीं)। इसे संभालने के कुछ तरीके हैं, मैं उनमें से दो लोकप्रिय जानता हूं:

  • उस स्थान का उपयोग करें जिसे लिंक की गई सूची के संदर्भ के रूप में मूल्य के लिए आवंटित किया जाना था। यह लिंक की गई सूची एक या एक से अधिक मान संग्रहीत करेगी, जो एक से कई मैपिंग में एक ही स्लॉट में रहती हैं। लिंक की गई सूची में खोज करने वाले किसी व्यक्ति की मदद करने की कुंजी भी है। यह एक ही अपार्टमेंट में कई लोगों की तरह है, जब एक डिलीवरी-मैन आता है, तो वह कमरे में जाता है और विशेष रूप से लड़के के लिए पूछता है।
  • एक सरणी में एक डबल हैश फ़ंक्शन का उपयोग करें जो एकल मान के बजाय हर बार मानों का समान अनुक्रम देता है। जब मैं एक मूल्य को संग्रहीत करने के लिए जाता हूं, तो मैं देखता हूं कि क्या आवश्यक मेमोरी स्थान मुफ्त है या कब्जा कर लिया गया है। यदि यह मुफ़्त है, तो मैं अपने मूल्य को वहां संग्रहीत कर सकता हूं, यदि यह कब्जा है तो मैं अनुक्रम से अगला मूल्य ले सकता हूं और इसी तरह जब तक मुझे एक मुक्त स्थान नहीं मिलता है और मैं अपना मूल्य वहां संग्रहीत करता हूं। मूल्य की खोज या पुनर्प्राप्ति करते समय, मैं उसी रास्ते पर वापस जाता हूं, जैसा कि अनुक्रम द्वारा दिया गया है और प्रत्येक स्थान पर vaue से पूछते हैं कि क्या यह तब तक है जब तक कि मैं इसे नहीं ढूंढता हूं या सरणी में सभी संभावित स्थानों को खोजता हूं।

CLRS द्वारा एल्गोरिदम का परिचय विषय पर एक बहुत अच्छी जानकारी प्रदान करता है।


0

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

(void) addValue : (object) value
{
   int bucket = calculate_bucket_from_val(value);
   if (bucket) 
   {
       //do nothing, just overwrite
   }
   else   //create bucket
   {
      create_extra_space_for_bucket();
   }
   put_value_into_bucket(bucket,value);
}

(bool) exists : (object) value
{
   int bucket = calculate_bucket_from_val(value);
   return bucket;
}

जहाँ calculate_bucket_from_val()हैशिंग फंक्शन है जहाँ सभी विशिष्ट जादू होने चाहिए।

अंगूठे का नियम है: किसी दिए गए मान को सम्मिलित करने के लिए, बाल्टी को उस मूल्य से UNIQUE & DERIVABLE FER होना चाहिए जो कि STORE के लिए माना जाता है।

बाल्टी कोई भी स्थान है जहां मान संग्रहीत किए जाते हैं - यहां के लिए मैंने इसे एक सरणी सूचकांक के रूप में अंतर रखा है, लेकिन यह शायद एक स्मृति स्थान भी है।


1
"अंगूठे का नियम है: किसी दिए गए मान को सम्मिलित करने के लिए, बाल्टी को उस मूल्य से UNIQUE & DERIVABLE FER होना चाहिए जो कि STORE को माना जाता है।" - यह एक सही हैश फ़ंक्शन का वर्णन करता है , जो आमतौर पर संकलन समय पर ज्ञात कुछ सौ या हजार मूल्यों के लिए संभव है। अधिकांश हैश टेबल को टक्करों को संभालना होता है । इसके अलावा, हैश टेबल सभी बाल्टियों के लिए जगह आवंटित करने की प्रवृत्ति रखते हैं, चाहे वे खाली हों या नहीं, जबकि आपका छद्म कोड create_extra_space_for_bucket()नई चाबियों के सम्मिलन के दौरान एक कदम है। बाल्टी हालांकि संकेत हो सकता है।
टोनी डेलरॉय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.