• 珠玑之椟


    [珠玑之椟]位向量/位图的定义和应用

      位向量/位图是一个很有用的数据结构,在充分利用小空间存储大量数据方面非常具有优势,Linux内核中很多地方都是用了位图。同时,它不但基础,而且用到了很多编程语言的知识,以及对细节的把握,常常作为面试题出现。这里将要介绍它的实现、操作、应用。

      与位图(bitmap)比,我更倾向于用位向量(bit vector),前者比较容易与图形学里的名词混淆,其实提到位图,多指的是“是使用像素阵列来表示的图像”(维基百科),为了避免这一点,下文中使用位向量。先来看看产生位向量的需求:

      一般地,对于多个对象和一个性质,这些对象可能满足(true)也可能不满足(false)这条性质。那么,为了表示所有对象对这个性质的满足情况,最容易想到的方式是分配一个int型变量(这里不讨论布尔型,C++没有对布尔型占用空间有明确规定;本文主要讨论C)作为标志,用1表示满足条件,用0表示不满足条件。同时为了方便查询,把这些对象的标志整合成一个数组。显然,使用int来表示是0还是1有些太浪费空间了,即使把int改为char,浪费的情况也只是减轻了一部分,仍有很大的空间被浪费。考虑到计算机中最小的数据单位是非0即1的二进制位,对于一个对象,使用一个二进制位就足够了。很多语言都具有位运算,利用位运算是可以完成本段开始处提出的要求的。当然,因为不能用一个变量名直接表示一个位,那么可以将多个位组合成一个基本数据类型,通过对这个基本数据类型进行操作,达到使用位的方法。同时,为了方便,延续使用int数组的做法,把这些由位组合成的基本数据类型也组成数组。

      经过这样分析,位向量的实现方法大体是:多个位组成一个基本数据类型,基本数据类型组合成数组。根据这个思路就可以写出位向量的表示了。在阅读下面代码前,建议读者尝试自己独立完成,这是一些提示:简单起见,使用int作为位组成的基本数据类型,且int使用32位表示;int数组中元素的个数如何计算?

     位向量的表示

      写完了表示,就需要为这个数据结构增加对应的操作了。对于每个位的操作,有三种:设置为0、设置为1、读取当前值。根据上文位向量的表示,实现这三种操作。同样建议读者先尝试独立完成。以下代码参考自《编程珠玑》习题1.2。

     位向量操作

      使用位向量前不要忘记对所有位进行初始化:

    for(i=0;i<N;i++)
        clr(i);

      当然,你也可以用这个或许更快的初始化方式:

    int temp = N/BITPERWORD+1;
    for (i=0; i< temp;i++) 
        a[i] = 0;

    补充讨论:

    Q:为什么要用看上去并不那么直接的位运算而不是i/32和i%32?

    A:为了速度而不是易读性。你所写下的最初版本如果使用的是/和%,当然没有错;当你着手于提高效率的时候,把它们改成移位运算就势在必行了。虽然编译器可能会对/和%在除数为2的幂时进行优化,写成移位运算,你当然可以自己来完成这件事来保证确实优化了。

     

    Q:是否可以将存放位向量的数组作为参数?

    A:当然可以,上文代码只是为了叙述方便。

     

    Q:是否可以把这三个函数写成宏?

    A:其实我曾经被面试过一道题,就是要求写一个宏,完成上面clr(i)的任务。写成宏当然没问题,注意加好括号就行,具体的用法让调用的人去担心吧(笑)。

     

    Q:位向量和位字段有什么区别和联系?可以用位字段来实现位向量吗?

    A:C语言允许将结构的整数成员存入比编译器通常允许的更小的空间里。然而它有三个风险:不同计算机中对齐限制不同;位字段宽度限制不同;将位包装成字的字节顺序不同。在位向量里,虽然也有依赖于具体计算机特性的限制(需要预先知道int的位数),但是位的包装是由程序员来控制的,也不必考虑对其限制。移植性略好于将成员声明为1位整数位字段的结构。

     

    应用:

    1.Linux中分配唯一pid的算法、内存管理的伙伴分配系统等,详细可以google,关键词:linux+位图。

    2.一个最多包含n个正整数的文件,每个数都小于n,其中n=107,并且没有重复。最多有1MB内存可用。要求用最快方式将它们排序并按升序输出。(《编程珠玑》第一章正文)方法是一次读入文件,把出现过的数字对应位置1;读取完毕后从低位到高位输出位向量为1的位所代表的数。

    3.如果有用时间换空间的必要,可以将寻找1至某个数之间的质数的埃氏筛法用位向量实现。原始版本的代码见于《编程珠玑(续)》习题1.2,算法简介:一个n比特的数组,初值均为真。从2开始,每发现一个质数,就把这个数组中所有这个数的倍数设为假。下一个质数就是数组中下一个为真的比特。循环至所有数都被遍历,即可得到该范围内所有的质数。

     

    引申和扩展:

    1.(习题1.6)每个整数最多出现10次而不是1次;如果内存限制不变,又应该怎么做?提示:多趟排序

    (更一般的扩展:用几个位表示一个对象的多个性质,也即一个对象不仅仅只占用一位而是多位,但每个对象占用的位数是相同的。重写位图。)

    2.(习题1.9)对于稀疏的位向量,初始化很浪费时间。一般地,对于一个向量(不仅限于位向量),怎样利用辅助数据结构,使得第一次访问位向量的某一位时才将其初始化为0?

    解法:

    对于向量int data[N],分配辅助数据结构int from[N]和to[N],以及一个整数top,初始化top=0。

    插入data[i]时,from[i]填入top,to[top] = i,top++;

    这样维持了一个性质:top代表下一个被插入的值的次序;from[i]代表了data[i]是第几个被插入的;to[]中小于top的元素to[j]保存了第j个被插入对应在data[]的下标。

    若data[i]未初始化,则from[i]也未初始化,此时即使from[i]中未初始化的垃圾值<top,to[from[i]]!= i,仍能判断出data[i]未初始化。

    “珠玑之椟”系列简介与索引

     
    [珠玑之椟]位向量/位图的定义和应用
    摘要: 位向量/位图是一个很有用的数据结构,在充分利用小空间存储大量数据方面非常具有优势,Linux内核中很多地方都是用了位图。同时,它不但基础,而且用到了很多编程语言的知识,以及对细节的把握,常常作为面试题出现。这里将要介绍它的实现、操作、应用。 与位图(bitmap)比,我更倾向于用位向量(bit vector),前者比较容易与图形学里的名词混淆,其实提到位图,多指的是“是使用像素阵列来表示的图像”(维基百科),为了避免这一点,下文中使用位向量。先来看看产生位向量的需求: 一般地,对于多个对象和一个性质,这些对象可能满足(true)也可能不满足(false)这条性质。那么,为了表示所有对象...阅读全文

    posted @ 2013-06-24 16:42 五岳 阅读(263) | 评论 (0) 编辑

    “珠玑之椟”系列简介与索引
    摘要: 系列博文主要目的: 收集《编程珠玑》和《编程珠玑(续)》(以下简称《续》)上的算法和思想,并包括了一些自己的思考和对相关问题的引申,以备复习和查用。内容提要: 主要是算法收集,结合了《程序设计实践》 (Practise of Programming)、《编程精粹:编写高质量C语言代码》(Writing solid code)和《算法导论》的部分相关内容,以及自己的思考,无论你是否看过原书,都能够帮助你快速理解以至于能够独立完成这些算法。我相信自己写的解释比原书详细而易于理解,并且根据我的经验,对很多问题进行了扩展,这也是这个系列命名为“珠玑之椟”的原因。当然,“买椟还珠”是不可取的,这个系..阅读全文

    posted @ 2013-06-24 16:41 五岳 阅读(16) | 评论 (0) 编辑

     
  • 相关阅读:
    c语言知识
    数字地与模拟地
    C语言实现顺序表(增删)
    传统数据库、Nosql数据库与云数据库区别?
    大数据处理架构如何
    warning: implicit declaration of function 'func1' [-Wimplicit-function-declaration]
    window10创建virtualenv虚拟环境
    二叉树的实现以及三种遍历方法--代码
    损失函数--KL散度与交叉熵
    市场回测与对冲套利
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3153557.html
Copyright © 2020-2023  润新知