แม้ว่าdjb2
ตามที่นำเสนอใน stackoverflow โดย cnicutarนั้นเกือบจะดีกว่า แต่ฉันคิดว่ามันก็คุ้มค่าที่จะแสดงแฮชK&Rด้วย:
1) เห็นได้ชัดว่าเป็นอัลกอริธึมแฮชที่แย่มากดังที่นำเสนอใน K&R ฉบับที่ 1 (ที่มา )
unsigned long hash(unsigned char *str)
{
unsigned int hash = 0;
int c;
while (c = *str++)
hash += c;
return hash;
}
2) อาจเป็นอัลกอริธึมแฮชที่ดีดังที่นำเสนอใน K&R เวอร์ชัน 2 (ตรวจสอบโดยฉันในหน้า 144 ของหนังสือ); หมายเหตุ: อย่าลืมลบออก% HASHSIZE
จากคำสั่ง return หากคุณวางแผนที่จะทำ modulus sizing-to-your-length นอกอัลกอริทึมแฮช นอกจากนี้ฉันขอแนะนำให้คุณสร้าง return และประเภท "hashval" unsigned long
แทนการใช้ simple unsigned
(int)
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
(เทมเพลตชุดแฮช) จะเป็นดังนี้
รหัส:
// 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;
}