हैश फ़ंक्शन स्ट्रिंग के लिए


124

मैं सी भाषा में हैश टेबल पर काम कर रहा हूं और स्ट्रिंग के लिए हैश फ़ंक्शन का परीक्षण कर रहा हूं।

पहला कार्य जो मैंने करने की कोशिश की है, वह है आस्की कोड जोड़ना और मोडुलो (100%) का उपयोग करना लेकिन मुझे डेटा के पहले परीक्षण के साथ खराब परिणाम मिले हैं: 130 शब्दों के लिए 40 टकराव।

अंतिम इनपुट डेटा में 8 000 शब्द होंगे (यह एक फ़ाइल में एक डिक्शनरी स्टोर है)। हैश तालिका को इंट टेबल के रूप में घोषित किया जाता है [10000] और इसमें txt फ़ाइल में शब्द की स्थिति होती है।

पहला सवाल यह है कि हैशिंग स्ट्रिंग के लिए सबसे अच्छा एल्गोरिथ्म कौन सा है? और हैश तालिका के आकार का निर्धारण कैसे करें?

अग्रिम में धन्यवाद !

:-)


11
यदि आपकी हैश तालिका में 10K प्रविष्टियाँ हैं, तो आप modulo 100 का उपयोग क्यों करेंगे? 130 शब्दों में से 40 टकराना इतने छोटे मापांक के साथ आश्चर्यजनक नहीं है।
कैरी ग्रेगोरी

13
Burtleburtle.net/bob/hash/evahash.html और partow.net/programming/hashfunctions देखें, जिनके लिए विभिन्न हैशिंग (सामान्य से स्ट्रिंग से क्रिप्टो) के बारे में संसाधन हैं।

3
@CareyGregory को स्पष्ट करने के लिए: आप महसूस करते हैं कि, एक बुनियादी गणितीय सत्य के रूप में, 100 बाल्टियों में 130 आइटम (यानी, मॉड 100) को 30 टकराव उत्पन्न करना चाहिए (जहां टकराव की गणना हर बार दूसरे, तीसरे, आदि के रूप में की जाती है)। एक बाल्टी), सही है? तो आप उससे थोड़ा ही ऊपर हैं।
derobert

4
@ लिलावुड: ठीक है, यही मैंने सोचा है, लेकिन एक बेहतर परीक्षा होने के लिए आपको 100 प्रविष्टियों की हैश तालिका के साथ 80 शब्दों का उपयोग करना चाहिए। यह आपके लाइव डेटा के समान अनुपात देगा और टक्करों को बाध्य नहीं करेगा।
कैरी ग्रेगरी

जवाबों:


185

मेरे पास djb2डैन बर्नस्टीन के अच्छे परिणाम हैं ।

unsigned long
hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

37
उत्तर में जुड़ा पृष्ठ बहुत दिलचस्प है।
एड्रियन प्लिसन

2
कैसे कार्यक्रम लूप से बाहर चलाता है ?? = एस
डैनियल एन

1
@ danfly09 जब c शून्य है। समतुल्य (c = * str ++) होगा (0! = (C = * str ++))
rxantos

5
@Josepas हैश फ़ंक्शन को आदर्श रूप से size_tया इस तरह के अन्य अहस्ताक्षरित मान (जैसे कि इस कोड में अहस्ताक्षरित लंबे) को वापस करना चाहिए । फोन करने वाले परिणाम के सापेक्ष ले जा हैश तालिका में फिट करने के लिए के लिए जिम्मेदार है। कॉल करने वाले को टेबल स्लॉट को नियंत्रित किया जा रहा है; फ़ंक्शन नहीं है। यह सिर्फ कुछ अहस्ताक्षरित संख्या देता है।
व्हाट्सक्राइग

6
गजब का। इस एल्गोरिथ्म ने मुरम हैश, एफएनवी वेरिएंट हैश और कई अन्य लोगों के नरक को हरा दिया! +1
डेविड हैम

24

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

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

उदाहरण के लिए, 8 बिट बाइट्स के सामान्य मामले को मानते हुए, आप 5 बिट्स द्वारा घुमा सकते हैं:

int hash(char const *input) { 
    int result = 0x55555555;

    while (*input) { 
        result ^= *input++;
        result = rol(result, 5);
    }
}

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


यह c नहीं है, लेकिन मैं इस संबंधित उत्तर के लिए आपके विचारों में दिलचस्पी
लूंगा

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

@ जेरी कॉफिन को आरएलएल () फ़ंक्शन के लिए मुझे किस लाइब्रेरी की आवश्यकता है?
thanos.a

@ thanos.a: मुझे इसके बारे में पता नहीं है कि यह एक लाइब्रेरी में है, लेकिन अपना रोल करने में केवल एक लाइन या दो कोड लगते हैं। एक चंक बाएं, दूसरा चंक दाएं, और उन्हें एक साथ शिफ्ट करें।
जेरी कॉफिन

8

विकिपीडिया एक अच्छा स्ट्रिंग हैश फ़ंक्शन दर्शाता है जिसे जेनकिंस वन एट ए टाइम हैश कहा जाता है। यह इस हैश के उन्नत संस्करणों को भी उद्धृत करता है।

uint32_t jenkins_one_at_a_time_hash(char *key, size_t len)
{
    uint32_t hash, i;
    for(hash = i = 0; i < len; ++i)
    {
        hash += key[i];
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    return hash;
}

8

सी के लिए मौजूदा hashtable कार्यान्वयन के एक नंबर सी मानक पुस्तकालय hcreate से, कर रहे हैं / hdestroy / hsearch, में उन लोगों के लिए अप्रैल और चिकना है, जो भी पहले से बनाए गए हैश फंक्शन प्रदान करते हैं। मैं अत्यधिक अपने स्वयं के हैशटेबल या हैश फ़ंक्शन का आविष्कार करने के बजाय उन का उपयोग करने की सलाह दूंगा; वे आम उपयोग के मामलों के लिए भारी अनुकूलित किए गए हैं।

यदि आपका डेटासेट स्थिर है, तो, आपका सबसे अच्छा समाधान शायद एक सही हैश का उपयोग करना है । gperf आपके लिए दिए गए डेटासेट के लिए एक सही हैश उत्पन्न करेगा।


hsearch स्ट्रिंग्स या स्ट्रिंग ptr पते की तुलना करके खोज करता है? मुझे लगता है कि यह सिर्फ ptr पते की जाँच कर रहा है? मैंने अलग-अलग पॉइंटर्स का उपयोग करने की कोशिश की लेकिन एक ही स्ट्रिंग कैल्यू। hsearch कोई तत्व नहीं मिला बताते हुए विफल रहता है
mk ..

3

djb2 ​​में इस 466k अंग्रेजी शब्दकोश के लिए 317 टकराव हैं, जबकि मुरमुरैश के पास 64 बिट हैश के लिए और 21 के लिए 32 बिट हैश (लगभग 25 के लिए 466k यादृच्छिक 32 बिट हैश) के लिए अपेक्षित नहीं है। मेरी सिफारिश मुरमुरैश का उपयोग कर रही है यदि उपलब्ध है, तो यह बहुत तेज़ है, क्योंकि यह एक बार में कई बाइट्स में लेता है। लेकिन अगर आपको अपनी परियोजना को कॉपी और पेस्ट करने के लिए एक सरल और संक्षिप्त हैश फ़ंक्शन की आवश्यकता है, तो मैं एक-एक बर्ट-इन-ब-टाइम संस्करण का उपयोग करने की सलाह दूंगा:

uint32_t inline MurmurOAAT32 ( const char * key)
{
  uint32_t h(3323198485ul);
  for (;*key;++key) {
    h ^= *key;
    h *= 0x5bd1e995;
    h ^= h >> 15;
  }
  return h;
}

uint64_t inline MurmurOAAT64 ( const char * key)
{
  uint64_t h(525201411107845655ull);
  for (;*key;++key) {
    h ^= *key;
    h *= 0x5bd1e9955bd1e995;
    h ^= h >> 47;
  }
  return h;
}

एक हैश तालिका का इष्टतम आकार है - संक्षेप में - जितना संभव हो उतना बड़ा है जबकि अभी भी मेमोरी में फिटिंग है। क्योंकि हम आमतौर पर नहीं जानते हैं या देखना चाहते हैं कि हमारे पास कितनी मेमोरी उपलब्ध है, और यह बदल भी सकती है, इष्टतम हैश टेबल का आकार तालिका में संग्रहित किए जाने वाले तत्वों की अपेक्षित संख्या से लगभग 2x गुणा है। इससे अधिक का आवंटन आपकी हैश तालिका को तेज़ बना देगा, लेकिन तेज़ी से घटते हुए रिटर्न से, आपकी हैश तालिका को इससे छोटा बना देगा, जिससे यह तेजी से धीमी हो जाएगी। ऐसा इसलिए है क्योंकि हैश टेबल के लिए अंतरिक्ष और समय जटिलता के बीच एक गैर-रैखिक व्यापार-बंद है , 2-sqrt (2) = 0.58 का एक इष्टतम लोड कारक ... जाहिरा तौर पर।


2

सबसे पहले, 130 शब्दों के लिए 40 टकरावों को 0..99 बुरा माना गया है? अगर आप इसके लिए विशेष रूप से कदम नहीं उठा रहे हैं तो आप सही हैशिंग की उम्मीद नहीं कर सकते। एक साधारण हैश फ़ंक्शन में अधिकांश समय यादृच्छिक जनरेटर की तुलना में कम टकराव नहीं होगा।

एक अच्छी प्रतिष्ठा के साथ एक हैश फ़ंक्शन मुरमुरैश 3 है

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


1
हैश टक्करों की अपेक्षित संख्या है n - m * (1 - ((m-1)/m)^n) = 57.075...। 40 टक्करों की तुलना में बेहतर हो सकता है जो मौका द्वारा उम्मीद की जा सकती है (0.999 के पी-स्कोर पर 46 से 70)। विचाराधीन हैश फ़ंक्शन यादृच्छिक होने की तुलना में अधिक समान है या हम एक बहुत ही दुर्लभ घटना देख रहे हैं।
वोल्फगैंग ब्रेहम

2

हालाँकि djb2, जैसा कि cnicutar द्वारा स्टैकओवरफ़्लो पर प्रस्तुत किया गया है , लगभग निश्चित रूप से बेहतर है, मुझे लगता है कि यह K & R हैश को दिखाने के लायक है:

1) K & R 1st संस्करण ( स्रोत ) में प्रस्तुत रूप में एक भयानक हैश एल्गोरिथम,

unsigned long hash(unsigned char *str)
{
    unsigned int hash = 0;
    int c;

    while (c = *str++)
        hash += c;

    return hash;
}

2) संभवतः एक बहुत ही सभ्य हैश एल्गोरिथ्म, जैसा कि K & R संस्करण 2 में प्रस्तुत किया गया है (मेरे द्वारा पृष्ठ की 144 पृष्ठ पर सत्यापित); NB: % HASHSIZEयदि आप हैश एल्गोरिथ्म के बाहर मापांक आकार-से-आपकी-लंबाई करने की योजना पर वापसी विवरण से हटाना सुनिश्चित करते हैं। इसके अलावा, मैं आपको unsigned longसाधारण unsigned(इंट) के बजाय रिटर्न और "हैशवल" प्रकार बनाने की सलाह देता हूं ।

unsigned hash(char *s)
{
    unsigned hashval;

    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31*hashval;
    return hashval % HASHSIZE;
}

ध्यान दें कि यह दो एल्गोरिदम से स्पष्ट है कि एक कारण 1 संस्करण हैश इतना भयानक है क्योंकि यह स्ट्रिंग स्ट्रिंग को ध्यान में नहीं रखता है क्रम , इसलिए hash("ab")इसलिए उसी मान को वापस करेगा hash("ba")। यह वह जगह है नहीं तो 2 संस्करण हैश के साथ, तथापि, जो होता है (ज्यादा बेहतर!) दो अलग-अलग मान उन स्ट्रिंग्स के लिए वापस जाएँ।

GCC C ++ 11 हैशिंग फ़ंक्शन unordered_map(हैश टेबल टेम्प्लेट) और unordered_set(हैश सेट टेम्प्लेट) के लिए उपयोग किए जाने वाले कार्य निम्नानुसार दिखाई देते हैं।

  • यह ऑस्टिन Appleby ( http://murmurhash.googlepages.com/ ) द्वारा GCC "MurmurHashUnaligned2" के कार्यान्वयन का उपयोग करते हुए बताते हुए कि GCC C ++ 11 हैश फ़ंक्शन के उपयोग के सवाल का एक आंशिक उत्तर है ।
  • "Gcc / libstdc ++ - v3 / libsupc ++ / hash_bytes.cc" फ़ाइल में, यहाँ ( https://github.com/gcc-mirror/gcc/blob/master/libsdc++-v3/libsupc++/hash_bytes.cc ), मुझे पता चला है। कार्यान्वयन। यहाँ "32-बिट size_t" रिटर्न मान के लिए एक है, उदाहरण के लिए (11 अगस्त 2017 को खींचा गया):

कोड:

// Implementation of Murmur hash for 32-bit size_t.
size_t _Hash_bytes(const void* ptr, size_t len, size_t seed)
{
  const size_t m = 0x5bd1e995;
  size_t hash = seed ^ len;
  const char* buf = static_cast<const char*>(ptr);

  // Mix 4 bytes at a time into the hash.
  while (len >= 4)
  {
    size_t k = unaligned_load(buf);
    k *= m;
    k ^= k >> 24;
    k *= m;
    hash *= m;
    hash ^= k;
    buf += 4;
    len -= 4;
  }

  // Handle the last few bytes of the input array.
  switch (len)
  {
    case 3:
      hash ^= static_cast<unsigned char>(buf[2]) << 16;
      [[gnu::fallthrough]];
    case 2:
      hash ^= static_cast<unsigned char>(buf[1]) << 8;
      [[gnu::fallthrough]];
    case 1:
      hash ^= static_cast<unsigned char>(buf[0]);
      hash *= m;
  };

  // Do a few final mixes of the hash.
  hash ^= hash >> 13;
  hash *= m;
  hash ^= hash >> 15;
  return hash;
}

2

मैंने इन हैश कार्यों की कोशिश की है और निम्नलिखित परिणाम प्राप्त किया है। मेरे पास लगभग 960 ^ 3 प्रविष्टियां हैं, प्रत्येक 64 बाइट्स लंबी, 64 चार्ट्स अलग क्रम में, हैश वैल्यू 32 बिट है। यहाँ से कोड ।

Hash function    | collision rate | how many minutes to finish
==============================================================
MurmurHash3      |           6.?% |                      4m15s
Jenkins One..    |           6.1% |                      6m54s   
Bob, 1st in link |          6.16% |                      5m34s
SuperFastHash    |            10% |                      4m58s
bernstein        |            20% |       14s only finish 1/20
one_at_a_time    |          6.16% |                       7m5s
crc              |          6.16% |                      7m56s

एक अजीब बात यह है कि लगभग सभी हैश फ़ंक्शन में मेरे डेटा के लिए 6% टकराव की दर है।


हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं।
Thewaywewere

एक अच्छी तालिका के लिए बनाया गया, अपने उत्तर में प्रत्येक हैश के लिए स्रोत कोड पोस्ट करना आवश्यक है। अन्यथा, लिंक टूट सकते हैं और हम भाग्य से बाहर हैं।
गेब्रियल स्टेपल्स

टक्करों की अपेक्षित संख्या 9.112499989700318E + 7 या 0.103 * 960³ होनी चाहिए यदि हैश वास्तव में यादृच्छिक थे, तो मुझे आश्चर्य नहीं होगा अगर वे उस मूल्य के आसपास थे, लेकिन 0.0616 * 960³ थोड़ा बंद लगता है, लगभग जैसे कि हैश को समान रूप से वितरित किया जाता है जो कि संयोग से अपेक्षित होगा, और 64 बाइट की लंबाई पर इस सीमा को निश्चित रूप से संपर्क किया जाना चाहिए। क्या आप स्ट्रिंग्स के सेट को साझा कर सकते हैं जिसे आपने हैश किया है ताकि मैं इसे पुन: पेश करने की कोशिश कर सकूं?
वुल्फगैंग ब्रेहम

0

एक चीज जो मैंने अच्छे परिणामों के साथ उपयोग की है, वह निम्नलिखित है (मुझे नहीं पता कि क्या इसका उल्लेख पहले से ही है क्योंकि मुझे इसका नाम याद नहीं है)।

आप अपनी कुंजी की वर्णमाला [0,255] में प्रत्येक वर्ण के लिए एक यादृच्छिक संख्या के साथ एक टेबल टी को रोकते हैं। आपने अपनी कुंजी 'k0 k1 k2 ... kN' को T [k0] xor T [k1] xor ... xor T [kN] से लिया। आप आसानी से दिखा सकते हैं कि यह आपके यादृच्छिक संख्या जनरेटर के रूप में यादृच्छिक है और इसकी कम्प्यूटेशनल रूप से बहुत संभव है और यदि आप बहुत सारे उदाहरणों के साथ बहुत खराब उदाहरण में चलते हैं तो आप बस यादृच्छिक संख्याओं के एक ताजा बैच का उपयोग करके पूरी बात दोहरा सकते हैं।


अगर मैं गलत नहीं हूँ तो यह गैब्रियल के जवाब में K & R 1st जैसी ही समस्या से ग्रस्त है; यानी "अब" और "बा" समान मूल्य के लिए हैश होगा।
जोहान ऑस्करसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.