हाल ही में जब तक मेरा जवाब जॉन स्कीट के यहाँ बहुत करीब रहा होगा। हालाँकि, मैंने हाल ही में एक परियोजना शुरू की जिसमें पावर-ऑफ-दो हैश टेबल का उपयोग किया गया, वह हैश टेबल जहाँ आंतरिक तालिका का आकार 8, 16, 32 है, आदि। अभाज्य संख्या संख्याओं के पक्ष में एक अच्छा कारण है, लेकिन वहाँ पावर ऑफ़ टू साइज़ के भी कुछ फ़ायदे हैं।
और यह बहुत ज्यादा चूसा। इसलिए थोड़े से प्रयोग और शोध के बाद मैंने निम्नलिखित के साथ अपने हैश को फिर से शुरू किया:
public static int ReHash(int source)
{
unchecked
{
ulong c = 0xDEADBEEFDEADBEEF + (ulong)source;
ulong d = 0xE2ADBEEFDEADBEEF ^ c;
ulong a = d += c = c << 15 | c >> -15;
ulong b = a += d = d << 52 | d >> -52;
c ^= b += a = a << 26 | a >> -26;
d ^= c += b = b << 51 | b >> -51;
a ^= d += c = c << 28 | c >> -28;
b ^= a += d = d << 9 | d >> -9;
c ^= b += a = a << 47 | a >> -47;
d ^= c += b << 54 | b >> -54;
a ^= d += c << 32 | c >> 32;
a += d << 25 | d >> -25;
return (int)(a >> 1);
}
}
और फिर मेरी शक्ति की दो हैश तालिका किसी भी अधिक चूसना नहीं था।
इसने मुझे परेशान कर दिया, क्योंकि ऊपर काम नहीं करना चाहिए। या अधिक सटीक रूप से, यह तब तक काम नहीं करना चाहिए जब तक कि मूल GetHashCode()
बहुत विशिष्ट तरीके से खराब न हो ।
एक हैशकोड को फिर से मिलाना एक महान हैशकोड में सुधार नहीं कर सकता है, क्योंकि एकमात्र संभावित प्रभाव यह है कि हम कुछ और टकरावों का परिचय देते हैं।
एक हैश कोड को फिर से मिलाना एक भयानक हैश कोड में सुधार नहीं कर सकता है, क्योंकि एकमात्र संभावित प्रभाव हम बदल रहे हैं जैसे मूल्य 53 पर बड़ी संख्या में टकराव 18,3487,291 मूल्य की बड़ी संख्या।
हैश कोड को री-मिक्स करने से केवल एक हैश कोड में सुधार हो सकता है जो कम से कम अपनी रेंज (2 32 संभावित मूल्यों) में पूरी तरह से टकराव से बचने में काफी अच्छी तरह से किया है, लेकिन बुरी तरह से टकराव से बचने के लिए जब एक हैश तालिका में वास्तविक उपयोग के लिए नीचे। जबकि पावर-ऑफ़-टू टेबल के सरल मोडुलो ने इसे और अधिक स्पष्ट कर दिया था, यह अधिक सामान्य प्राइम-नंबर तालिकाओं के साथ नकारात्मक प्रभाव भी डाल रहा था, जो कि स्पष्ट नहीं था (पुनर्वितरण में अतिरिक्त काम लाभ को पछाड़ देगा , लेकिन लाभ अभी भी होगा)।
संपादित करें: मैं भी खुले-संबोधन का उपयोग कर रहा था, जिससे टकराव के प्रति संवेदनशीलता भी बढ़ गई होगी, शायद इस तथ्य से कहीं अधिक यह शक्ति-दो था।
और अच्छी तरह से, यह गड़बड़ी थी कि .NET (या यहां अध्ययन ) string.GetHashCode()
में कार्यान्वयन कितना बेहतर हो सकता है (कम टकराव के कारण लगभग 20-30 गुना तेजी से चल रहे परीक्षणों के क्रम पर) और अधिक परेशान करने पर मेरे खुद के हैश कोड सुधार किया जा सकता है (इससे कहीं अधिक)।
सभी GetHashCode () कार्यान्वयन जिन्हें मैंने पिछले समय में कोडित किया था, और वास्तव में इस साइट पर उत्तरों के आधार के रूप में उपयोग किया गया था, वे मेरे मुकाबले बहुत खराब थे । ज्यादातर समय यह बहुत सारे उपयोगों के लिए "काफी अच्छा" था, लेकिन मैं कुछ बेहतर करना चाहता था।
तो मैंने उस प्रोजेक्ट को एक तरफ रख दिया (यह वैसे भी एक पालतू प्रोजेक्ट था) और जल्दी से .NET में एक अच्छा, अच्छी तरह से वितरित हैश कोड का उत्पादन करने के तरीके को देखने लगा।
अंत में मैं SpookyHash को .NET में पोर्ट करने पर बस गया । वास्तव में ऊपर दिया गया कोड 32-बिट इनपुट से 32-बिट आउटपुट का उत्पादन करने के लिए SpookyHash का उपयोग करने का एक तेज़-पथ संस्करण है।
अब, SpookyHash कोड के टुकड़े को याद करने के लिए एक अच्छा त्वरित नहीं है। इसके बारे में मेरा पोर्ट इसलिए भी कम है क्योंकि मैंने इसे बेहतर गति के लिए बहुत कुछ हाथ में लिया है। लेकिन यही कोड पुन: उपयोग के लिए है।
फिर मैंने उस प्रोजेक्ट को एक तरफ रख दिया , क्योंकि जिस तरह से ओरिजिनल प्रोजेक्ट ने बेहतर हैश कोड का उत्पादन करने के सवाल का उत्पादन किया था, इसलिए उस प्रोजेक्ट ने एक बेहतर .NET मेमसीपी का उत्पादन करने का सवाल पैदा किया।
फिर मैं वापस आया, और बहुत सारे ओवरलोड्स का उत्पादन किया जो आसानी से सभी देशी प्रकारों के बारे में (। को छोड़कर decimal
) एक हैड कोड में फ़ीड कर सके ।
यह तेज़ है, जिसके लिए बॉब जेनकिंस सबसे अधिक श्रेय के हकदार हैं क्योंकि उनका मूल कोड मैंने अभी भी तेजी से बनाया है, खासकर 64-बिट मशीनों पर जो एल्गोरिथ्म-के लिए अनुकूलित है।
पूरा कोड https://bitbucket.org/JonHanna/spookilysharp/src पर देखा जा सकता है लेकिन विचार करें कि ऊपर दिया गया कोड इसका एक सरलीकृत संस्करण है।
हालाँकि, चूंकि अब यह पहले से ही लिखा गया है, इसलिए इसका उपयोग अधिक आसानी से किया जा सकता है:
public override int GetHashCode()
{
var hash = new SpookyHash();
hash.Update(field1);
hash.Update(field2);
hash.Update(field3);
return hash.Final().GetHashCode();
}
यह बीज मान भी लेता है, इसलिए यदि आपको अविश्वासित इनपुट से निपटने की आवश्यकता है और हैश DoS हमलों से बचाव करना चाहते हैं, तो आप अपटाइम या समान के आधार पर एक बीज निर्धारित कर सकते हैं, और हमलावरों द्वारा अप्रत्याशित परिणाम दे सकते हैं:
private static long hashSeed0 = Environment.TickCount;
private static long hashSeed1 = DateTime.Now.Ticks;
public override int GetHashCode()
{
//produce different hashes ever time this application is restarted
//but remain consistent in each run, so attackers have a harder time
//DoSing the hash tables.
var hash = new SpookyHash(hashSeed0, hashSeed1);
hash.Update(field1);
hash.Update(field2);
hash.Update(field3);
return hash.Final().GetHashCode();
}
* इसमें एक बड़ा आश्चर्य यह है कि एक रोटेशन विधि हाथ से (x << n) | (x >> -n)
सुधारने वाली चीजें बेहतर हुई हैं। मुझे यकीन है कि जिटर ने मेरे लिए इनलाइन किया होगा, लेकिन प्रोफाइलिंग अन्यथा दिखाई गई।
† decimal
नेट परिप्रेक्ष्य से मूल नहीं है, हालांकि यह C # से है। इसके साथ समस्या यह है कि इसकी अपनी GetHashCode()
परिशुद्धता को महत्वपूर्ण मानते हैं जबकि अपने स्वयं के Equals()
नहीं। दोनों वैध विकल्प हैं, लेकिन इस तरह मिश्रित नहीं हैं। अपने स्वयं के संस्करण को लागू करने के लिए, आपको एक या दूसरे को चुनने की आवश्यकता है, लेकिन मुझे नहीं पता कि आप क्या चाहते हैं।
Comparison तुलना के माध्यम से। यदि एक तार पर उपयोग किया जाता है, तो 64 बिट्स पर स्पूकीहैश string.GetHashCode()
32 बिट्स की तुलना string.GetHashCode()
में काफी तेज होता है, जो कि 64 बिट्स की तुलना में थोड़ा तेज होता है , जो कि 32 बिट्स पर स्पूकीहैश की तुलना में काफी तेज है, हालांकि अभी भी काफी तेजी से एक उचित विकल्प है।
GetHashCode
। मुझे उम्मीद है कि यह दूसरों के लिए मददगार होगा। एरिक लिपर्ट द्वारा लिखित GetHashCode के लिए दिशानिर्देश और नियम