天天看點

使用位運算來吾日三省吾身

在文章之前我先抛出一個有趣的問題,相信有的同學是看過的,沒有看過的也不打緊兒,文章下面會進行解答。問題如下:

現在有1000個瓶子,裡面999瓶是水,1瓶是毒藥。最少通過多少次的試驗,能确定哪瓶是毒藥。

這個問題你可以思考一下,我先來說一說最近在生活中遇到的另一間相關的事。

前段時間在設計一張資料表的時候遇到了一個小問題。情況是這樣的,一條資料有着三種狀态,這三種狀态是可能疊加并且重複的。什麼意思呢?我舉一個通俗的例子來類比一下。

孔子曰:吾日三省吾身。高乎?帥乎?富乎?高、富、帥可以說是一個人的三種狀态。比如通過「骨骼生長」這個函數的處理,人可以擁有「高」這個狀态;通過「自我打理、運動健身」這個函數的處理,人可以擁有「帥」這個狀态;通過「發奮圖強」這個函數的處理,人可以擁有「富」這個狀态。什麼是疊加?就是一個人可以擁有其中的多種狀态;什麼是重複?比如一個人多次「奮發圖強」,依然會擁有「富」的狀态。

解釋完這些,再回到問題本身。我當時想的是,給每個狀态一個單獨的布爾類型(可以判斷真或假)的字段,然後在進行處理時對相應狀态進行分别判斷。這樣雖然也能完成任務,但是顯得笨重且不優雅。舉兩個顯而易見的例子:

● 假如這條資料将來不止三種狀态,難道需要重新對資料庫進行改造嗎?而且程式也要相應地修改。資料庫作為基礎設施,一旦确定,修改地成本是很高的。

● 因為一條資料可能會經過多次相同類型地處理,假如這樣設計,每次處理前程式都需要知道目前狀态是真是假,處理完再決定是否修改狀态碼。這會導緻程式的備援、不簡潔。

● ...

針對這些問題,X叔(組裡一位我很敬重的老哥)建議我可以用一個籠統的字段

status

,然後每種狀态的值可以設定為1、2、4...這樣。我心想,妙啊。

多重狀态疊加産生的值是不會産生重複的,比如「高富」=3,「高帥」=5,「富帥」=6等等。而且以後有新的狀态,假設新增「健康」這些,到時候,直接約定為相應值8、16等等。

那這和位運算有什麼關系呢?我們來看下面這張圖。

使用位運算來吾日三省吾身

而在重複處理方面,位與運算帶來了更加簡潔的操作。舉個例子,假如目前狀态時是「高帥」,需要再次進行「帥」處理,如果按照傳統的加減方式,需要判斷目前有沒有「帥」這個狀态4,有的話不加,沒有的話加上4。這無疑是非常繁瑣的。

使用位與運算,會簡潔很多,比如:

使用位運算來吾日三省吾身

很友善。在進行位或的時候也是如此,可以結合具體情況試一試。

除此之外,在一些程式設計語言的源碼裡,經常會用到位運算,因為它不僅高效,而且有時候更靈活。比如JDK8裡在優化HashMap的擴容時,将取模運算換成了位運算。可以參考上篇

滴滴好用,但是不安全。HashMap:我也是。

解決了這個問題。回頭看看文章開頭的智力題。

可以先公布一下答案:10。

假設現在有十隻小白鼠,如何10隻小白鼠的生死來找出那一瓶毒藥呢?我們先将1000個瓶子用二進制編号,就像下圖:

使用位運算來吾日三省吾身

從000000001-1111111111,代表1号到1024号(1001-1024個瓶子可以忽視,因為隻需要1000個),可以看到,每個數都有十位,那麼我們讓每隻小白鼠負責一列,将這一列的編号出現1的瓶子中的液體混合着喝下去。如果某隻小白鼠死了那麼可以判定這列為1的所有瓶子中,有一瓶是毒藥。通過十隻小白鼠的生死情況組合來看,不難發現那瓶是毒藥的瓶子編号。

如果你覺得數量太大,有些納悶,可以減少數量,假設總共共有八個瓶子,其中一瓶是毒藥。仔通過這種方式來計算一下。

使用位運算來吾日三省吾身

使用位運算來吾日三省吾身,每天都是4,哎。

原文釋出時間為:2018-09-1

本文作者:寒食君i

本文來自雲栖社群合作夥伴“

位元組流

”,了解相關資訊可以關注“

”。