日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用bitmap处理海量数据问题:43亿QQ号所占内存大小为什么是512M?40亿个QQ号如何去重?

發布時間:2023/12/29 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用bitmap处理海量数据问题:43亿QQ号所占内存大小为什么是512M?40亿个QQ号如何去重? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?參考:

  • 騰訊43億QQ號碼用完后怎么辦?
  • 騰訊三面:40億個QQ號碼如何去重

一、背景:

首先,明確兩點:

  • QQ號是 unsigned int 類型(4字節無符號整數,共32bit), 也就是說 QQ號的取值范圍是:[0,232?1]\color{blue}{[0, 232 - 1]}[0,232?1]
  • QQ號是一個長度為10位的整數,大約是43億,這也是QQ號碼的理論最大值。
  • QQ號碼的最小值是 10001, 為什么tx要做這種限制呢?僅僅是早期的一個設定而已。

二、43億QQ號所占內存大小計算:

其次,43億QQ號所占內存大小是多少呢?512M

  • 一個 unsigned int 類型數據可以標識 0 ~ 31 這32個整數的存在與否(4Byte,0 ~ 31 )
  • 兩個 unsigned int 類型數據可以標識 0 ~ 64 這64個整數的存在與否(8Byte,0 ~ 64 )

又因為QQ號是 unsigned int 類型(無符號整數,4Byte,32位),也就是說 QQ號的取值范圍是:[0,232?1]\color{blue}{[0, 232 - 1]}[0,232?1]
因此可對 232 次方范圍內的QQ號所占大小進行推導:

  • 1Byte所能標識數的范圍為0~7(1字節,8 位)
  • 4Byte所能標識數的范圍為0~31(4字節,32 位)
  • 那么多少Byte才能夠標識 數的范圍為 0 ~ 232-1(232 位)呢?
  • 綜上,用 232 / 32 來表示:0 ~ 232-1(232 位) 能包含多少個 0 ~ 31(32位) 范圍的塊。并且每個 0 ~ 31(32位) 的塊兒大小為4字節,因此 232 / 32 × 4字節 的結果就是 0 ~ 232-1 范圍所占字節的總大小,也就是 43億 QQ號所占字節總大小。
    232位 / 32位 × 4字節 / 1024 / 1024 = 512M\color{blue}{512M}512M(1M=1024KB,1KB=1024Byte)

    由此可見,512M的內存大小,就可以用來標識所有QQ號的存在與否。

    三、bitmap的具體實現:

    • 把所有 bitMap 位數組依次拼接后可表示 0 ~ 232 范圍內43億的數,所以足夠存儲40億不重復的賬號了
    • bitmap數組中的一個uint32類型元素,由4字節組成(每個字節占8位):{[8位][8位][8位][8位]} {[8位][8位][8位][8位]} {[8位][8位][8位][8位]} {[8位][8位][8位][8位]} …
    • 如:[0 1 2 3 4 5 6 7] [8 9 10 11 12 13 14 15] [16 17 18 19 20 21 22 23] [24 25 26 27 28 29 30 31] … [...] [...] [...] [... 40億]
    • 共需 40億/8位 = 5億字節,5億字節/4字節 = 1.25億 個uint32類型元素數組來串聯拼接表示(uint32類型大小占4字節),所占內存大小為:4000000000/8/1024/1024 ≈ 477M\color{blue}{477M}477M
    • 還原qq號(數組下標從0開始):當前第block塊數 * 每個uint32 block塊的固定大小為32 + 當前block塊內的偏移量余數 yushu

    • 每次新增QQ號時,都只需要在 bitMap[block] 的結果基礎上繼續進行異或 ^ 運算即可 ~
    • 比如原本"QQ號=9(二進制表示 1 0000 0000,位下標為 9)“是有值的,現在又新增了"qq號=10(二進制表示 10 0000 0000,位下標為 10)”
    • 那么9和10相異或后得: 1 0000 0000 ^ 10 0000 0000 = 11 0000 0000,表示第 9 位和第 10 位的QQ號都有值了
    • 0x1<<(yuShu-1):比如原本"QQ號=9"時,那就把 0x1 左移8位,即:0x1 << 8,得到 100000000,最后從右至左第9位為1
    • 關鍵代碼:bitMap[block] = bitMap[block] ^ (0x1 << (yuShu - 1)) // 設置標記位:在之前bitMap已有結果的基礎上,設置第block塊上的第 “(0x1 << (yuShu - 1))” bit位為1
    // 把所有位數組依次拼接后可表示 0~232 范圍內的數,所以足夠存儲40億不重復的賬號了 // bitmap數組中的一個uint32類型元素,由4字節組成(每個字節占8位):{[8位][8位][8位][8位]} {[8位][8位][8位][8位]} {[8位][8位][8位][8位]} {[8位][8位][8位][8位]} ... // 如:[0 1 2 3 4 5 6 7][8 9 10 11 12 13 14 15][16 17 18 19 20 21 22 23][24 25 26 27 28 29 30 31] ... [...][...][...][... 40億] // 共需 40億/8bit = 5億byte,5億byte/4byte = 1.25億個uint32類型元素數組來串聯表示(uint32類型大小占4字節),所占內存大小為:4000000000/8/1024/1024≈477M // 還原qq號(數組下標從0開始):當前第block塊數 * 每個block塊的固定大小32 + 當前block塊內的偏移量余數yushu// 每次新增QQ號時,都只需要在bitMap[block]的結果上繼續進行異或^運算即可~ // 比如原本"QQ號=9(二進制表示 1 0000 0000,位下標為9)"是有值的,現在又新增了"qq號=10(二進制表示 10 0000 0000,位下標為10)" // 那么相異或后得: 1 0000 0000 ^ 10 0000 0000 = 11 0000 0000,表示第9位和第10位的QQ號都有值了 // 0x1<<(yuShu-1):比如原本"QQ號=9"時,那就把0x1左移8位,即:0x1 << 8,得到 100000000,從右至左第9位為1// 根據QQ號來設置其在bitMap中bit位 func BitCalculation(bitMap []uint32, everyBlockSize, qq uint32) {// 判斷是屬于第幾塊的第幾個bit位block := qq / everyBlockSize // 塊數:第block塊(bitmap數組從第0塊開始,無需根據yuShu是否為0來判斷block是否+1)yuShu := qq % everyBlockSize // 余數:第block塊中的具體第幾個bit位// fmt.Printf("block=%v, yuShu=%v \n", block, yuShu)fmt.Printf("在對%v(%b)位運算設置標記位1之前,bitMap的第%v塊上二進制表示: %b\n", qq, 0x1<<(yuShu-1), block, bitMap[block])bitMap[block] = bitMap[block] ^ (0x1 << (yuShu - 1)) // 設置標記位:在之前bitMap已有結果的基礎上,設置第block塊上的第 "(0x1 << (yuShu - 1))" bit位為1fmt.Printf("在對%v(%b)位運算設置標記位1之后,bitMap的第%v塊上二進制表示: %b,還原qq號為: %v\n\n", qq, 0x1<<(yuShu-1), block, bitMap[block], block*everyBlockSize+yuShu) }// 初始化相關參數 func TestBit(qqCnt uint32, qqArr []uint32) {var everyBlockSize uint32 = 4 * 8 // uint32類型占32位:一個uint32能標記0~31范圍內32個QQ號blockCnt := qqCnt / everyBlockSize // 40億QQ號所需uint32類型的塊數bitMap := make([]uint32, blockCnt) // 存儲40億QQ號的bitmap,統一初始化為0// fmt.Println("blockCnt 40億QQ號所需uint32類型的塊數: ", blockCnt) // 1.25億塊for i := 0; i < len(qqArr); i++ {// 設置每個測試QQ到bitMap中BitCalculation(bitMap, everyBlockSize, qqArr[i])} }func main() {// 以下均默認在64位操作系統下執行:// i := int(1) // int在64位系統下默認大小為8,即:int64// fmt.Println(unsafe.Sizeof(i)) // 8//// j := int32(1)// fmt.Println(unsafe.Sizeof(j)) // 4//// k := int64(1)// fmt.Println(unsafe.Sizeof(k)) // 8 相當于long/double類型,占8字節(64位) 最大能表示范圍為:2??//// ll := float64(1)// fmt.Println(unsafe.Sizeof(ll))// int32 表示范圍: -2147483648 ~ 2147483647,因為賬號數有40億個,所以采用無符號類型 uint32 類型,表示范圍: 0 ~ 4294967295var qqAccountCnt uint32 = 4000000000testNumArr := []uint32{9, 399999999, 1234567890} // test dataTestBit(qqAccountCnt, testNumArr)return }

    示例結果展示:

    四、擴展

    練習一:文件中有40億個互不相同的QQ號碼,請設計算法對QQ號碼進行排序\color{blue}{排序}排序,內存限制1G。
    很顯然,直接用bitmap,
    標記這40億個QQ號碼的存在性,然后從小到大遍歷正整數,當bitmapFlag的值為1時,就輸出該值,輸出后的正整數序列就是排序后的結果。
    請注意,這里必須限制40億個QQ號碼互不相同。通過bitmap記錄,客觀上就自動完成了排序功能。

    練習二:文件中有40億個互不相同的QQ號碼,求這些QQ號碼的中位數\color{blue}{中位數}中位數,內存限制1G。
    一些刷題經驗豐富的人,最開始想到的肯定是用堆或者文件切割,這明顯是犯了本本主義錯誤。直接用bitmap排序,當場搞定中位數。

    練習三:文件中有40億個互不相同的QQ號碼,求這些QQ號碼的topK\color{blue}{topK}topK,內存限制1G。
    很多人背誦過topK問題,信心滿滿,想到用小頂堆或者文件切割,這明顯又是犯了本本主義錯誤。直接用bitmap排序,當場搞定topK問題。

    練習四:文件中有80億個QQ號碼,試判斷其中是否存在相同\color{blue}{相同}相同的QQ號碼,內存限制1G。
    一些吸取了經驗教訓的人肯定說,直接bitmap啊。然而,又一次錯了。根據容斥原理可知:
    因為QQ號碼的個數是43億左右(理論值2^32 - 1),所以80億個QQ號碼必然存在相同的QQ號碼。

    五、總結:

    • QQ號理論上的范圍為:10001 - 43億(232),其類型為 unsigned int
    • 43億QQ號所占內存大小,經計算后大約占 512M (滿足小于1G的要求)
    • 40億QQ號所占內存大小,經計算后大約占 477M (滿足小于1G的要求)
    • bitMap實現方案 及 異或 ^ 等位運算
    • 利用 bitMap 來處理海量數據問題,內存占用低,滿足要求且可以取得不錯的效果。包括:排序、中位數、topK、去重 等等…

    總結

    以上是生活随笔為你收集整理的利用bitmap处理海量数据问题:43亿QQ号所占内存大小为什么是512M?40亿个QQ号如何去重?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。