zoukankan      html  css  js  c++  java
  • 海量处理 bitmap及区段划分

    还有一种经常使用的策略是bitmap. Bitmap本身也是一种hash-table, 只不过hash的结果恰好落在[0, sizeof_bitmap_in_bits]内. 因为hash到的每个slot只有一个bit,所以通常用作判断是否存在等bool型的问题.

    例子, 已知40亿个不重复的unsigned int, 如何判断一个整数在不在这40亿个整数里面?

    给定说unsigned int,其实是指定了元素的可能范围, [0, 2^32 – 1]中, 注意2^32=42,9496,7296, 大约是42亿.

    判断存在与否, 一个bit就够了,而记录所有的uint32_t, 4GB/8=512MB的bitmap也就够了, 这年头,512内存的需求是很正常的.

    和这个问题类似的一个问题:

    已知一个web server的客户ip地址日志, 大约有40亿条记录, 如何判断某个ip地址是否在这个日志中?

    这个问题由于有了上下文, 就要具体分析. 具体来说, 一个web server的访问记录不大可能是不重复的, 我们有理由假定许多记录都是重复的, 所以一个较小的hash-table就可以了. 当然,hash-table小了, 会导致准确度的下降, 因为这时候的hash函数不是单射了. 为了保证成功, 512MB的bitmap还是可以考虑的.

    这个问题的一个变种:

    2.5亿个uint32_t整数中, 寻找不重复的整数的个数.

    1bit信息只能判断存在与否, 那么是否可以附加1bit的信息, 每个元素映射到2bit, 00表示不存在, 01表示出现1次, 10表示出现2次, 11表示更多, 则很容易就实现了.

    这种做法可以换个思路来理解, 用两个bitmap来判断, 当bm1里面没有置位的时候,将bm1中对应的位置位; 当bm1中对应位置已经置位的时候, 就将bm2里面的对应位置置位. 最后, 只要找到在bm1而不在bm2的那些bit位就可以了.

     

    这种问题也可以产生一些变种, 譬如在64bit已经普及的今天:

    已知40亿个不重复的uint64_t, 如何判断一个uint64_t在不在这个集合里面?

    在看这个问题之前, 先看一个更一般性的问题:

    有一个proxy server的url访问记录, 大约有40亿条记录, 如何快速确定某个url是否在这个日志中?

    url不好处理,可以先用cryptographic hash function做一个hash(注意这种hash与通常所说的hash-table的hash的区别, 由于cryptographic hash function的性质, 我们认为这种hash是抗碰撞的). MD5已经不行了, 目前我们可以假设160bit的SHA-1是抗碰撞的.

    这就转化成了: 已知40亿个不重复的uint160_t, 如何判断一个uint160_t在不在这个集合里面?
     

    对于以上问题, 由于可能的取值空间太大(2^64或者2^160), 直接bitmap内存肯定放不下了. 但是可以利用前面hash的思想, 将40亿个元素通过hash拆分到多个小文件(子集)中, 也即桶划分(区段划分). 具体拆分规则是这样的, 假设我们的hash规则是选取前k位, 那么将最多拆分成2^k个文件, 而每个文件至多有2^(64-k)个元素. ---- 事实上, 这种拆分还可以继续细化为多级的拆分, 譬如前3bit用作第一级目录名, 4-6bit用作第二级目录名, 7-10bit用作文件名…
     

    当我们走了一遍拆分成2^k个小文件以后, 每个小文件都可以放到内存了, 剩下的就好办了. 每个小文件可以自身就是一个bitmap, 这样对于给定的元素, 找到它应该在哪个子集(小文件)里面, 然后把那个文件的内容拉到内存, whatever后续处理.
     

    类似的思想也可以用于解决下面的这些相关问题.

    已知有40亿个不重复的uint32_t整数, 如何快速找到一个整数, 不在这40亿个整数内?

    已知有40亿个不重复的uint32_t整数, 如何找到它们的中位数?

    第一个问题, 拆分以后, 必定有若干个子集中元素个数小于2^(64-k), 表明这个子集的区段里面, 是有不存在的元素的, 读入这个文件, 随便搞就行了.

    第二个问题, 拆分以后, 可以统计各个区段的元素个数, 立刻可以知道中位数位于哪个子集, 搞到内存里, 很快就能找到了. 注: 读入该子集以后, 由于子集中元素个数已知, 转化为求一个n元素序列中第k大元素的问题, 这个问题可以用类似于qsort中partition的办法, 在线性时间内解决, 参见http://www.cnblogs.com/qsort/archive/2011/05/09/2041653.html

     

    这里桶划分(区段划分)的思想并不新鲜, 譬如上面"判断一个uint64_t是否在某集合里面", 其实就是一个操作系统中线性地址是否已经映射的判断, 而操作系统中关于地址空间页表的分段管理(两级或三级页表), 就是上面的按区段划分的典型例子.

  • 相关阅读:
    Codeforces Round #592 (Div. 2)C. The Football Season(暴力,循环节)
    Educational Codeforces Round 72 (Rated for Div. 2)D. Coloring Edges(想法)
    扩展KMP
    poj 1699 Best Sequence(dfs)
    KMP(思路分析)
    poj 1950 Dessert(dfs)
    poj 3278 Catch That Cow(BFS)
    素数环(回溯)
    sort与qsort
    poj 1952 buy low buy lower(DP)
  • 原文地址:https://www.cnblogs.com/qsort/p/2042626.html
Copyright © 2011-2022 走看看