位图算法的应用

位图的应用

编程珠玑 Chapter1

位图位向量图作为一个集合,表示的这样的一个数据结构:

          用字符串 0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 表示集合 {1,2,3,5,8,13}.

    位图的应用需要数据有如下的特性:

         1.输入数据限制在相对较小的范围内;

         2.数据没有重复;

         3.除了单一整数外,没有任何其他关联数据.

    但很可惜的是,大多数待排序数据没有这些特性(就是说这些特性在大多数情况下是很难满足的).

习题1 如果不缺内存,如何使用一个具有库的语言来实现一种排序算法以表示和排序集合

解题报告:

拥有库的语言,C/C++/JAVA都是很好的选择,由于目前只会C++,因此对于我来说别无选择. 而对于C++,实现排序的库有太多的选择,典型的有STL中的stdlib.h中的qsort和algorithm中的sort.对于这两者的区别,在这里不想多谈(其实我并不清楚,大概了解的是stdlib是C的产物,而algorithm的后代,不过,sort在使用上比较简单).

#include #include #include using namespace std;int main(){vector a;for(int i=0;i<=5;i++) a.push_back(i-5);sort(a.begin(),a.end());for(int i=0;i<5;i++) cout<

习题二 如何使用位逻辑运算(例如与、或、移位)来实现位向量

解题报告:

一开始当然没有想到要用位逻辑运算来实现位向量,而是用十进制来实现(这是理所当然的事)。

用一个一维数组a[10000000]来存储至多1E7个号码,考虑整数m,一旦发现这个号码,根据我们的算法,应当置a[m]=1.

好的,一切看起来都如此完美,简单的算法,出色的时间效率,差强人意的空间效率.但上机起来就不是这回事了:

#include #include using namespace std;int main(){ifstream in;ofstream outt;in.open("c:/project/out.txt");outt.open("c:/project/outt.txt");bool a[10000000];for(int i=0;i<10000000;i++) a[i]=false;int m;for(int i=0;i<1000000;i++) {in>>m; a[m]=true;}for(int i=0;i<10000000;i++) if(a[m]==true) outt<

但是美好的想法在现实面前是如此的脆弱,这段代码在运行的时候出错了,原因是数组越界.好吧,现在我可以承认,数组开到1E7是不现实的,这该如何是好?

现在是时候回到位逻辑运算了,这是一种模仿计算机底层二进制运算的运算方法,十分高效,但是第一次看上去会显得晦涩难懂,等到将它与十进制运算联系起来后,会发现它相当有用.

整个的思想是,a[10000000]显得太过巨大的原因是每一个元素a[i]只保留了一个bool值或者是一个整型值0或1,如果我们把每一个元素包含的内容扩充,使之保留尽可能多的号码是否存在的信息,那么数组范围会得到明显的下降.

事实上,我们是用每一个元素表示一个32位的二进制字符串,这样这个元素可以保留相邻32个号码是否存在的信息,数组范围就下降到10000000/32了.例如对于号码89256,由于89256 mod 32=2789…8,这样我们应该置a[2789]中32位字符串的第8位(从低位数起)为1.

现在问题的关键是,如何用位逻辑运算来表示这种操作. 关于位逻辑运算的知识,你应当去参考手头的C++教材,因为在这里我无法讲的比教材更好:

#define WORD 32#define SHIFT 5 //移动5个位,左移则相当于乘以32,右移相当于除以32取整#define MASK 0x1F //六进制下的31#define N 10000000//置位函数——用"|"操作符,i&MASK相当于mod操作//m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1)void set(int i){a[i>>SHIFT]|=(1<<(i&MASK));}//清除位操作,用&~操作符void clear(int i){a[i>>SHIFT]&=~(1<<(i&MASK));}//测试位操作用&操作符int test(int i){return a[i>>SHIFT]&(1<<(i&MASK));}

重要的是要从十进制运算的思维转化为二进制运算,位逻辑运算不过是工具而已.

下面是一个位运算的类:

 namespace mybv { class BitVector { private : const int shift; const int mask; const int bitPerWord; int* a; public: const int n; public: BitVector():shift(5),mask(0x1F),bitPerWord(32),n(10000000) { a = new int[1+n/bitPerWord]; for (int i =0; i>shift] |=(1<<(i&mask)); } void clr(int i) { a[i>>shift] &=~(1<<(i&mask)); } int test(int i) { return a[i>>shift]&(1<<(i&mask)); } }; }

习题三 在你自己的系统上实现位图排序并度量其运行时间

解题报告:

#define WORD 32#define SHIFT 5 //移动5个位,左移则相当于乘以32,右移相当于除以32取整#define MASK 0x1F //六进制下的31#define N 10000000#include #include using namespace std;//置位函数——用"|"操作符,i&MASK相当于mod操作//m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1)void set(int i){a[i>>SHIFT]|=(1<<(i&MASK));}//清除位操作,用&~操作符void clear(int i){a[i>>SHIFT]&=~(1<<(i&MASK));}//测试位操作用&操作符int test(int i){return a[i>>SHIFT]&(1<<(i&MASK));}int main(){ifstream in;ofstream outt;in.open("c:/project/out.txt");outt.open("c:/project/outt.txt");int m;for(int i=0;i>m; set(m);}for(int i=0;i

为什么说这个算法时空效率达到极致呢?我们对100万个不重复的正整数(1000,0000以内)的文件进行测试:

 

系统排序

C++/STL.set

C++/sort

C++/位图

总时间(s)

89

38

12.6

10.7

计算时间(s)

79

28

2.4

0.5

内存使用(MB)

0.8

70

4

1.25

(本测试数据是在较旧的电脑上测试的,但还是体现性能的差距)
第一行是总时间,第二行的计算时间是总时间减去数据读取耗时10.2秒。虽然通用C++程序使用内存和CPU时间是专用C++程序(C++位图)的50倍,但是它的使用仅需要一半的代码,并能很容易扩展到其他问题上,这也是专用C++程序最大的缺点吧。

 

原文链接:http://knightsgang.spaces.live.com/Blog/cns!5536415C9770F9BF!144.entry

你可能感兴趣的:(算法)