我為什麼要使用雜湊

2021-09-19 15:54:15 字數 4895 閱讀 5217

雜湊(英語:hashing),通常音譯作雜湊,是電腦科學中一種對資料的處理方法,通過某種特定的函式、演算法將要檢索的項與用來檢索的索引關聯起來,生成一種便於搜尋的資料結構。也譯為雜湊。

-- from 雜湊, wikipedia

實際上通俗的說法就是把某種狀態或者資料給對映到某個值上的操作。

本醬大概就解釋到這裡了,至於雜湊的進一步認知包括衝突的產生和解決等,如果公尺娜桑不了解的話還請自行學習咕。థ౪థ

這個不是我在實踐中遇到的問題,而是當年去某不作惡的大廠面試時候遇到的問題,覺得比較經典,所以就拿出來了。ᕙ༼ຈل͜ຈ༽ᕗ

給定一棵二叉樹,假設每個節點的資料只有左右子節點,自身並不儲存資料。請找出兩兩完全相等的子樹們。

有興趣的童鞋可以自己先思考一下。₍₍◝(・'ω'・)◟⁾⁾

實際上我也不知道自己的做法是不是正確做法,不過既然通過了那一輪面試,想來也不會偏差到哪去喵。ლ(╹ε╹ლ)

做法大概如下:

後序遍歷一遍整棵樹。

對於遍歷到每乙個節點,都獲取到左右子節點的雜湊值,然後將其拼接重新計算出自身的雜湊值,並返回給父親節點。

至於雜湊值怎麼算,方法有很多。最簡單的就是設葉子節點乙個雜湊值,比如是md5(""),然後每次非葉子節點的雜湊值就用md5(left_hash + right_hash)來計算。大家也可以自己隨便想一種方法來做就好了。

很多人可能不解了,明明是用md5,這篇文章是講雜湊,有毛線關係。(╯°o°)╯┻━┻

實際上md5就是一種雜湊演算法,而且是非常經典的雜湊演算法。

典型的雜湊演算法包括 md2、md4、md5 和 sha-1 等。當然不侷限於這些,對於數字來說,取模也算是雜湊演算法,對於字串狀態轉整數狀態雜湊來說還有諸如 bkdr、elf 等等。

如果大家想多了解一些字串轉數字雜湊的演算法,可以參考一下 byvoid 的這篇《各種字串hash函式比較》,或者想直接在 node.js 裡面使用的小夥伴們可以光顧下這個包——bling-hashes。

初步的輪廓已經明晰了,說白了就是將每個節點的雜湊全算出來,如果是父親節點就用子節點的雜湊拼接起來再雜湊一遍。σ`∀´)σ

把這些雜湊算出來之後放在乙個雜湊表裡面待查。如果乙個算出來的雜湊跟之前已有的雜湊值相等,那麼就是說這個節點跟那個節點為根節點的子樹有可能完全相等。

注意:有可能完全相等。

注意:只是有可能完全相等。

注意:重要的事情說三遍,只是有可能完全相等。

雜湊是存在著一定的衝突概率的,所以說兩個相等的雜湊所檢索到的源不一定一樣,所以我們根據這些計算到的雜湊建立雜湊表,然後把表中同雜湊值的子樹再兩兩同時遍歷一遍以檢驗是否相等。

同時遞迴,取兩個子樹的根節點。

後序遍歷,看看每個節點是不是都一樣存在(或者不存在)左子節點以及存在(或者不存在)右子節點。

迴圈往復一直到兩兩遍歷完整棵樹得到驗證結果。如果半路有乙個節點的左右子節點狀態不一樣就可以直接跳出遞迴返回false

至此為止,我們可以看出大概是兩大步——計算各子樹的雜湊值驗證各同雜湊子樹的相等性。不過稍微變通一下,我們就可以在計算出雜湊值的時候就去跟以前的對比了。

剪枝實際上上面的做法還有乙個優化的方案,不過跟雜湊相關性已經基本上很小了。不過還是跟解決衝突有一丟丟的關係的,沒興趣的童鞋也可以直接跳過了。(๑•́ ₃ •̀๑)

由於子樹雜湊值是存在一定的衝突概率的,所以兩個同雜湊的子樹不一定相同。那麼我們如果能一眼看出這樣的兩棵子樹是不相等的,就可以省略驗證這乙個遞迴的步驟了。

這裡有一種最顯而易見的情況我們是可以忽略省略步驟的,那就是深度。

如果兩棵子樹兩兩完全相等,那麼說明這倆基佬的深度(或者說高度)是一樣的,如果連深度都不一樣了還如何愉快搞基——所以說如果有兩個相等雜湊值的子樹的深度不一樣的話可以直接略過驗證步驟了。

那麼就可以這麼做:

設所有葉子節點的深度為0,然後每往上一層加一。

遇到左右子節點深度不一樣的父節點時,取深度大的那個子節點深度去加一。

以上步驟在遍歷計算雜湊的時候順便也做了,這樣就多了乙個驗證標記了。

所以差不多就這樣了,淺嘗輒止。( ˘・з・)

就上述的場景來說,雜湊非常好地將乙個非常複雜的狀態轉化成乙個可以檢索的狀態。本來毫無頭緒的乙個問題使用了雜湊之後就完全變成了乙個檢索加驗證的過程了。

這個問題就是我在大搜車中確實遇到的場景了。大家也不需要知道什麼是報告圖,就當它是乙個代號了。

要做的事情大概就是說給定乙個報告,我們根據報告的各個細節選定各種圖層然後揉成一團疊加在一起形成最後乙個結果圖。

其實本來就有個系統在做這件事情的——每來乙個報告就生成一張圖,然後儲存好之後給前端使用。

我做的事情是將邏輯遷移到另一套計算密集型任務集中處理系統中去。(´艸`)

其實生成這樣一張的邏輯是 cpu 計算密集型的邏輯,所以比較耗費資源和時間的,那麼我們就能在這上面做點手腳優化一下。

首先我們要知道的是,有哪些圖層是固定的,所以其實這算半個排列組合的問題了。

不過我們也知道排列組合的增長性非常快,更何況我這裡有約 100 個圖層選擇,所以可能性非常多,一下子全生成好不可能。

那麼就可以用雜湊和懶惰的思想來實現了。(ˇωˇ人)

雖然報告是有無限種可能的,但是把報告轉成圖層資料之後,擁有完全一樣的圖層資料的報告就可以用同一張了,這樣就可以大大節省空間和時間了。

其實大概的步驟非常簡單:

把圖層資料計算成雜湊。(比如把所有圖層檔案路徑用某種符號拼接,再用md5計算一下)

去資料庫查詢這個雜湊主鍵存不存在。

如果不存在就說明當前資料庫還沒有這個圖層情況的報告圖生成,那麼就執行生成報告圖邏輯。

報告圖生成之後,將其存入資料庫中。

如果大家想知道「按某種演算法重新生成雜湊」裡面「某種演算法」的話可以看看下面的瞎狗眼的說明了。(ノ◕ヮ◕)ノ*:・゚✧

其實很簡單,把圖層資料的這個字串加某個固定字元當小尾巴,如果雜湊還是衝突則繼續加這個小尾巴,直到計算出來的雜湊不衝突為止。

比如我就用了這字元當小尾巴——?(麻將牌中的蘭)。(♛‿♛)

在這種場景中,我把雜湊拿來作檢索某種報告圖是否已經生成的用途。如果沒有生成則生成一張,如果已經生成則直接拿已有的報告圖去用。

至少比原來的來一張報告就生成一張來得快,並且省空間——相當於作冗餘處理了。

事實上在很多的網盤系統中也有作冗餘處理的。你以為你有多少多少 t 的空間,實際上相同的檔案最終在網盤系統裡面只存乙份(不過排除備份的那些),而我相信做這些冗餘判斷的原理就是雜湊了,sha-1 也好 md5 也好,反正就是這樣。

上面網盤的冗餘處理原理也只是我的猜測,我沒在那些廠子裡面工作過所以不能說就是就是這樣子的。歡迎指正。。゚ヽ(゚´д`)ノ゚。

這是我來這邊工作後的另乙個小插曲了,遇到乙個主鍵生成的小需求。

有乙個資料要插入到資料庫,所以要給它生成乙個主鍵,但是需求比較奇葩,可能是歷史遺留問題吧。(눈‸눈)

如果是字首 + 隨機數的衝突概率會比較大的,所以還是用雜湊來搞。

非常簡單。首先字首是固定的,我們就不管了,然後我根據這次進來的資料拼接成字串(資料不會完全一樣的),加上一點隨機鹽,然後用字串雜湊計算一遍,加上前導零,加上當前時間戳的後幾位拼接起來,最後接上字首就好了。

這個generate函式看起來就像這樣子:

var bling = require("bling-hashes");

function generate(type, bodyparamstr)

var date = moment();

var hash = bling.bkdr(bodyparamstr + date.valueof()).pad(10);

hash = date.millisecond().pad(3) + hash;

return baseprefix + hash;

};

注意:這裡的bling就是上面提到過的那個 bling-hashes,採用了bkdr演算法來計算雜湊。以及number.prototype.pad函式是我**得使用了 sugarjs 裡面的函式,就是加上前導零的意思。如果受「千萬不要修改原型鏈」影響較深地童鞋別學我哦。bodyparamstr是前端傳過來的raw form data,它看起來像"data1=1&data2=2&..."

最後得到的這個字串是我們所要的主鍵了。。:.゚ヽ(*´∀`)ノ゚.:。

不過要注意的是,這個主鍵仍然又衝突的可能性,所以一旦衝突了(無論是自己檢測到的還是插入資料庫的時候疼了)就需要再生產一遍。就目前來說再生成的時候毫秒時間戳後三位會不一樣,所以問題不大,允許存在的誤差——畢竟不是那種分分鐘集千萬條的資料,肯定在int範圍內。如果到時候真出問題了再改進。

這裡的雜湊是用在生成基本上沒有碰撞的主鍵身上,感覺效果也是非常不錯的——前提是你也有這種奇葩需求。

在程式設計中,無論是實際用途還是自己玩玩的題目,多動動腦子就會出來一些「奇技淫巧」。雜湊也好,別的東西也罷,反正都是為了解決問題的——千萬別因為實際開發中通常性的「並沒有什麼卵用」而去忽視它們,雖然雜湊已經是夠常用的了。(๑•ૅω•´๑)

為什麼要使用blog

有哥們問我,你為什麼使用blog?我總結了一下,覺得有如下幾個原因。1對自己的督促 有了blog,就會經常記得寫點東西 就會經常翻翻網上的新文章,了解一下新技術,不至於迷失在忙碌的生活中 如果把自己的所感所想所學寫出了,自己對自己也會有個概念,不至於迷迷糊糊 還有,畢竟是掛在網上的文字,心中難免擔心...

為什麼要使用XML

xml 代表擴充套件標記語言 extensible markup language 是由 world wide web consortium w 3c 的 xml工作組定義的。這個工作組是這樣描述該語言的 擴充套件標記語言 xml 是 sgml 的子集,其目標是允許普通的 sgml 在web 上以目...

為什麼要使用Nginx?

有人說這些基準測試是不準確的,因為在這樣那樣的環境下,做的比較不一致。我傾向同意基準測試只是告訴了我們其中一部分情況,你能做的是消除偏見 有人見過所有人都同意乙個基準測試是公平的嗎?我是沒見過。我們投資的一些公司把web平台切換到nginx後,可以顯著的解決擴充套件問題。nginx明顯有效的實現了今...