c++ - 算法题:怎么区分互不相同的<a, b>数对,hash还是?
迷茫
迷茫 2017-04-17 11:23:00
0
5
551

问题:有N个数对<a, b>,其中a的范围是[0, 100],b的范围是[0, 255],N < 10000,如何区分这N对数对?

我的需求最好是利用a和b的值通过某种算法产生一个数,每个数对产生不一样的值,这样就能相互区分了。

比如有下列这些数对:
<1, 2>
<2, 3>
<2, 1>
<7, 76>
<4, 2>
<45, 123>
...
等等,怎么设计一个利用a和b设计一个算法,来区分这些数对,比如a + b,当然我只是举个例子,简单的加减乘除都不行,因为有些数对比如<2, 1>与<1, 2>就不能区分了。

如果区分不了,那么能不能设计一个hash算法,使得这些数对全部映射到[0, M]之间,M最好<5000,这样就可能有不能区分的了,没关系,只要使得冲突最小也行。

重点:这样的需求不好解决问题,因为<a, b>是随机的,没什么分布规律。但<a, b>有一些分布特点:如果同一个a,b非常有可能是分段连续的,比如像下面这样:
<2, 1>
<2, 2>
<2, 3>
<2, 4>
<2, 5>
<2, 124>
<2, 125>
<2, 126>
<2, 127>
<2, 128>
<2, 129>
<2, 130>
...

我的要求:尽可能时间高效,比如用hash,一下就能判断。

当然有人会想到用最简单的map<int, int> m,m的大小就是数对的个数,但显然这样的方向时空性能都不太高,而且没有利用a和b的某些规律。

请大家发挥自己的想象力吧~~~

迷茫
迷茫

业精于勤,荒于嬉;行成于思,毁于随。

全部回覆(5)
阿神

101*256 > 5000 所以M

那麼怎麼設計衝突最小的hash演算法呢? 這就牽涉到題主的資料的分佈情況了,那麼姑且先認為分佈是均勻的。

均勻分佈的情況其實這個演算法很容易設計,直接取餘即可 hash = (a * 256 + b) % M

依照上述hash演算法,我們來算隨機兩個不同數對a1, b1a2, b2hash衝突的機率

容易證明 a*256 + b這個數值在題主的ab範圍內和數對是一一對應的,且正好是[0, 25600+255]

那麼這個衝突的機率也就等價於[0, 25855]内两个不同的随机整数对M的同余的概率

沒有公式編輯器,機率論的東西也差不多還給老師了,後面的計算和證明就省略了,總之,題主的問題在平均分佈的假設下直接取餘就是衝突最小的hash演算法了。較普適的諸如md5sha1等hash演算法主要是在輸入長度不固定,不一定平均分佈的情況下,最小化衝突為目標設計的


根據題主更新的分佈,我建議M的取值選257的倍數,這樣可以保證<x, 0-255><0-100, y>一定不衝突,不過會有<x+1, y+1>一定和<x,y>衝突的問題,如果要保證“斜著走不衝突”,還需要設法映射一下

Ty80

演算法:兩步hash,你的數是很規範的,a和b的範圍都是給定的。算全域hash,a也只有100個hash值,b有250個,所以總共只有100×250=25000個hash值,超過你的

具體設計:先給a設計映射函數,可以映射到a到[0,50)上,映射b到[0,99]上,這樣總共有50*100=5000的需求。

另外,你的數字還有一個規律,在a相同的情況下,會出現b的連續值,這個在設計b的hash函數時可以利用起來,hash(b)=b%100就能夠比較好的把連續的b映射到不同的key。但你要是用了取高位的hash函數,就會把連續b映射到相同的key上,衝突會比較大。

update:

其是你這個完全可以用bitmap來做。每個映射到一個a256+b的整數上,這樣實際總共是[0,256101],可以設定一個bitset101+1>就可以了,每一個存在就設定bitset[256a+b]為1,總共需要空間也不大。訪問也是常數時間的。

巴扎黑

有個O(n)的演算法,也有個O(2)的演算法,直接就能夠定位。

這不就是個二維向量麼,求向量模做hash,產生衝突的時候根據方向來決定取previous還是next。

舉例:
1. 準備一個一維數組存node,這個node有兩個指針,一個previous,一個next,然後剩下的部分存
2. 針對給定的(假設是這個)取向量模=5=hashkey,根號計算比較耗時間,那就不用開根號,我們的'hashkey'就=25.
3. 隨便你用什麼hash方法去算一維數組的index,我比較喜歡取模,那麼就是 'hashkey%數組長度' 得到一個index.
4. 如果對應的index裡面是個空node,那麼就把這個node放進去,如果不是空的,那就算一下向量方向'4-3 > 0',方向大於0的node就用next去指,方向小於0的就用previous去指。
5. 然後根據上一次計算出來的方向不斷訪問previous或next,直到找到就好。

因為有了方向,其實尋找到那個node就很快了,比一般的hash演算法都能快很多。因為產生碰撞之後,另一個方向的node list直接就可以不用遍歷了。

如果模和方向都一樣,很容易就找到相同的node了。

O(2)的演算法就是基於上面的再最佳化一下。可以根據向量斜率再來一輪hash,那麼第二步直接就定位到唯一向量了,不會有衝突(模長相同且斜率相同的向量是相同向量,而且題設有個隱含條件,所有向量的起點都是原點。要真出現衝突,那就恭喜,你找到重複的node了。不過我覺得一輪hash也差不多就夠了。一次遍歷之後所有重複的node就都能找到了。

最終它大概長這個樣子:
--------| |
| | |
--------| | (假設有,我懶得去湊數字了。。)

橫線表示空格,沒有任何意義

例如給一個,你會發現5對應的位置有了,然後你看方向,小於0,那麼就取那個node的previous指針往上找,誒,瞬間就找到了~因為有了方向,直接就不用比較了。

小葫芦

確實如上所說 hashed = a*256+b 但是樓上兩位和題主似乎忽略了一個問題,有10000個數,但是hash範圍只有5000,這衝突率不是200%麼?

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板