哈希表查找失败的平均查找长度_你还应该知道的哈希冲突解决策略
鏈接:https://mp.weixin.qq.com/s/5vxYoeARG1nC7Z0xTYXELA
作者:Xuegui Chen
哈希是一種通過(guò)對(duì)數(shù)據(jù)進(jìn)行壓縮, 從而提高效率的一種解決方法,但由于哈希函數(shù)有限,數(shù)據(jù)增大等緣故,哈希沖突成為數(shù)據(jù)有效壓縮的一個(gè)難題。本文主要介紹哈希沖突、解決方案,以及各種哈希沖突的解決策略上的優(yōu)缺點(diǎn)。
一、哈希表概述
哈希表的哈希函數(shù)輸入一個(gè)鍵,并向返回一個(gè)哈希表的索引。可能的鍵的集合很大,但是哈希函數(shù)值的集合只是表的大小。
哈希函數(shù)的其他用途包括密碼系統(tǒng)、消息摘要系統(tǒng)、數(shù)字簽名系統(tǒng),為了使這些應(yīng)用程序按預(yù)期工作,沖突的概率必須非常低,因此需要一個(gè)具有非常大的可能值集合的散列函數(shù)。
密碼系統(tǒng):給定用戶密碼,操作系統(tǒng)計(jì)算其散列,并將其與存儲(chǔ)在文件中的該用戶的散列進(jìn)行比較。(不要讓密碼很容易被猜出散列到相同的值)。
消息摘要系統(tǒng):給定重要消息,計(jì)算其散列,并將其與消息本身分開(kāi)發(fā)布。希望檢查消息有效性的讀者也可以使用相同的算法計(jì)算其散列,并與發(fā)布的散列進(jìn)行比較。(不要希望偽造消息很容易,仍然得到相同的散列)。
這些應(yīng)用的流行哈希函數(shù)算法有:
- md5 : 2^128個(gè)值(找一個(gè)沖突鍵,需要哈希大約2 ^ 64個(gè)值)
- sha-1:2^160個(gè)值(找一個(gè)沖突鍵,需要大約2^80個(gè)值)
二、哈希沖突
來(lái)看一個(gè)簡(jiǎn)單的實(shí)例吧,假設(shè)采用hash函數(shù):H(K) = K mod M,插入這些值:217、701、19、30、145
H(K) = 217 % 7 = 0H(K) = 701 % 7 = 1
H(K) = 19 % 7 = 2
H(K) = 30 % 7 = 2
H(K) = 145 % 7 = 5
上面實(shí)例很明顯 19 和 30 就發(fā)生沖突了。
三、沖突解決策略
除非您要進(jìn)行“完美的散列”,否則必須具有沖突解決策略,才能處理表中的沖突。
同時(shí),該策略必須允許查找,插入和刪除正確運(yùn)行的操作!
沖突解決技術(shù)可以分為兩類:開(kāi)散列方法( open hashing,也稱為拉鏈法,separate chaining )和閉散列方法( closed hashing,也稱為開(kāi)地址方法,open addressing )。這兩種方法的不同之處在于:開(kāi)散列法把發(fā)生沖突的關(guān)鍵碼存儲(chǔ)在散列表主表之外,而閉散列法把發(fā)生沖突的關(guān)鍵碼存儲(chǔ)在表中另一個(gè)槽內(nèi)。
下面介紹業(yè)內(nèi)比較流行的hash沖突解決策略:
- 線性探測(cè)(Linear probing)
- 雙重哈希(Double hashing)
- 隨機(jī)散列(Random hashing)
- 分離鏈接(Separate chaining)
上面線性探測(cè)、雙重哈希、隨機(jī)散列都是閉散列法,而分離鏈接則是開(kāi)散列法。
1、線性探測(cè)(Linear probing)
插入一個(gè)值
使用散列函數(shù)H(K)在大小為M的表中插入密鑰K時(shí):
因此,線性探測(cè)基本上是在發(fā)生碰撞時(shí)對(duì)空槽進(jìn)行線性搜索。
優(yōu)點(diǎn):易于實(shí)施;總是找到一個(gè)位置(如果有);當(dāng)表不是很滿時(shí),平均情況下的性能非常好。
缺點(diǎn):表的相鄰插槽中會(huì)形成“集群”或“集群”鍵;當(dāng)這些簇填滿整個(gè)陣列的大部分時(shí),性能會(huì)嚴(yán)重下降,因?yàn)樘结樞蛄袌?zhí)行的工作實(shí)際上是對(duì)大部分陣列的窮舉搜索。
簡(jiǎn)單例子
如哈希表大小M = 7, 哈希函數(shù):H(K) = K mod M。插入這些值:701, 145, 217, 19, 13, 749
H(K) = 701 % 7 = 1H(K) = 145 % 7 = 5
H(K) = 217 % 7 = 0
H(K) = 19 % 7 = 2
H(K) = 13 % 7 = 1(沖突) --> 2(已經(jīng)有值) --> 3(插入位置3)
H(K) = 749 % 7 = 2(沖突) --> 3(已經(jīng)有值) --> 4(插入位置4)
可見(jiàn),如果哈希表如果不是很大,隨著數(shù)據(jù)插入,沖突也會(huì)組件發(fā)生,探針遍歷次數(shù)將會(huì)逐漸變低,檢索過(guò)程也就成為窮舉。
檢索一個(gè)值
如果使用線性探測(cè)將鍵插入表中,則線性探測(cè)將找到它們!
當(dāng)使用散列函數(shù) H(K)在大小為N的表中搜索鍵K時(shí):
問(wèn)題:如何從使用線性探測(cè)的表中刪除鍵?
能否進(jìn)行“延遲刪除”,而只是將已刪除密鑰的插槽標(biāo)記為空?
很明顯,在線性探測(cè)很難做到,如果把位置置為空,那么如果后面的值也是哈希沖突,線性探測(cè)插入,則再也無(wú)法遍歷這些值了。2、雙重哈希(Double hashing)
線性探測(cè)沖突解決方案會(huì)導(dǎo)致表中出現(xiàn)簇,因?yàn)槿绻麅蓚€(gè)鍵發(fā)生碰撞,則探測(cè)到的下一個(gè)位置對(duì)于這兩個(gè)鍵都是相同的。
雙重哈希的思想:使偏移到下一個(gè)探測(cè)到的位置取決于鍵值,因此對(duì)于不同的鍵可以不同。
需要引入第二個(gè)哈希函數(shù) H 2(K),用作探測(cè)序列中的偏移量(將線性探測(cè)視為 H 2(K)== 1 的雙重哈希)。
對(duì)于大小為 M 的哈希表,H 2(K)的值應(yīng)在 1到M-1 的范圍內(nèi);如果M為質(zhì)數(shù),則一個(gè)常見(jiàn)選擇是 H2(K)= 1 +((K / M)mod(M-1))。
然后,用于雙哈希的插入算法為:
哈希表為質(zhì)數(shù)情況,雙重hash在實(shí)踐中非常有效
雙重 Hash 也見(jiàn):https://blog.csdn.net/chenxuegui1234/article/details/103454285
3、隨機(jī)散列(Random hashing)
與雙重哈希一樣,隨機(jī)哈希通過(guò)使探測(cè)序列取決于密鑰來(lái)避免聚類。
使用隨機(jī)散列時(shí),探測(cè)序列是由密鑰播種的偽隨機(jī)數(shù)生成器的輸出生成的(可能與另一個(gè)種子組件一起使用,該組件對(duì)于每個(gè)鍵都是相同的,但是對(duì)于不同的表是不同的)。
然后,用于隨機(jī)哈希的插入算法為:
隨機(jī)散列很容易分析,但是由于隨機(jī)數(shù)生成的“費(fèi)用”,它并不經(jīng)常使用。雙重哈希在實(shí)踐中還是經(jīng)常被使用。
4、分離鏈接(Separate chaining)
在具有哈希函數(shù) H(K)的表中插入鍵K時(shí)
在具有哈希函數(shù)H(K)的表中搜索鍵K時(shí)
使用哈希函數(shù) H(K)刪除表中的鍵K時(shí)
優(yōu)點(diǎn):隨著條目數(shù)量的增加,平均案例性能保持良好。甚至超過(guò)M;刪除比開(kāi)放地址更容易實(shí)現(xiàn)。
缺點(diǎn):需要?jiǎng)討B(tài)數(shù)據(jù),除數(shù)據(jù)外還需要存儲(chǔ)指針,本地性較差,導(dǎo)致緩存性能較差。
很明顯,Java7 的 HashMap 就是一種分裂鏈接的實(shí)現(xiàn)方式。
分離鏈哈希分析
請(qǐng)記住表的填充程度的負(fù)載系數(shù)度量:α = N / M。
其中M是表格的大小,并且 N 是表中已插入的鍵數(shù)。
通過(guò)單獨(dú)的鏈接,可以使 α> 1 給定負(fù)載因子α,我們想知道在最佳,平均和最差情況下的時(shí)間成本。
成功找到
新鍵插入和查找失敗(這些相同),最好的情況是O(1),最壞的情況是O(N)。讓我們分析平均情況
分裂鏈接的平均成本
假設(shè)負(fù)載系數(shù)為 α = N / M 的表
在M個(gè)鏈接列表中總共分配了N個(gè)項(xiàng)目(其中一些可能為空),因此每個(gè)鏈接列表的平均項(xiàng)目數(shù)為:
- 如果查找/插入失敗,則必須窮舉搜索表中的鏈表之一,并且表中鏈表的平均長(zhǎng)度為α。因此,使用單獨(dú)鏈接進(jìn)行插入或不成功查找的比較平均次數(shù)為
- 成功查找后,將搜索包含目標(biāo)密鑰的鏈接列表。除目標(biāo)密鑰外,該列表中平均還有(N-1)/ M個(gè)密鑰;在找到目標(biāo)之前,將平均搜索其中一半。因此,使用單獨(dú)鏈接成功找到的比較平均次數(shù)為
當(dāng)α<1時(shí),它們分別小于1和1.5。并且即使當(dāng)α超過(guò)1時(shí),它們?nèi)匀皇荗(1),與N無(wú)關(guān)。
四、開(kāi)散列方法 VS 閉散列方法
如果將鍵保留為哈希表本身中的條目,則可以使用線性探測(cè),雙重和隨機(jī)哈希... 這樣做稱為“開(kāi)放式尋址”,也稱為“封閉式哈希”。
另一個(gè)想法:哈希表中的條目只是指向鏈表(“鏈”)頭部的指針;鏈接列表的元素包含鍵... 這稱為“單獨(dú)鏈接”,也稱為“開(kāi)放式哈希”。
通過(guò)單獨(dú)的鏈接,沖突解決變得容易:只要在其鏈表中插入一個(gè)鍵,就可以將其插入(為此,可以使用比鏈表更高級(jí)的數(shù)據(jù)結(jié)構(gòu);但是正如我們將看到的,鏈表在一般情況下效果很好)。
讓下面我們看一下這些策略的時(shí)間成本。
開(kāi)放式地址哈希分析
分析哈希表“查找”或“插入”性能時(shí),一個(gè)有用的參數(shù)是負(fù)載系數(shù) α = N / M。
其中 M 是表格的大小,并且 N 是表中已插入的鍵數(shù)負(fù)載系數(shù)是表滿度的一種度量。
給定負(fù)載因子 α ,我們想知道在最佳,平均和最差情況下的時(shí)間成本。
成功找到
對(duì)所有鍵,最好的情況是O(1),最壞的情況是O(N),新鍵插入和查找失敗(這些相同),所以讓我們分析平均情況。
我們將給出隨機(jī)哈希和線性探測(cè)的結(jié)果。實(shí)際上,雙重哈希類似于隨機(jī)哈希;
平均不成功的查找/插入成本
假定負(fù)載系數(shù)為α= N / M的表。考慮隨機(jī)散列,因此聚類不是問(wèn)題。每個(gè)探針位置是隨機(jī)且獨(dú)立生成的對(duì)于每個(gè)探針,找到空位置的可能性為(1-α)。查找空位置將停止查找或插入,這是一個(gè)伯努利過(guò)程,成功概率為(1-α)。該過(guò)程的預(yù)期一階到達(dá)時(shí)間為 1 /(1-α)。所以:
使用隨機(jī)哈希進(jìn)行插入或不成功查找的探針的平均數(shù)量為
使用線性探測(cè)時(shí),探頭的位置不是獨(dú)立的。團(tuán)簇形成,當(dāng)負(fù)載系數(shù)高時(shí)會(huì)導(dǎo)致較長(zhǎng)的探針序列。可以證明,用于線性探測(cè)的插入或未成功發(fā)現(xiàn)的探針的平均數(shù)量約為
當(dāng) α 接近1時(shí),這些平均案例時(shí)間成本很差,僅受M限制;但當(dāng) α 等于或小于7.75(與M無(wú)關(guān))時(shí),效果還不錯(cuò)(分別為4和8.5)
平均成功查找成本
假定負(fù)載系數(shù)為 α= N / M 的表。考慮隨機(jī)散列,因此聚類不是問(wèn)題。每個(gè)探針位置是隨機(jī)且獨(dú)立生成的。
對(duì)于表中的鍵,成功找到它所需的探針數(shù)等于將其插入表中時(shí)所采用的探針數(shù)。每個(gè)新密鑰的插入都會(huì)增加負(fù)載系數(shù),從0開(kāi)始到α。
因此,通過(guò)隨機(jī)散列成功發(fā)現(xiàn)的探測(cè)器的平均數(shù)量為
通過(guò)線性探測(cè),會(huì)形成簇,從而導(dǎo)致更長(zhǎng)的探針序列。可以證明,通過(guò)線性探測(cè)成功發(fā)現(xiàn)的平均探針數(shù)為
當(dāng)α接近1時(shí),這些平均案例時(shí)間成本很差,僅受M限制;但當(dāng)α等于或小于7.75時(shí)好(分別為1.8和2.5),與M無(wú)關(guān)。
總結(jié)
以上是生活随笔為你收集整理的哈希表查找失败的平均查找长度_你还应该知道的哈希冲突解决策略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python 第三方绘图库_D3py首页
- 下一篇: flash一个按钮控制动画_flutte