哈希函數與哈希表

哈希函數與哈希表

1、哈希函數

1.1 哈希函數性質:

  1. input輸入域是無窮的
  2. output輸出域有窮的
  3. 當輸入參數固定的狀況下,返回值必定同樣
  4. 當輸入不同,可能獲得同樣的值。(必然會出現,由於輸入域很大,輸出域很小),產生哈希碰撞
  5. 均勻分佈的特徵,就至關於一個香水噴了房間,讓它自由的布朗運動,那麼房間內就漫步了香水分子

Input計算哈希值後的結果都很大,可是若是把他們都與m取餘,那麼就在一個0-m-1的這個域(hashmap就是這樣找下標的)。若是在S域上是均勻分佈的,那麼在mod上0~m-1也是均勻分佈的。前端

1.2 如何經過一個哈希函數作出1000個相互獨立的哈希函數?

先將一個hash結果拆分兩個(高8位,低8位,是相互獨立的)獲得兩個h1,h2,而後組合,如h1+i*h2 獲得1000個哈希函數。java

2、哈希表

注意每一個下標的鏈表是均勻往下漲的,哈希函數第五點性質

哈希表擴容(能夠認爲擴容代價爲O(1),由於優化技巧不少,實際數學上不是):面試

  1. 假設一個哈希表長度爲17,某個下標的個數爲5,那麼能夠認爲其餘下標也放置了5個節點,假設效率不行,就須要擴容了。如擴容到104,那原來的mod(17)就失效了,
  2. 離線擴容就是原來的哈希表還在使用(用戶不須要等待擴容過程),在後臺拿一個桶來,等數據都導入到新的桶,下一個請求就轉到新的桶。不存在在Java的JVM中(Java是用紅黑樹)。

Java中是怎麼實現的呢?
一個桶,桶裏放的是什麼?不是鏈表而是紅黑樹treemap。是個平衡搜索二叉樹。數組

3、有關題目

3.1 大數據的題

技巧:哈希函數作分流(利用哈希函數相同輸入相同輸出,不一樣輸入均勻分佈的性質)
題目:假設有一個大文件(好比100T),大文件裏每行是個字符串,可是是無序的,把全部重複的字符串打印出來。
假設有1000臺機器,標號,0-999臺機器。大文件存儲在一個分佈式文件上,按行讀取字符串 計算哈希值,mod1000,而後分到1000臺機器,相同的文本必定會分到一臺機器上(相同hash輸入,獲得的結果必定是同樣的)。服務器

3.2 兩個哈希表(實現索引功能)

題目:設計randomPool結構
題目內容:設計一種結構,在該結構中有以下三個功能:
insert(key):將某個key加入到該結構,作到不重複加入
delete(key):將本來在結構中的某個key移除,
getRandom():等機率隨機返回結構中的任何一個key
要求:三個方法的時間複雜度都是O(1)負載均衡

解法:準備兩張hash表(一張hash表沒法作到嚴格等機率隨機返回一個)dom

HashMap<String,Integer> keyIndexMap = new HashMap<String, Integer>();
HashMap<Integer,String> indexKeyMap = new HashMap<Integer, String>();

作法
A 第0個進入hash表 , 表A key A value 0 表B key 0 value A
B 第1個進入hash表 , 表A key B value 1 表B key 1 value B
insert(key)代碼實現:分佈式

public void insert(String key){
    if(keyIndexMap.containsKey(key)){
        return;
    }else{
        keyIndexMap.put(key,number);
        indexKeyMap.put(number,key);
        number++;
    }
}

利用math的random函數,隨機從size取一個數字,在哈希表2取對應數字的key,就是隨機等機率的
getRandom()代碼實現:函數

public String getRandom(){
    if(size ==0){
        return null;
    }
    int index = (int)(Math.random()*size);
    return map2.get(index);
}

若是要remove呢?
直接remove會出現問題:刪除key對應要刪除某個index,那麼就會產生「洞」,調用getRandom就一次調用獲得等機率結果。
那麼該如何去刪呢?
如假設有1000個key,要刪除str17,那麼找到index17, 把str999在keyIndexMap的index變爲17,map2的17改成str999,刪除index999的洞,即產生洞的時候刪除最後一條,再刪除函數須要刪除的key。經過交換最後一行數據保證index是連續的。大數據

public void delete(String key){
    if(keyIndexMap.containsKey(key)){
        Integer deleteIndex = keyIndexMap.get(key);
        int lastIndex = --number;
        String lastKey = indexKeyMap.get(lastIndex);
        indexKeyMap.put(deleteIndex,lastKey);
        keyIndexMap.put(lastKey,deleteIndex);
        keyIndexMap.remove(key);
        indexKeyMap.remove(number);
    }
}

3.3 布隆過濾器(搜索相關的公司幾乎都會問到)

解決的問題:爬蟲去重問題。

黑名單問題(100億個url,每一個url64字節,當用戶搜索某個url的時候,過濾。屬於黑名單返回true,不屬於返回false;用哈希表hashset作的話那麼至少要6400億字節,實際還不止!640G放到內存耗費巨大代價;也能夠用哈希分流給多個機器作,可是須要的機器較多)

布隆過濾器可能 存在較低失誤率:可能會把清白的判斷爲黑名單,可是隻要是黑名單,必會判斷爲黑名單。

所以,若是面試官問這種問題:能夠先用哈希分流的方法回答,再則問面試官是否容許較低失誤率?若是容許的話,採用布隆過濾器。

布隆過濾器:比特數組
如 int[] arr = new int[1000]; 那麼就有32000比特(int 4個字節 32位)
怎麼給某個位的比特抹黑?

int index = 30000; //要描黑的位置
int intIndex = index/32;  //找打數組的下標
int bitIndex = index%32;  //下標對應元素的哪一個位置應該被描黑
arr[intIndex] = (arr[intIndex] | (1<<bitIndex)); //描黑操做

黑名單應該怎麼設計?
思路:url -> 計算哈希值,%m,獲得的結果能夠對應到0~m-1的位置,算到的地方描黑;
此時並非布隆過濾器。
準備hash1,hash2,…,hashk 個哈希函數描黑(可能多個hash函數會到同一個位置,url描黑意味着這個url進到這個布隆過濾器)

比特數組應該儘量大一些,否則小了一下就數組全描黑了

利用布隆過濾器判斷:來一個url,就在這k個hash函數獲得K個位置,若是都是黑的,就是在黑名單,若有一個不是,就不在黑名單內。
解釋:若是url曾經進過,確定都是黑的。有一個位置不是黑的,那確定沒進過,就是白的。
失誤緣由:

  1. 數組長度不夠!致使都是黑的。
  2. 正常非黑名單用戶可能結算的結果也對應在描黑位置
數組空間越大,失誤率越低。

哈希函數好處:數組空間開多大與單樣本的大小無關,而和url的樣本個數有關。

4、認識一致性哈希技術

幾乎集羣都用到這個,須要抗壓服務器(牽扯,服務器設計,負載均衡)

服務器如何進行抗壓的呢?
如前端要存儲
作法:存儲的數據,計算哈希值%後臺服務器數,而後存到對應機器

前端計算分配到後臺服務器的數目會巨均衡。

問題:當想要加機器和減機器就出現問題了,由於%的服務器數目變了。
解決方法:經過一致性哈希就能解決問題,遷移成本低
經過機器的IP或者MAC來計算哈希的位置,劃分哈希值的環(把整個哈希結果想象成一個環)來管理。
存在問題:機器少的時候,不能均分這個哈希的環。有可能只有兩個機器的狀況,兩個機器很近,負載很不均勻!
解決方法:虛擬節點技術

m - 1(分配1000個虛擬節點):m - 1 - 1, m-1-2,m-1-3,m-1-4,.. m - 2:m-2-1,m-2-3,m-2-3,… m - 3… 用這3000個虛擬節點搶這個環。搶到的給對應機器處理。這樣就比較均勻了。幾乎均分這個環。
相關文章
相關標籤/搜索