2009-07-03 9 views
17

मेरे पास एक ऑब्जेक्ट है जिसके लिए मैं एक अद्वितीय हैश उत्पन्न करना चाहता हूं (GetHashCode() को ओवरराइड करें) लेकिन मैं अतिप्रवाह या कुछ अप्रत्याशित से बचना चाहता हूं।क्या निजी सदस्यों के लिए हैश कोड एक नया हैश कोड उत्पन्न करना संभव है?

कोड स्ट्रिंग के एक छोटे से संग्रह के हैश कोड को जोड़ने का परिणाम होना चाहिए।

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

क्या ऐसा कुछ पर्याप्त होगा और क्या ऐसा करने का एक बेहतर तरीका है?

int hash = 0; 
foreach(string item in collection){ 
    hash += (item.GetHashCode()/collection.Count) 
} 
return hash; 

संपादित करें: अभी तक उत्तर के लिए धन्यवाद। @Jon स्कीट: नहीं, आदेश नहीं महत्वपूर्ण है

मुझे लगता है कि यह लगभग एक एक और सवाल है लेकिन जब से मैं परिणाम उपयोग कर रहा हूँ एक कैश कुंजी (स्ट्रिंग) उत्पन्न करने के लिए यह मतलब होगा MD5 की तरह एक crytographic हैश समारोह का उपयोग करने के या बस इस int के स्ट्रिंग प्रतिनिधित्व का उपयोग करें?

+0

यह आपके अपडेट से लगता है कि आप इस प्रक्रिया से आउटपुट की उम्मीद कर रहे हैं ताकि टक्कर की पर्याप्त कम संभावना हो सके ताकि इसे एक अद्वितीय कुंजी के रूप में पेश किया जा सके ... आपको _very_ good हैश और कुछ और बिट्स की आवश्यकता है इस काम को बनाने के लिए 32 से अधिक – ShuggyCoUk

+0

यदि आप एक कुंजी चाहते हैं तो क्रिप्टो हैश का उपयोग करना सामान्य रूप से पर्याप्त होगा (जब तक आपको इसकी क्रिप्टो गुणों की परवाह नहीं है MD5 ठीक है) लेकिन यह अन्य की तुलना में गणना करने के लिए काफी महंगा होगा जैसे प्रभावी गैर क्रिप्टो हैश होंगे। – ShuggyCoUk

उत्तर

24

मार्क और जॉन द्वारा बताए गए मूलभूत सिद्धांत हैं बुरा नहीं है लेकिन वे परिणामों के वितरण की उनकी समानता के मामले में इष्टतम से दूर हैं। अफसोस की बात है कि 'मुंह से गुणा' दृष्टिकोण नकल से इतने सारे लोगों द्वारा प्रतिलिपि बनाई गई है not the best choice in many cases बेहतर वितरण कार्यों की गणना करने के लिए सस्ता द्वारा हासिल किया जा सकता है (हालांकि यह बहुत आधुनिक हार्डवेयर पर मामूली है)। वास्तव में हैशिंग के कई पहलुओं में प्राइम फेंकना no panacea है।

यदि यह डेटा महत्वपूर्ण आकार के हैश तालिकाओं के लिए उपयोग किया जाता है तो मैं Bret Mulvey's excellent study and explanation of various modern (and not so modern) hashing techniques को आसानी से सी # के साथ पढ़ने की अनुशंसा करता हूं।

ध्यान दें कि विभिन्न हैश फ़ंक्शंस के तारों के साथ व्यवहार बहुत अधिक पक्षपातपूर्ण होता है, जिससे तार कम होते हैं (मोटे तौर पर बोलते हैं कि बिट्स प्रवाह से पहले कितने अक्षर हैं) या लंबे समय तक।

लागू करने के लिए सबसे सरल और आसान में से एक भी है, जेनकिन्स वन एक समय हैश में सबसे अच्छा है।

private static unsafe void Hash(byte* d, int len, ref uint h) 
{ 
    for (int i = 0; i < len; i++) 
    { 
     h += d[i]; 
     h += (h << 10); 
     h ^= (h >> 6); 
    } 
} 

public unsafe static void Hash(ref uint h, string s) 
{ 
    fixed (char* c = s)    
    { 
     byte* b = (byte*)(void*)c; 
     Hash(b, s.Length * 2, ref h); 
    } 
} 

public unsafe static int Avalanche(uint h) 
{ 
    h += (h<< 3); 
    h ^= (h>> 11); 
    h += (h<< 15); 
    return *((int*)(void*)&h); 
} 

आप तो यह इतना की तरह उपयोग कर सकते हैं:

uint h = 0; 
foreach(string item in collection) 
{ 
    Hash(ref h, item); 
} 
return Avalanche(h); 

तुम इतनी जैसे कई अलग अलग प्रकार के विलय कर सकते हैं:

public unsafe static void Hash(ref uint h, int data) 
{ 
    byte* d = (byte*)(void*)&data; 
    AddToHash(d, sizeof(int), ref h); 
} 

public unsafe static void Hash(ref uint h, long data) 
{ 
    byte* d= (byte*)(void*)&data; 
    Hash(d, sizeof(long), ref h); 
} 

आप केवल के साथ एक वस्तु के रूप में क्षेत्र में पहुंच सकते हैं आंतरिकों का कोई ज्ञान नहीं, आप बस प्रत्येक पर GetHashCode() को कॉल कर सकते हैं और उस मान को गठबंधन कर सकते हैं:

uint h = 0; 
foreach(var item in collection) 
{ 
    Hash(ref h, item.GetHashCode()); 
} 
return Avalanche(h); 

अफसोस की बात है कि आप आकार (टी) नहीं कर सकते हैं, इसलिए आपको प्रत्येक संरचना को व्यक्तिगत रूप से करना होगा।

यदि आप प्रतिबिंब का उपयोग करना चाहते हैं तो आप प्रति प्रकार के आधार पर एक फ़ंक्शन बना सकते हैं जो संरचनात्मक पहचान और सभी क्षेत्रों पर हैशिंग करता है।

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

+0

ऐसा लगता है कि आपके द्वारा पोस्ट किया गया लिंक हैश वैल्यू * मॉडुलो * एक प्राइम का उपयोग करने के बारे में बात करता है, हैश वैल्यू उत्पन्न नहीं करता है। दूसरे शब्दों में, यह हैश पीढ़ी नहीं है, यह हैश -> बाल्टी परिवर्तन। –

+0

यदि आप अगले लिंक (ब्रेट्स सिंपलशैश विश्लेषण) को देखते हैं तो यह दिखाता है कि वितरण की समानता में यह कितना खराब है, http://home.comcast.net/~bretm/hash/5.html सरल परीक्षण को पहले परीक्षण के रूप में वर्णित करता है – ShuggyCoUk

+0

आप उस लिंक में सही हैं जहां यह मुद्दा इस मुद्दे को भ्रमित करता है। – ShuggyCoUk

1

इस दृष्टिकोण के साथ कुछ भी गलत नहीं है जब तक कि जिन सदस्यों के हैंशकोड आप संयोजन कर रहे हैं, हैश कोड के नियमों का पालन करें। संक्षेप में ...

  1. निजी सदस्यों के हैश कोड वस्तु के जीवन भर के लिए परिवर्तन नहीं होना चाहिए
  2. कंटेनर वस्तु निजी सदस्यों बदले में ऐसा न हो कि को इंगित परिवर्तन नहीं करना चाहिए हैश कोड बदलने वे सिर्फ अच्छी तरह से ज्यादातर स्थितियों में वितरित किया जा करने के लिए होती हैं - कंटेनर के
24

Hashes नहीं मतलब अद्वितीय होना है। वे सिर्फ सुसंगत होने के लिए हैं। ध्यान दें कि अतिप्रवाह एक समस्या नहीं होनी चाहिए।

बस जोड़ना आम तौर पर एक अच्छा विचार नहीं है, और निश्चित रूप से विभाजित नहीं है। यहाँ दृष्टिकोण मैं आमतौर पर उपयोग करें:

int result = 17; 
foreach (string item in collection) 
{ 
    result = result * 31 + item.GetHashCode(); 
} 
return result; 

आप एक जाँच संदर्भ में अन्यथा हैं, तो आप जानबूझकर यह अनियंत्रित बनाने के लिए चाहते हो सकता है।

ध्यान दें कि यह मानता है कि आदेश महत्वपूर्ण है, यानी कि {"ए", "बी"} {"बी", "ए"} से अलग होना चाहिए। अगर यह मामला नहीं है तो कृपया हमें बताएं।

+1

lol - हमने अलग-अलग प्राइम्स चुना (और मैंने एक गिनती में गेट किया। गेटशैशकोड), लेकिन फिर भी हम एक दूसरे की नकल करते हैं ;- –

+0

दरअसल - मुझे पूरा यकीन नहीं है कि आप हर बार हैश कोड क्यों गुणा कर रहे हैं, इसे फिर भी एक मिनट में गुणा किया जाएगा ... –

+0

सच है, सच है - लेकिन आपके पास और भी विस्तार है इसलिए मैं हटा रहा हूं। एक मिनट में –

1

यदि आइटम का क्रम महत्वपूर्ण नहीं है (यानी {"ए", "बी"} {"बी", "ए"} जैसा ही है) तो आप हैश कोड को गठबंधन करने के लिए विशेष रूप से उपयोग कर सकते हैं:

hash ^= item.GetHashCode(); 

[संपादित करें: के रूप में मार्क एक अलग जवाब के लिए एक टिप्पणी में बताया, यह भी तरह संग्रह देने की सबसे बड़ी खामी है { "एक"} और { "एक", "ख", "ख"} एक ही हैश कोड]

हैं क्रम महत्वपूर्ण है, आप के बजाय एक प्रमुख संख्या से गुणा और जोड़ सकते हैं:।

hash *= 11; 
hash += item.GetHashCode(); 

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

संबंधित मुद्दे