位图应用

来源:互联网 发布:手游网络加速器排行 编辑:程序博客网 时间:2024/06/06 10:52

一、背景介绍

       本文主要是对编程珠玑中第一章的学习总结。问题背景大致如下:一个最多包含n个正整数的文件,每个数都小于n,其中n=10^7,同时所有的正整数不重复出现。现在需要按升序将所有的整数输出,约束条件是最多有1MB的内存空间可用,但磁盘空间充足。
       如果按通常的处理的方法,使用int类型(32位)存储整数,这样1MB的空间可存储10^6B/4B,即约250000个整数,虽然不能同时对所有的数据进行排序。但也可以采取多趟遍历磁盘的方法完成排序工作,但却IO开销大耗时多。于是文中便提出了利用位图(位向量)的方法来解决问题。

二、位图实现

1、实现介绍

       例如要表示集合{1,2,3,5,8,13},可采用20位的字符串“01110100100001000000”表示。在问题中每个7位的十进制数表示一个小于1000万的整数,于是我们可以使用一个具有1000万个位的字符串来表示这个文件,其中当且仅当整数i在文件中存在时,第i位为1。

2、C语言实现

       由于C语言中没有实现位图的数据结构(c++中有bitset集合),需要通过位运算来实现。下面的C语言程序采用了int来实现位图。
       假设总的位图位数为N(题设为1000万),则需要的int数组的位数为(N/32 + 1),所以定义了数组int a[N/32 + 1]。
       对于逻辑上的第i位,其存储的位置则为第i/32(位运算采用i>>5,即将i右移5位)个int位置,位位置为i%32(位运算采用i&0x1F实现,即i与数31进行“与”运算)。
       所以对逻辑上第i位置1(函数set)为:a[i>>5 ] = a[i>>5] | 1<<(i&0x1F)。即将1左移(i&0x1F)位后再和a[i>>5]进行“或”运算。例如当i等于33时,由33/32及33%32知逻辑位33由a[1]的第1位表示(含第0位)。位运算中,[i>>5]即为a[1],1<<(i&0x1F)结果为2(即0000 0010),假如此时a[1]为0(即0000 0000),所以或运算的结果是将a[1]的第1位置1。
c语言实现代码如下:

#include<stdio.h>#include<stdlib.h>#include<time.h>#define N 10000000int a[N/32 + 1];//将逻辑上第i位置1void set(int i){    a[i>>5 ] = a[i>>5] | 1<<(i&0x1F);}//查看逻辑上第i位状态int test(int i){    return a[i>>5] & (1<<(i&0x1F));}//将逻辑上第i位置0void clear(int i){    a[i>>5] = a[i>>5] & (~(1<<(1&0x1F)));}//清零void pre(){    int i;    for(i = 0; i< N; i++)        clear(i);}int main(){    FILE *fp;    srand((unsigned)time(NULL));    int count = 0;    int t, i;    if( (fp = fopen("input.txt", "r")) == NULL )    {        printf("can not open the file!\n");        exit(0);    }    //对位图清零    pre();    //读入文件,将读入数据对应逻辑位置1    while(fscanf(fp, "%d", &t) != EOF){        set(t);    }    //输出排序结果    for(i = 0; i < N; i++)    {        if(test(i)){            printf("%d\n", i);        }    }    fclose(fp);    return 0;}

三、其他

       在编程珠玑第一章的习题中,包括了将内存上限严格限制为1MB(习题5),因为上面的实现实际是需要1.25MB的。其实只要理解了前面的内容也还是很好实现的,最关键的两个思想是位图和多趟排序。以及习题6中将题目条件改为每个整数最多重复10次,则可以用4位来记录每个整数的出现次数(好吧 我开始的想法是用10位~)。
       以上。

0 0