THE HASH

一篇文章搞懂C++实现哈希算法 哈希算法,也称为散列算法,是一种从任意长度的输入数据创建固定大小输出的方法,这种输出通常被称为“哈希值”、“散列值”或简单地“哈希”。在计算机科学中,哈希算法主要用于快速数据查找和数据结构中的高效数据管理,如哈希表。哈希也是加密和数据完整性验证的基础。哈希算法通过一个称为哈希函数的数学过程来运作。这个函数接受输入(称为“预映射值”)并返回一个通常较短、固定长度的哈希值。这个哈希值在理想情况下为每个不同的输入值提供一个唯一的标识(尽管在实际中这并不总是可能的)。 阅读详情

Abstract

I offer you a new hash function for hash table lookup that is faster and more thorough than the one you are using now. I also give you a way to verify that it is more thorough.

All the text in this color wasn't in the 1997 Dr Dobbs article. The code given here are all public domain.

The Hash

Over the past two years I've built a general hash function for hash table lookup. Most of the two dozen old hashes I've replaced have had owners who wouldn't accept a new hash unless it was a plug-in replacement for their old hash, and was demonstrably better than the old hash.

These old hashes defined my requirements:

  • The keys are unaligned variable-length byte arrays.
  • Sometimes keys are several such arrays.
  • Sometimes a set of independent hash functions were required.
  • Average key lengths ranged from 8 bytes to 200 bytes.
  • Keys might be character strings, numbers, bit-arrays, or weirder things.
  • Table sizes could be anything, including powers of 2.
  • The hash must be faster than the old one.
  • The hash must do a good job.

Without further ado, here's the fastest hash I've been able to design that meets all the requirements. The comments describe how to use it.

Update: I'm leaving the old hash in the text below, but it's obsolete, I have faster and more thorough hashes now. http://burtleburtle.net/bob/c/lookup3.c (2006) is about 2 cycles/byte, works well on 32-bit platforms, and can produce a 32 or 64 bit hash. SpookyHash (2011) is specific to 64-bit platforms, is about 1/3 cycle per byte, and produces a 32, 64, or 128 bit hash.

typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */
typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */

#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)

/*
--------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
For every delta with one or two bits set, and the deltas of all three
  high bits or all three low bits, whether the original value of a,b,c
  is almost all zero or is uniformly distributed,
* If mix() is run forward or backward, at least 32 bits in a,b,c
  have at least 1/4 probability of changing.
* If mix() is run forward, every bit of c will change between 1/3 and
  2/3 of the time.  (Well, 22/100 and 78/100 for some 2-bit deltas.)
mix() was built out of 36 single-cycle latency instructions in a 
  structure that could supported 2x parallelism, like so:
      a -= b; 
      a -= c; x = (c>>13);
      b -= c; a ^= x;
      b -= a; x = (a<<8);
      c -= a; b ^= x;
      c -= b; x = (b>>13);
      ...
  Unfortunately, superscalar Pentiums and Sparcs can't take advantage 
  of that parallelism.  They've also turned some of those single-cycle
  latency instructions into multi-cycle latency instructions.  Still,
  this is the fastest good hash I could find.  There were about 2^^68
  to choose from.  I only looked at a billion or so.
--------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
  a -= b; a -= c; a ^= (c>>13); \
  b -= c; b -= a; b ^= (a<<8); \
  c -= a; c -= b; c ^= (b>>13); \
  a -= b; a -= c; a ^= (c>>12);  \
  b -= c; b -= a; b ^= (a<<16); \
  c -= a; c -= b; c ^= (b>>5); \
  a -= b; a -= c; a ^= (c>>3);  \
  b -= c; b -= a; b ^= (a<<10); \
  c -= a; c -= b; c ^= (b>>15); \
}

/*
--------------------------------------------------------------------
hash() -- hash a variable-length key into a 32-bit value
  k       : the key (the unaligned variable-length array of bytes)
  len     : the length of the key, counting by bytes
  initval : can be any 4-byte value
Returns a 32-bit value.  Every bit of the key affects every bit of
the return value.  Every 1-bit and 2-bit delta achieves avalanche.
About 6*len+35 instructions.

The best hash table sizes are powers of 2.  There is no need to do
mod a prime (mod is sooo slow!).  If you need less than 32 bits,
use a bitmask.  For example, if you need only 10 bits, do
  h = (h & hashmask(10));
In which case, the hash table should have hashsize(10) elements.

If you are hashing n strings (ub1 **)k, do it like this:
  for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);

By Bob Jenkins, 1996.  bob_jenkins@burtleburtle.net.  You may use this
code any way you wish, private, educational, or commercial.  It's free.

See http://burtleburtle.net/bob/hash/evahash.html
Use for hash table lookup, or anything where one collision in 2^^32 is
acceptable.  Do NOT use for cryptographic purposes.
--------------------------------------------------------------------
*/

ub4 hash( k, length, initval)
register ub1 *k;        /* the key */
register ub4  length;   /* the length of the key */
register ub4  initval;  /* the previous hash, or an arbitrary value */
{
   register ub4 a,b,c,len;

   /* Set up the internal state */
   len = length;
   a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
   c = initval;         /* the previous hash value */

   /*---------------------------------------- handle most of the key */
   while (len >= 12)
   {
      a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
      b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
      c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
      mix(a,b,c);
      k += 12; len -= 12;
   }

   /*------------------------------------- handle the last 11 bytes */
   c += length;
   switch(len)              /* all the case statements fall through */
   {
   case 11: c+=((ub4)k[10]<<24);
   case 10: c+=((ub4)k[9]<<16);
   case 9 : c+=((ub4)k[8]<<8);
      /* the first byte of c is reserved for the length */
   case 8 : b+=((ub4)k[7]<<24);
   case 7 : b+=((ub4)k[6]<<16);
   case 6 : b+=((ub4)k[5]<<8);
   case 5 : b+=k[4];
   case 4 : a+=((ub4)k[3]<<24);
   case 3 : a+=((ub4)k[2]<<16);
   case 2 : a+=((ub4)k[1]<<8);
   case 1 : a+=k[0];
     /* case 0: nothing left to add */
   }
   mix(a,b,c);
   /*-------------------------------------------- report the result */
   return c;
}

Most hashes can be modeled like this:

  initialize(internal state)
  for (each text block)
  {
    combine(internal state, text block);
    mix(internal state);
  }
  return postprocess(internal state);

In the new hash, mix() takes 3n of the 6n+35 instructions needed to hash n bytes. Blocks of text are combined with the internal state (a,b,c) by addition. This combining step is the rest of the hash function, consuming the remaining 3n instructions. The only postprocessing is to choose c out of (a,b,c) to be the result.

Three tricks promote speed:

  1. Mixing is done on three 4-byte registers rather than on a 1-byte quantity.
  2. Combining is done on 12-byte blocks, reducing the loop overhead.
  3. The final switch statement combines a variable-length block with the registers a,b,c without a loop.

The golden ratio really is an arbitrary value. Its purpose is to avoid mapping all zeros to all zeros.

The Hash Must Do a Good Job

The most interesting requirement was that the hash must be better than its competition. What does it mean for a hash to be good for hash table lookup?

A good hash function distributes hash values uniformly. If you don't know the keys before choosing the function, the best you can do is map an equal number of possible keys to each hash value. If keys were distributed uniformly, an excellent hash would be to choose the first few bytes of the key and use that as the hash value. Unfortunately, real keys aren't uniformly distributed. Choosing the first few bytes works quite poorly in practice.

The real requirement then is that a good hash function should distribute hash values uniformly for the keys that users actually use.

How do we test that? Let's look at some typical user data. (Since I work at Oracle, I'll use Oracle's standard example: the EMP table.) The EMP table. Is this data uniformly distributed?

EMPNOENAMEJOBMGRHIREDATESALCOMMDEPTNO
7369SMITHCLERK790217-DEC-80800 20
7499ALLENSALESMAN769820-FEB-81160030030
7521WARDSALESMAN769822-FEB-81125050030
7566JONESMANAGER783902-APR-812975 20
7654MARTINSALESMAN789828-SEP-811250140030
7698BLAKEMANAGER753901-MAY-812850 30
7782CLARKMANAGER756609-JUN-812450 10
7788SCOTTANALYST769819-APR-873000 20
7839KINGPRESIDENT 17-NOV-815000 10
7844TURNERSALESMAN769808-SEP-811500 30
7876ADAMSCLERK778823-MAY-871100020
7900JAMESCLERK769803-DEC-81950 30
7902FORDANALYST756603-DEC-813000 20
7934MILLERCLERK778223-JAN-821300 10

Consider each horizontal row to be a key. Some patterns appear.

  1. Keys often differ in only a few bits. For example, all the keys are ASCII, so the high bit of every byte is zero.
  2. Keys often consist of substrings arranged in different orders. For example, the MGR of some keys is the EMPNO of others.
  3. Length matters. The only difference between zero and no value at all may be the length of the value. Also, "aa aaa" and "aaa aa" should hash to different values.
  4. Some keys are mostly zero, with only a few bits set. (That pattern doesn't appear in this example, but it's a common pattern.)

Some patterns are easy to handle. If the length is included in the data being hashed, then lengths are not a problem. If the hash does not treat text blocks commutatively, then substrings are not a problem. Strings that are mostly zeros can be tested by listing all strings with only one bit set and checking if that set of strings produces too many collisions.

The remaining pattern is that keys often differ in only a few bits. If a hash allows small sets of input bits to cancel each other out, and the user keys differ in only those bits, then all keys will map to the same handful of hash values.

A common weakness

Usually, when a small set of input bits cancel each other out, it is because those input bits affect only a smaller set of bits in the internal state.

Consider this hash function:

  for (hash=0, i=0; i<hash; ++i)
    hash = ((hash<<5)^(hash>>27))^key[i];
  return (hash % prime);

This function maps the strings "EXXXXXB" and "AXXXXXC" to the same value. These keys differ in bit 3 of the first byte and bit 1 of the seventh byte. After the seventh bit is combined, any further postprocessing will do no good because the internal states are already the same.

Any time n input bits can only affect m output bits, and n > m, then the 2n keys that differ in those input bits can only produce 2m distinct hash values. The same is true if n input bits can only affect m bits of the internal state -- later mixing may make the 2m results look uniformly distributed, but there will still be only 2m results.

The function above has many sets of 2 bits that affect only 1 bit of the internal state. If there are n input bits, there are (n choose 2)=(n*n/2 - n/2) pairs of input bits, only a few of which match weaknesses in the function above. It is a common pattern for keys to differ in only a few bits. If those bits match one of a hash's weaknesses, which is a rare but not negligible event, the hash will do extremely bad. In most cases, though, it will do just fine. (This allows a function to slip through sanity checks, like hashing an English dictionary uniformly, while still frequently bombing on user data.)

In hashes built of repeated combine-mix steps, this is what usually causes this weakness:

  1. A small number of bits y of one input block are combined, affecting only y bits of the internal state. So far so good.
  2. The mixing step causes those y bits of the internal state to affect only z bits of the internal state.
  3. The next combining step overwrites those bits with z more input bits, cancelling out the first y input bits.

When z is smaller than the number of bits in the output, then y+z input bits have affected only z bits of the internal state, causing 2y+z possible keys to produce at most 2z hash values.

The same thing can happen in reverse:

  1. Uncombine this block, causing y block bits to unaffect y bits of the internal state.
  2. Unmix the internal state, leaving x bits unaffected by the y bits from this block.
  3. Unmixing the previous block unaffects those x bits, cancelling out this block's y bits.

If x is less than the number of bits in the output, then the 2x+y keys differing in only those x+y input bits can produce at most 2x hash values.

(If the mixing function is not a permutation of the internal state, it is not reversible. Instead, it loses information about the earlier blocks every time it is applied, so keys differing only in the first few input blocks are more likely to collide. The mixing function ought to be a permutation.)

It is easy to test whether this weakness exists: if the mixing step causes any bit of the internal state to affect fewer bits of the internal state than there are output bits, the weakness exists. This test should be run on the reverse of the mixing function as well. It can also be run with all sets of 2 internal state bits, or all sets of 3.

Another way this weakness can happen is if any bit in the final input block does not affect every bit of the output. (The user might choose to use only the unaffected output bit, then that's 1 input bit that affects 0 output bits.)

A Survey of Hash Functions

We now have a new hash function and some theory for evaluating hash functions. Let's see how various hash functions stack up.

Additive Hash

ub4 additive(char *key, ub4 len, ub4 prime)
{
  ub4 hash, i;
  for (hash=len, i=0; i<len; ++i) 
    hash += key[i];
  return (hash % prime);
}

This takes 5n+3 instructions. There is no mixing step. The combining step handles one byte at a time. Input bytes commute. The table length must be prime, and can't be much bigger than one byte because the value of variable hash is never much bigger than one byte.

Rotating Hash

ub4 rotating(char *key, ub4 len, ub4 prime)
{
  ub4 hash, i;
  for (hash=len, i=0; i<len; ++i)
    hash = (hash<<4)^(hash>>28)^key[i];
  return (hash % prime);
}

This takes 8n+3 instructions. This is the same as the additive hash, except it has a mixing step (a circular shift by 4) and the combining step is exclusive-or instead of addition. The table size is a prime, but the prime can be any size. On machines with a rotate (such as the Intel x86 line) this is 6n+2 instructions. I have seen the (hash % prime) replaced with

  hash = (hash ^ (hash>>10) ^ (hash>>20)) & mask;

eliminating the % and allowing the table size to be a power of 2, making this 6n+6 instructions. % can be very slow, for example it is 230 times slower than addition on a Sparc 20.

One-at-a-Time Hash

ub4 one_at_a_time(char *key, ub4 len)
{
  ub4   hash, i;
  for (hash=0, 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 & mask);
} 

This is similar to the rotating hash, but it actually mixes the internal state. It takes 9n+9 instructions and produces a full 4-byte result. Preliminary analysis suggests there are no funnels.

This hash was not in the original Dr. Dobb's article. I implemented it to fill a set of requirements posed by Colin Plumb. Colin ended up using an even simpler (and weaker) hash that was sufficient for his purpose.

Bernstein's hash

ub4 bernstein(ub1 *key, ub4 len, ub4 level)
{
  ub4 hash = level;
  ub4 i;
  for (i=0; i<len; ++i) hash = 33*hash + key[i];
  return hash;
}

If your keys are lowercase English words, this will fit 6 characters into a 32-bit hash with no collisions (you'd have to compare all 32 bits). If your keys are mixed case English words, 65*hash+key[i] fits 5 characters into a 32-bit hash with no collisions. That means this type of hash can produce (for the right type of keys) fewer collisions than a hash that gives a more truly random distribution. If your platform doesn't have fast multiplies, no sweat, 33*hash = hash+(hash<<5) and most compilers will figure that out for you.

On the down side, if you don't have short text keys, this hash has a easily detectable flaws. For example, there's a 3-into-2 funnel that 0x0021 and 0x0100 both have the same hash (hex 0x21, decimal 33) (you saw that one coming, yes?).

FNV Hash

I need to fill this in. See it on wikipedia. It's faster than lookup3 on Intel (because Intel has fast multiplication), but slower on most other platforms. Preliminary tests suggested it has decent distributions. It's public domain.

I have three complaints against it. First, it's specific about how to reduce the size if you don't use all the bits, it's not just a mask. Increasing the result size by one bit gives you a completely different hash. If you use a hash table that grows by increasing the result size by one bit, one old bucket maps across the entire new table, not to just two new buckets. If your algorithm has a sliding pointer for which buckets have been split, that just won't work with FNV. Second, it's linear. That means that widely separated things can cancel each other out at least as easily as nearby things. Third, since multiplication only affects higher bits, the lowest 7 bits of the state are never affected by the 8th bit of all the input bytes.

On the plus side, very nearby things never cancel each other out at all. This makes FNV a good choice for hashing very short keys (like single English words). FNV is more robust than Bernstein's hash. It can handle any byte values, not just ASCII characters.

ub4 fnv()
{
  /* I need to fill this in */
}

Goulburn Hash

The Goulburn hash is like my one-at-a-time, but more thorough and slower for all lengths beyond 0, asymptotically over 2x slower. It has two tables, g_table0 and g_table1, of respectively 256 and 128 4-byte integers.

For large hash tables (which is where being more thorough ought to buy you something), it does worse, because its internal operations not reversible. Specifically h^=rotate(h,3) and h^=rotate(h,14), which each cause an even number of bits to be set. I hashed the 232 32-bit integers with lookup3, one-at-a-time, and goulburn, and they produced 2,696,784,567, and 1,667,635,157, and 897,563,758 values respectively. The expected number for a random mapping would be 2,714,937,129 values.

u4 goulburn( const unsigned char *cp, size_t len, uint32_t last_value)
{
  register u4 h = last_value;
  int u;
  for( u=0; u<len; ++u ) {
    h += g_table0[ cp[u] ];
    h ^= (h << 3) ^ (h >> 29);
    h += g_table1[ h >> 25 ];
    h ^= (h << 14) ^ (h >> 18);
    h += 1783936964UL;
  }
  return h;
}

MurmurHash

I need to fill this in too. This is faster than any of my hash, and is more nonlinear than a rotating hash or FNV. I can see it's weaker than my lookup3, but I don't by how much, I haven't tested it.

http://murmurhash.googlepages.com/.

Cessu

The Cessu hash (search for msse2 in his blog) uses SSE2, allowing it to be faster and more thorough (at first glance) than what I can do 32 bits at a time. I haven't done more than a first glance at it.

Pearson's Hash

char pearson(char *key, ub4 len, char tab[256])
{
  char hash;
  ub4  i;
  for (hash=len, i=0; i<len; ++i) 
    hash=tab[hash^key[i]];
  return (hash);
}

This preinitializes tab[] to an arbitrary permutation of 0 .. 255. It takes 6n+2 instructions, but produces only a 1-byte result. Larger results can be made by running it several times with different initial hash values.

CRC Hashing

ub4 crc(char *key, ub4 len, ub4 mask, ub4 tab[256])
{
  ub4 hash, i;
  for (hash=len, i=0; i<len; ++i)
    hash = (hash >> 8) ^ tab[(hash & 0xff) ^ key[i]];
  return (hash & mask);
}

This takes 9n+3 instructions. tab is initialized to simulate a maximal-length Linear Feedback Shift Register (LFSR) which shifts out the low-order bit and XORs with a polynomial if that bit was 1. I used a 32-bit state with a polynomial of 0xedb88320 for the tests. Keys that differ in only four consecutive bytes will not collide.

A sample implementation, complete with table, is here.

You could also implement it like

ub4 crc(char *key, ub4 len, ub4 mask, ub4 tab[256])
{
  ub4 hash, i;
  for (hash=len, i=0; i<len; ++i)
    hash = (hash << 8) ^ tab[(hash >> 24) ^ key[i]];
  return (hash & mask);
}

but, since shifts are sometimes slow, the other way might be faster. If you did it that way you'd have to reverse the bits of the generating polynomial because bits shift out the top instead of the bottom.

Generalized CRC Hashing

This is exactly the same code as CRC hashing except it fills tab[] with each of the 4 bytes forming a random permutation of 0..255. Unlike a true CRC hash, its mixing is nonlinear. Keys that differ in only one byte will not collide. The top byte has to be a permutation of 0..255 so no information is lost when the low byte is shifted out. The other bytes are permutations of 0..255 only to make hold the guarantee that keys differing in one byte will not collide.

A sample implementation, complete with table, is here.

Universal Hashing

ub4 universal(char *key, ub4 len, ub4 mask, ub4 tab[MAXBITS])
{
  ub4 hash, i;
  for (hash=len, i=0; i<(len<<3); i+=8)
  {
    register char k = key[i>>3];
    if (k&0x01) hash ^= tab[i+0];
    if (k&0x02) hash ^= tab[i+1];
    if (k&0x04) hash ^= tab[i+2];
    if (k&0x08) hash ^= tab[i+3];
    if (k&0x10) hash ^= tab[i+4];
    if (k&0x20) hash ^= tab[i+5];
    if (k&0x40) hash ^= tab[i+6];
    if (k&0x80) hash ^= tab[i+7];
  }
  return (hash & mask);
}

This takes 52n+3 instructions. The size of tab[] is the maximum number of input bits. Values in tab[] are chosen at random. Universal hashing can be implemented faster by a Zobrist hash with carefully chosen table values.

Zobrist Hashing

ub4 zobrist( char *key, ub4 len, ub4 mask, ub4 tab[MAXBYTES][256])
{
  ub4 hash, i;
  for (hash=len, i=0; i<len; ++i)
    hash ^= tab[i][key[i]];
  return (hash & mask);
}

This takes 10n+3 instructions. The size of tab[][256] is the maximum number of input bytes. Values of tab[][256] are chosen at random. This can implement universal hashing, but is more general than universal hashing.

Zobrist hashes are especially favored for chess, checkers, othello, and other situations where you have the hash for one state and you want to compute the hash for a closely related state. You xor to the old hash the table values that you're removing from the state, then xor the table values that you're adding. For chess, for example, that's 2 xors to get the hash for the next position given the hash of the current position.

Paul Hsieh's hash

This is kind of a cross between that big hash at the start of this article and my one-at-a-time hash. Paul's timed it and it was than that big hash. It has a 4-byte internal state that it does light nonlinear mixing after every combine. That's good. It combines 2-byte blocks with its 4-byte state, which is something I'd never tried. (FNV and CRC and one-at-a-time combine 1-byte blocks with the 4-byte state. Their input blocks are all smaller than their state, and they mix their state after each input block, which makes it impossible for consecutive input blocks to cancel.)

On the down side, it has funnels of 3 bits into 2, for example hex 01 00 00 00 00 00 00 00 and 00 00 20 00 01 00 00 00 both hash to 0xc754ae23.

#include "pstdint.h" /* Replace with  if appropriate */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif

#if !defined (get16bits)
#define get16bits(d) ((((const uint8_t *)(d))[1] << UINT32_C(8))\
                      +((const uint8_t *)(d))[0])
#endif

uint32_t SuperFastHash (const char * data, int len) {
uint32_t hash = len, tmp;
int rem;

    if (len <= 0 || data == NULL) return 0;

    rem = len & 3;
    len >>= 2;

    /* Main loop */
    for (;len > 0; len--) {
        hash  += get16bits (data);
        tmp    = (get16bits (data+2) << 11) ^ hash;
        hash   = (hash << 16) ^ tmp;
        data  += 2*sizeof (uint16_t);
        hash  += hash >> 11;
    }

    /* Handle end cases */
    switch (rem) {
        case 3: hash += get16bits (data);
                hash ^= hash << 16;
                hash ^= data[sizeof (uint16_t)] << 18;
                hash += hash >> 11;
                break;
        case 2: hash += get16bits (data);
                hash ^= hash << 11;
                hash += hash >> 17;
                break;
        case 1: hash += *data;
                hash ^= hash << 10;
                hash += hash >> 1;
    }

    /* Force "avalanching" of final 127 bits */
    hash ^= hash << 3;
    hash += hash >> 5;
    hash ^= hash << 4;
    hash += hash >> 17;
    hash ^= hash << 25;
    hash += hash >> 6;

    return hash;
}

My Hash

This takes 6n+35 instructions. It's the big one at the beginning of the article. It's implemented along with a self-test at http://burtleburtle.net/bob/c/lookup2.c.

lookup3.c

A hash I wrote nine years later designed along the same lines as "My Hash", see http://burtleburtle.net/bob/c/lookup3.c. It takes 2n instructions per byte for mixing instead of 3n. When fitting bytes into registers (the other 3n instructions), it takes advantage of alignment when it can (a trick learned from Paul Hsieh's hash). It doesn't bother to reserve a byte for the length. That allows zero-length strings to require no mixing. More generally, the length that requires additional mixes is now 13-25-37 instead of 12-24-36.

One theoretical insight was that the last mix doesn't need to do well in reverse (though it has to affect all output bits). And the middle mixing steps don't have to affect all output bits (affecting some 32 bits is enough), though it does have to do well in reverse. So it uses different mixes for those two cases. "My Hash" (lookup2.c) had a single mixing operation that had to satisfy both sets of requirements, which is why it was slower.

On a Pentium 4 with gcc 3.4.?, Paul's hash was usually faster than lookup3.c. On a Pentium 4 with gcc 3.2.?, they were about the same speed. On a Pentium 4 with icc -O2, lookup3.c was a little faster than Paul's hash. I don't know how it would play out on other chips and other compilers. lookup3.c is slower than the additive hash pretty much forever, but it's faster than the rotating hash for keys longer than 5 bytes.

lookup3.c does a much more thorough job of mixing than any of my previous hashes (lookup2.c, lookup.c, One-at-a-time). All my previous hashes did a more thorough job of mixing than Paul Hsieh's hash. Paul's hash does a good enough job of mixing for most practical purposes.

The most evil set of keys I know of are sets of keys that are all the same length, with all bytes zero, except with a few bits set. This is tested by frog.c.. To be even more evil, I had my hashes return b and c instead of just c, yielding a 64-bit hash value. Both lookup.c and lookup2.c start seeing collisions after 253 frog.c keypairs. Paul Hsieh's hash sees collisions after 217 keypairs, even if we take two hashes with different seeds. lookup3.c is the only one of the batch that passes this test. It gets its first collision somewhere beyond 263 keypairs, which is exactly what you'd expect from a completely random mapping to 64-bit values.

MD4

This takes 9.5n+230 instructions. MD4 is a hash designed for cryptography by Ron Rivest. It takes 420 instructions to hash a block of 64 aligned bytes. I combined that with my hash's method of putting unaligned bytes into registers, adding 3ninstructions. MD4 is overkill for hash table lookup.

The table below compares all these hash functions.

NAME

is the name of the hash.

SIZE-1000

is the smallest reasonable hash table size greater than 1000.

SPEED

is the speed of the hash, measured in instructions required to produce a hash value for a table with SIZE-1000 buckets. It is assumed the machine has a rotate instruction. These aren't very accurate measures ... I should really just do timings on a Pentium 4 or such.

INLINE

This is the speed assuming the hash is inlined in a loop that has to walk through all the characters anyways, such as a tokenizer. Such a loop doesn't always exist, and even when it does inlining isn't always possible. Some hashes (my new hash and MD4) work on blocks larger than a character. Inlining a hash removes 3n+1 instructions of loop overhead. It also removes the n instructions needed to get the characters out of the key array and into a register. It also means the length isn't known. Inlining offers other advantages. It allows the string to be converted to uppercase, and/or to unicode, before the hash is performed without the expense of an extra loop or a temporary buffer.

FUNNEL-15

is the largest set of input bits affecting the smallest set of internal state bits when mapping 15-byte keys into a 1-byte result.

FUNNEL-100

is the largest set of input bits affecting the smallest set of internal state bits when mapping 100-byte keys into a 32-bit result.

COLLIDE-32

is the number of collisions found when a dictionary of 38,470 English words was hashed into a 32-bit result. (The expected number of collisions is 0.2 .)

COLLIDE-1000

is a chi2 measure of how well the hash did at mapping the 38470-word dictionary into the SIZE-1000 table. (A chi2 measure greater than +3 is significantly worse than a random mapping; less than -3 is significantly better than a random mapping; in between is just random fluctuations.)

Comparison of several hash functions

NAMESIZE-1000SPEEDINLINEFUNNEL-15FUNNEL-100COLLIDE-32COLLIDE-1000
Additive10095n+3n+215 into 2100 into 237006+806.02
Rotating10096n+32n+24 into 125 into 124+1.24
One-at-a-Time10249n+95n+8nonenone0-0.05
Bernstein10247n+33n+23 into 23 into 24+1.69
FNV1024??????
Pearson102412n+54n+3nonenone0+1.65
CRC10249n+35n+22 into 111 into 100+0.07
Generalized10249n+35n+2nonenone0-1.83
Universal102452n+348n+24 into 350 into 280+0.20
Zobrist102410n+36n+2nonenone1-0.03
Paul Hsieh's10245n+17N/A3 into 23 into 21+1.12
My Hash10246n+35N/Anonenone0+0.33
lookup3.c10245n+20N/Anonenone0-0.08
MD410249.5n+230N/Anonenone1+0.73

From the measurements we can conclude that the Additive and Rotating hash (and maybe Bernstein) were noticably bad for 32-bit results, and only the Additive hash was noticably bad for 10-bit results. If inlining is possible, the Rotating hash was the fastest acceptable hash, followed by Bernstein, Pearson or the Generalized CRC (if table lookup is OK) or Bernstein or One-at-a-Time (if table lookup is not OK). If inlining is not possible, it's a draw between lookup3 and Paul Hsieh's hash. Note that, for many applications, the Rotating hash is noticably bad and should not be used, and the Bernstein hash is marginal. Table lengths should always be a power of 2 because that's faster than prime lengths and all acceptable hashes allow it.

The COLLIDE-1000 numbers should be ignored, unless the numbers are bigger than 3 or less than -3. For example, generalized CRC produced +.8, -.8, or -1.8 for three different tables I tried. It's just noise. A different set of keys would give unrelated random fluctuations.

Conclusion

A common weakness in hash function is for a small set of input bits to cancel each other out. There is an efficient test to detect most such weaknesses, and many functions pass this test. I gave code for the fastest such function I could find. Hash functions without this weakness work equally well on all classes of keys.


Testimonials:

相似图像搜索的哈希算法思想及实现(差值哈希算法和均值哈希算法) 图像相似度比较哈希算法: 什么是哈希(Hash)? • 散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小 的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定 下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums, 或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。 • 通过哈希算法得到的任意长度的二进制值映射为较短的固定长度的二进制 阅读详情

相关推荐

密码学之哈希算法

1 哈希算法 哈希算法是将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性。一般用于快速查找和加密算法。 哈希算法又称为摘要算法,它可以将任意数据...

star_yang的博客 1万+

Hash Functions for Hash Table Lookup

原贴:http://burtleburtle.net/bob/hash/evahash.html Hash Functions for Hash Table Lookup Robert J. Jenkins Jr., 1995-1997 AbstractThis paper presents new hash functions for table lookup usin

最实用的Linux博客 2693

[横向移动] Pass The Hash(PTH,哈希传递)攻击横向移动

而在 Windows 网络中,散列值是用来证明用户身份的(有正确的用户名和密码散列值,就能通过验证),而微软自己的产品和工具显然不会支持这种攻击,所以,通常情况下攻击者会采用第三方工具来完成攻击。在上面那个方法中,我们 PTH 攻击成功后,还得远程桌面到靶机上,然后通过 IPC$ 才能横向移动,既麻烦,动作又大,那么有没有啥简单的方法来通过 PTH 上线内网其它机器呢?当用户需要登录某网站时,如果该网站使用明文的方式保存了用户的密码,那么,一旦该网站出现了安全漏洞,所有用户的明文密码就均会被泄露出去。

Blue17 の 小窝 1340

HASH函数

HASH函数 计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人类”的语言描述单向函数就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很难计算出输入来,这就是单项函数。各种加密函数都可以被认为是单向函数的逼近。Hash函数(或者成为散列函数)也可以看成是单向函数的一个

rongwenbin的专栏 948

ic后端学习笔记-floorplan

floorplan是布图规划,规划包括定义芯片的大小,定义芯片内各种模块、hard macros(硬核。比如RAM,ROM及其他IP模块等)、IO等的大小和位置,有时候还需要提前定义一些placement blockages,groups/regions等等,是后端设计的第一步,也是后续所有P&R工作的基础。floorplan与placement的区别是。

tfrofc的博客 5827

CCS20实战入门:第一个工程搭建示例

通过实际操作演示如何在ccs20中创建并配置首个开发项目,涵盖环境设置与基础调试步骤,帮助新手快速上手ccs20集成开发环境,提升嵌入式开发效率。

weixin_31185473的博客 883

哈希传递攻击(Pass-the-Hash)

目录使用msf进行哈希传递攻击使用mimikatz进行哈希传递攻击PTH(工作组) 使用msf进行哈希传递攻击 有些时候,当我们获取到了某台主机的Administrator用户的LM-Hash和 NTLM-Hash ,并且该主机的445端口(文件共享)打开着。我们则可以利用 exploit/windows/smb/psexec 漏洞用MSF进行远程登录(哈希传递攻击)。(注意:只能是adminis...

whoim_i的博客 1万+

Zemax热分析实战:多重结构编辑器与操作数优化技巧

本文详细介绍了在Zemax中进行热分析的全流程,重点讲解了如何利用多重结构编辑器模拟不同温度环境,并通过TENP、CRVT、THIC等关键操作数建立热膨胀模型。文章结合双胶合透镜实例,阐述了如何设置热跟随、构建多温度评价函数,并运用锤型优化提升光学系统在全温度范围内的热稳定性与成像质量。

yog99的博客 575

The Hash

Abstract I offer you a new hash function for hash table lookup that isfaster and more thorough than the one you are using now. I also giveyou a way to verify that it is more thorough. All the text

wj971hewei的专栏 943

手把手教你用Multisim 14.2仿真一个4.4V的LDO电路(附完整仿真文件)

本文详细介绍了如何使用Multisim 14.2仿真一个4.4V的LDO电路,从环境配置、核心参数解析到电路搭建和仿真调试。通过分压电阻精确计算和瞬态响应测试,帮助读者掌握LDO电路的设计与验证技巧,并附有完整仿真文件供参考。

weixin_28738397的博客 274

Unity插件UniWebView实战:UGUI动态调整内嵌网页尺寸与交互优化

本文详细解析了如何在Unity游戏中使用UniWebView插件实现内嵌网页的动态尺寸适配与深度交互。核心在于利用Reference Rect Transform属性,将网页窗口精准绑定至UGUI控件,并随其动态调整。文章进一步探讨了通过消息系统与JavaScript执行实现Unity与网页的双向通信,并提供了加载优化、过渡动画及事件拦截等实用技巧,助力开发者打造无缝、流畅的游戏内网页体验。

earth的博客 275

Hash函数预览

最先进的非加密散列函数在过去几年中得到了快速推广。当我这周搜索的时候,我很高兴的看到新的尖端散列函数已经发布即使上次我进行这个方面的搜索是6个月到1年前的事情了。 非加密散列函数将字符串作为输入,通过计算输出一个整数。理想的散列函数的一个特性是输出非常均匀分布在可能的输出域,特别是当输入非常相似的时候。不同于加密散列函数,这些函数不是为防止攻击者找出碰撞而设计的。加密散列函数有这个特性但是要慢的

大胖子 860

Altium Designer实战:手把手教你设计智能车竞赛疯狂电路组赛道(附封装库)

本文详细介绍了使用Altium Designer软件进行智能车竞赛“疯狂电路”组赛道设计的全流程实战指南。从理解比赛规则、搭建设计环境、创建专用元器件封装库,到电路布局、路径规划及设计输出验证,手把手教你高效完成符合计分标准的赛道图纸设计,并附上实用的封装库管理方法。

alice7model的博客 1320

【PTH(pass the hash) 哈希传递】

在 pstools 中利用 psexec 可以在远程计算机上执行命令,其基本原理:通过管道在远程目标主机上创建一个 psexec 服务,并在本地生成一个 psexesvc 的二进制文件,然后通过 psexec 服务运行命令,运行结束后删除服务。使用 wmic 连接远程主机,需要目标主机开放 135 端口(wmic 默认的管理端口),445 端口(wmicexec 使用此端口传回显)。在 12server-01 上以管理员权限使用 mimikatz,hash利用上面获取的,成功调出终端。

m0_64237310的博客 1333

六、大模型(LLMs)参数高效微调(PEFT) 面

链接: https://pan.baidu.com/s/1TYqdCK53tDFEYGtAvpnTbA 提取码: 6a9s。考核中的核心考点,并针对这些考点提供了详尽的解答。通过网盘分享的文件:06大模型(LLMs)参数高效微调(PEFT) 面。)有哪些方法,能不能稍微介绍一下它们间?这种微调方法和全参数比起来有什么劣势吗?能不能总结一下各种参数高效微调方法?高效参数微调,如何进行存储?多种不同的高效微调方法对比。模型显存占用的部分有哪些?微调方法为啥能加速训练?微调计算可训练参数的比例。

weixin_51988935的博客 534

【docker】第10篇 Failed with result ‘exit-code和Start request repeated too quickly和Failed to start Docker

【docker】解决 Failed with result 'exit-code'和Start request repeated too quickly和Failed to start Docker。

Fanjufei的博客 6817

pth(pass the hash)哈希传递攻击

pth(pass-the-hash)哈希传递攻击在内网渗透中是一种很经典的攻击方式,原理就是攻击者可以直接通过LM Hash和NTLM Hash访问远程主机或服务,而不用提供明文密码域/工作组环境账号hash内网中存在和当前机器相同的密码(ps:微软打了KB22871997补丁后只有Administrator(SID 500)可以PTH成功,默认windows server 2012后都会存在补丁)

http://www.gov.cn/xinwen/2016-11/07/content_5129723.htm 3386

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Pass-the-Hash哈希传递攻击实战:adsec用Mimikatz免密控制整个域

Pass-the-Hash(哈希传递攻击)是 Windows 域渗透中最经典的横向移动技术,攻击者无需破解密码明文,仅凭一个 NTLM 哈希就能登录远程主机。开源项目 **adsec**(An introduction to Active Directory security)提供了一整套 Active Directory 安全入门课程,包含理论课件、实验环境搭建指南和 8 个循序渐进的实操练习。

gitblog_00192的博客 400

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

拿到一串陌生哈希,3秒认出它是什么:Name-That-Hash 哈希识别工具指南

Name-That-Hash(命令 `nth`)是一个命令行哈希识别工具。给它一串十六进制字符,它会告诉你这串哈希可能是哪种算法、通常用在什么场景、该用 hashcat 或 John 的哪个模式去跑。内置 300 多种哈希类型,适合渗透测试、CTF 选手,以及偶尔要处理凭证字符串的运维人员。 ## 💢 先说痛点 Web 渗透后从数据库里捞出一串 32 位十六进制,看长度可能是 MD5、可能是

gitblog_00996的博客 183

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

HDU - 6768 The Oculus(哈希)

传送门 看到斐波那契数列,以为是矩阵或者公式那种题,但是本题给出的位数太大,一时间没想到怎么写,赛后题解说是哈希才恍然大悟,看来还是自己对哈希不太熟练(还有一个重要原因是这个复杂度遍历竟然不超时,这是无论如何都想不到的) 正解: 令修改的那一位为kkk,则要找到这个kkk满足 A×B=C+F(k)A×B = C+F(k)A×B=C+F(k),因为斐波那契数列第几十项以后就爆long  long long~~long~long  long 了,那么

Happig的博客 311

Name That Hash:现代哈希识别系统的终极解决方案

在网络安全和渗透测试领域,快速准确识别哈希算法是每个安全专家面临的核心挑战。面对超过300种不同的哈希算法,传统的识别工具如HashID(最后更新于2015年)和Hash-Identifier(最后更新于2011年)已经无法满足现代安全需求。Name That Hash应运而生,作为一款2021年推出的现代哈希识别系统,它彻底改变了哈希分析的游戏规则。 ## 🔍 技术挑战:哈希识别的三大痛点

gitblog_00797的博客 775

FNV哈希算法

由来:FNV哈希算法全名为Fowler-Noll-Vo算法,是以三位发明人Glenn Fowler,Landon Curt Noll,Phong Vo的名字来命名的,最早在1991年提出。 特点和用途:FNV能快速hash大量数据并保持较小的冲突率,它的高度分散使它适用于hash一些非常相近的字符串,比如URL,hostname,文件名,text,IP地址等。 算法版本:FNV算法有三个版本:...

weixin_34122810的博客 100

哈希算法---Equations

All the problems in this contest totally bored you. And every time you get bored you like playing with quadratic equations of the form a*X 2 + b*X + c = 0. This time you are very curious to know how m

锅巴 1104

【 哈希算法 】

哈希(Hash)算法就是单向散列算法,它把某个较大的集合P映射到另一个较小的集合Q中,假如这个算法叫H,那么就有Q = H(P)。对于P中任何一个值p都有唯一确定的q与之对应,但是一个q可以对应多个p。作为一个有用的Hash算法,H还应该满足:H(p)速度比较快;给出一个q,很难算出一个p满足q = H(p);给出一个p1,很难算出一个不等于p1的p2使得 H(p1)=H(p2)。数学原理听起来很抽

楚江枫 574

计算与数据结构篇 - 哈希算法 (Hash)

计算与数据结构篇 - 哈希算法 (Hash) 哈希算法的定义和原理非常简单,基本上一句话就可以概括了。将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值。 构成哈希算法的条件: 从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法) 对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大...

stark张宇 1万+

哈希算法

对于哈希算法,在我们平时的开发中,都是基本上拿来就用就行了,所以这节我们将重点放在如何使用,并不进行哈希算法的原理剖析和如何设计一个哈希算法的讲解. 什么是哈希算法? 将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值(散列值).一个优秀的哈希算法需要满足: 从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法)...

Tattoo的博客 4万+
上一篇: SLUB DEBUG原理
下一篇: 从资源池和管理的角度理解物理内存
废言Pro
博客等级 码龄16年 1092粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值