位圖與布隆過濾器

一如叼、問題引入

先來思考這樣一個問題:假如給你20億個數(shù)字,范圍大小是 1- 20億妻熊,需要你把這些數(shù)字存儲起來,然后再隨機給定一個數(shù)字仑最,判斷其是否存在這20億個數(shù)字中扔役, 你怎么解決。
首先如果不考慮其個數(shù)多少的話警医,我們首先想到的可能就是使用一個set集合亿胸,用來存儲數(shù)字集合,然后直接判斷數(shù)字是否存在预皇,因為其底層使用了hash的數(shù)據(jù)結構侈玄,判斷給定數(shù)字是否存在也是O(1) 的時間復雜度。但是當這個數(shù)字來到10億吟温,乃至更大的時候序仙,如果還是用set集合,可能會極大得浪費我們的存儲空間鲁豪,并且可能會引發(fā)OOM(我們可以大概估算一下潘悼,java中,一個int類型的數(shù)字是4個字節(jié)爬橡,取值范圍:-2147483648~+2147483647治唤。那么這20億個數(shù)據(jù)如果我們用int類型表示,那么占用內(nèi)存就是 4 * 20億 = 80億字節(jié)糙申,約等于8個G)宾添。這個時候,我們需要怎么解決這個問題呢柜裸?
我們主要解決的是占用內(nèi)存過大的這個問題缕陕,那我們想,有沒有比字節(jié)更小的單位呢粘室,沒錯了榄檬,就是bit 位。 我們知道衔统,計算機中鹿榜,最原始都是使用二進制表示,int類型有32bit位锦爵,如果我們用每個bit位表示一個數(shù)字舱殿,那么一個int類型就能夠存儲32個數(shù)字,而且題干中剛好只要求我們能夠判斷目標數(shù)字是否存在险掀,那我們只需要對應的bit位上存儲 1 或者 0沪袭,用來表示存在或者不存在。這么算下來樟氢,原本 約8個G的存儲 / 32,也就是200多M冈绊,就可以解決這個問題侠鳄。

二、思路歸納

為了方便計算死宣,我們暫且將 數(shù)字縮小化便于理解伟恶。
比如給定的數(shù)字集合,最大是130∫愀茫現(xiàn)在我們向集合中添加128博秫,129這兩個數(shù)字,具體應該怎么添加呢眶掌?
1挡育、首先要確定這個集合怎么表示,我們可以使用int類型的數(shù)組(當然你也可以使用Long類型的朴爬,這里只是舉例)即寒,因為int類型占用4個字節(jié),32位召噩,所以一個int可以表示32個數(shù)字蒿叠,也就是int[0] 可以表示0-31,int[1]表示32-63蚣常,依次類推。
2痊银、有了數(shù)組抵蚊,那么我們就要確定給定的數(shù)字位于數(shù)組的下標位置。我們可以用 (130 / 32 )+1 = 5 來表示下標位置溯革。為什么數(shù)組長度需要+1呢贞绳,因為4個長度的int數(shù)組只能表示0-127,所以多出來的幾個數(shù)字致稀,只能數(shù)組長度再加一冈闭。
3、有了下標抖单,還需要找到數(shù)組下標中的bit位置萎攒。128 % 32 = 0 來表示在第幾個bit位。
向int數(shù)組中添加128矛绘,


128

向int數(shù)組中添加129耍休,


128,129

有了如上添加數(shù)字將對應bit位置置為1的邏輯,接下來給定一個數(shù)字货矮,判斷這個數(shù)字是否存在于集合內(nèi)羊精,就只要判斷該數(shù)字對應的big位上 的值是否等于1 即可。
比如給定100囚玫,首先找到數(shù)組下標 index = 100 / 32 = 3, num = 100 % 32 = 4, 即只要判斷bigmap[2]的第四個bit位上是否等于1喧锦,等于1读规,說明存在,不等于1燃少,則不存在束亏。

三:布隆過濾器

所謂位圖,就是使用位這個最小的存儲單元來儲存供汛,針對于Integer4個字節(jié)枪汪,根據(jù)位的高低來存儲不同大小的數(shù)字。正常來說怔昨,20億個正整型數(shù)字雀久,數(shù)組來存,需要20億的長度趁舀,但是如果用位來存赖捌,一個Integer占32位,也就是數(shù)組中的一個Integer可以表示32個數(shù)字矮烹,那么就整整縮小了32倍的數(shù)組長度越庇。
問題一:
我們想,現(xiàn)在只是針對數(shù)字奉狈,如果我們把范圍擴大到其他類型卤唉,如果是字符串呢,那么我們就需要先對目標進行哈希運算仁期,然后再計算其數(shù)組下標桑驱。
問題二:
同時我們的哈希表(數(shù)組)長度總是有限的,如果產(chǎn)生哈希沖突跛蛋,那么最后判斷哈希表中的值熬的,就會不準確。那么怎么解決呢赊级?通過設置多個哈希函數(shù)押框,同時將多個位上的值置為1。當判斷一個key所對應的值存不存在時理逊,通過多個哈希函數(shù)得到的數(shù)組下標所對應的值都為1時橡伞,才能確定這個key是存在的,否則不存在挡鞍。這樣當然也可能會存在誤判骑歹,不過我們可以根據(jù)業(yè)務場景的容忍度,通過設置哈希表的大小和哈希函數(shù)的個數(shù)來調(diào)整誤判的概率墨微。redis中的布隆過濾器也就是這么做的道媚。

java的位圖實現(xiàn):

/**
 * @author zhangxiaohu
 * @descript int整數(shù)位圖
 * @date 2022-10-12
 */
public class BitMap {

    private int[] bitMap;
    /**
     * java中int 占用4個字節(jié),共32位
     * 如果最大數(shù)值是 128,那么bigMap 數(shù)組的長度就是 4(占用128位)最域,如果用普通的方式存儲谴分,最大值是128,那么我們的 數(shù)組長度就是128,相比之下镀脂,內(nèi)存空間減少了32倍
     * @param range
     */
    public BitMap(int range) {
        /**
         * range >> 5 = range / 32
         * 這里為什么要 + 1呢牺蹄,這是因為,比如最大值是128薄翅,數(shù)組長度是4沙兰,最多只能存儲到 0 - 127,所以存儲128數(shù)組長度要再+1
         * 數(shù)組長度+1之后翘魄,所以range = 128鼎天,但是我們可以表示的最大值應該是  128 + 31 = 159
         */
        bitMap = new int[(range >> 5) + 1];
    }

    public void set(Integer number) {
        /**
         * number >> 5 = number / 32
         * 假如number  =  128
         * index = 4  表示在數(shù)據(jù)的最后一個位置,也就是bitMap[3]
         * num = 0 表示在bitMap[3]上的第0位(bitMap[3] 上有32位)暑竟,也就是: 00000000 00000000 00000000 00000001
         */
        int index = number >> 5;
        int num = number % 32;
        /**
         * 1 << num = 1 * 2的num次冪,保證相應位置上的值無論為0還是1斋射,都變成1
         * 這里為什么要用 | 運算呢 ,  | 或運算:有一個為1,值就為1
         * 是因為多次給每個bitMap位置上的32個元素賦值為1時但荤,這32個值不相互影響
         * 比如罗岖,第一次number=128,bitMap[3] = 00000000 00000000 00000000 00000001
         * 第二次number = 130,  bitMap[3] 不應該 =  00000000 00000000 00000000 00000100腹躁,而是應該 = 00000000 00000000 00000000 00000101
         * 所以需要每次賦值時進行或運算
         */
        bitMap[index] = bitMap[index] | 1 << num;
    }

    public boolean exist(Integer number) {
        int index = number / 32;
        int num = number % 32;
        /**
         * & 與運算:全部為1桑包,才為1
         * 與運算后的結果為 0或者2的num次冪,只要 != 0,就說明這個元素存在
         */
        return true == ((bitMap[index] &  1 << num ) !=  0) ;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市纺非,隨后出現(xiàn)的幾起案子捡多,更是在濱河造成了極大的恐慌,老刑警劉巖铐炫,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒜焊,居然都是意外死亡倒信,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門泳梆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳖悠,“玉大人,你說我怎么就攤上這事优妙〕俗郏” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵套硼,是天一觀的道長卡辰。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么九妈? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任反砌,我火速辦了婚禮,結果婚禮上萌朱,老公的妹妹穿的比我還像新娘宴树。我一直安慰自己,他們只是感情好晶疼,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布酒贬。 她就那樣靜靜地躺著,像睡著了一般翠霍。 火紅的嫁衣襯著肌膚如雪锭吨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天壶运,我揣著相機與錄音耐齐,去河邊找鬼。 笑死蒋情,一個胖子當著我的面吹牛埠况,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棵癣,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼辕翰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狈谊?” 一聲冷哼從身側響起喜命,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎河劝,沒想到半個月后壁榕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡赎瞎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年牌里,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片务甥。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡牡辽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敞临,到底是詐尸還是另有隱情态辛,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布挺尿,位于F島的核電站奏黑,受9級特大地震影響炊邦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜攀涵,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一铣耘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧以故,春花似錦蜗细、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昆烁,卻和暖如春吊骤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背静尼。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工白粉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鼠渺。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓鸭巴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拦盹。 傳聞我的和親對象是個殘疾皇子鹃祖,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容