1. 程式人生 > 實用技巧 >Hash演算法有哪些?

Hash演算法有哪些?

轉載:https://www.jianshu.com/p/958eb591ecd6

Hash演算法的有哪幾種,優缺點,使用場景

Hash ,一般叫做雜湊演算法,就是把任意長度的輸入通過雜湊演算法,變換成固定長度的輸入,相當於一種壓縮對映,將任意長度的訊息壓縮到某一固定長度的訊息摘要的函式。

• 加法Hash;把輸入元素一個一個的加起來構成最後的結果

/**
  * 加法hash
  *
  * @param key
  *            字串
  * @param prime
  *            一個質數
  * @return hash結果
  */
public static int
additiveHash(String key, int prime) { int hash, i; for (hash = key.length(), i = 0; i < key.length(); i++) hash += key.charAt(i); return (hash % prime); }

• 位運算Hash;這型別Hash函式通過利用各種位運算(常見的是移位和異或)來充分的混合輸入元素

/**
  * 旋轉hash
  *
  * @param key
  *            輸入字串
  * @param prime
  *            質數
  * 
@return hash值 */ public static int rotatingHash(String key, int prime) { int hash, i; for (hash = key.length(), i = 0; i < key.length(); ++i) hash = (hash << 4) ^ (hash >> 28) ^ key.charAt(i); return (hash % prime); // return (hash ^ (hash>>10) ^ (hash>>20)); }
• 乘法Hash;這種型別的Hash函式利用了乘法的不相關性(乘法的這種性質,最有名的莫過於平方取頭尾的隨機數生成演算法,雖然這種演算法效果並不好);
static
int bernstein(String key) { int hash = 0; int i; for (i=0; i<key.length(); ++i) hash = 33*hash + key.charAt(i); return hash; }

jdk5.0裡面的String類的hashCode()方法也使用乘法Hash;32位FNV演算法

int M_SHIFT = 0;
public int FNVHash(byte[] data) {
  int hash = (int) 2166136261L;
  for(byte b : data)
    hash = (hash * 16777619) ^ b;
  if(M_SHIFT == 0)
    return hash;
  return (hash ^ (hash >> M_SHIFT)) & M_MASK;
}

改進後的 FNV 演算法

public static int FNVHash1(String data) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < data.length(); i++)
            hash = (hash ^ data.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        return hash;
    }

常見的還有乘以一個不斷改變的數

static int RSHash(String str) {
        int b = 378551;
        int a = 63689;
        int hash = 0;
        for (int i = 0; i < str.length(); i++) {
            hash = hash * a + str.charAt(i);
            a = a * b;
        }
        return (hash & 0x7FFFFFFF);
    }

• 除法Hash;除法和乘法一樣,同樣具有表面上看起來的不相關性。不過,因為除法太慢,這種方式幾乎找不到真正的應用

• 查表Hash;查表Hash最有名的例子莫過於CRC系列演算法。雖然CRC系列演算法本身並不是查表,但是,查表是它的一種最快的實現方式。查表Hash中有名的例子有:Universal Hashing和Zobrist Hashing。他們的表格都是隨機生成的。

• 混合Hash;混合Hash演算法利用了以上各種方式。各種常見的Hash演算法,比如MD5、Tiger都屬於這個範圍。它們一般很少

  • 陣列 hash
inline int hashcode(const int *v)
{
  int s = 0;
  for(int i=0; i<k; i++)
    s=((s<<2)+(v[i]>>4))^(v[i]<<10);
  s = s % M;
  s = s < 0 ? s + M : s;
  return s;
}

在面向查詢的Hash函式裡面使用

環 hash

環 hash 計算步驟

  1. 首先求出雜湊值, 並將其分配到 0~2^32 的圓上,其實把機器編號 hash 到這個環上。
  2. 採用同樣的方法求出儲存資料鍵的雜湊值,並對映到相同的圓上
  3. 然後從資料對映到位置開始順時針開始找,將資料儲存到找到的第一個伺服器,如果 2^32 仍然找不到伺服器,就會儲存到第一臺伺服器上。
圖片.png

環 hash 存在的問題

資料傾斜問題

資料傾斜是指,當機器不多時,幾臺機器在環上面貼的很近,分佈是不是很均勻,會導致大部分資料集中到這幾臺機器上,這樣就產生了資料傾斜問題。

如何解決資料傾斜問題?

引入了虛擬機器器概念,一臺機器需要在環上映射出多個這個位置,比如 我們用機器的 ip 來 hash ,這樣就實現了一臺物理機映射出多個虛擬機器器的編號。

參考資料