Can I fly high in the Sky?

Never say never.

导航

[编程珠玑]如何使用位逻辑来实现位向量

Posted on 2015-05-23 16:44  lsr_flying  阅读(997)  评论(0编辑  收藏  举报

编程珠玑开篇的一道题目是这样的:

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

 

一.何为位向量?

在许多情况下(如对象为满足或不满足某条性质的情况),用一个二进制位就足够表示一个对象了。但是,不能用一个变量名直接表示一个位(不存在单独为一位的数据类型)。于是,就考虑将多个位组成一个基本数据类型,通过对这个基本数据类型的操作,达到使用位的方法。同时,为了方便,把由位组成的基本数据类型组成数组,这样,就可以对一定范围的位数据集合进行操作。我们把这种形式的数据结构称为位向量。

 

接下来,再考虑一点,对位向量各位的操作我们不能通过名称去访问,只能通过位的位置去操作,即我们要操作第几位的数据。在我们看来位是由0——n连续的,实际上因为我们是用基本数据类型的数组进行存储的,因为,这些位是分割在不同的数组元素当中,处在不同数组元素的不同位置当中。假设我们以int类型做为基本数据类型,则一个int类型(c++)中,可以存储32个位。则对于特定的一个位(位置为:pos),我们首先考虑它处在哪个int数组元素当中(pos/32,即知道处在哪个数组元素当中),然后考虑该位处在该数组元素的哪个位置(即pos mod 32)。

 

对位向量的操作主要有三个:对特定位置1,对特定位清0,判断特定位。

 

二.实现代码

有了以上知识,我们看下编程珠玑中对该问题的实现代码。

 1 #define BITSPERWORD 32  //使用的基本数据类型为32位,int类型
 2 #define SHIFT 5   //与确定位处于哪个数组元素有关
 3 #define MASK 0x1F    //与确定位处于数组元素哪一位有关
 4 #define N 10000000  //位长度
 5 int a[1+N/BITSPERWORD];  //数组长度
 6 
 7 //将对应位置1
 8 void set(int i) {
 9 a[i>>SHIFT] |=(1<<(i & MASK));
10 }
11 
12 //将对应位置0
13 void clr(int i){
14 a[i>>SHIFT] &=~(1<<(i & MASK));
15 }
16 
17 //判断对应位
18 void test(int i){
19 return a[i>>SHIFT] & (1<<(i & MASK));
20 }

下面来分析下以上代码。

1)确定数组元素,即i/32,对于2的倍数的整除,我们可以使用位移运算,因为32=2^5,所以i/32=i>>5,故a[i>>SHIFT]确定了数组元素。

2)确定数组元素后,我们要确定位的相对位置,即为i mod 32,观察,当对i执行右移(5位)运算时,把低5位消除了,而这低5位正是余数,因此可以通过i&0x1F,获得低5位的值,进而得到位的相对位置,故如i & MASK。

3)确定位置后,我们进一步转成对应的位位置掩码,即1<<(i&MASK)。

通过以上三步,我们完成了定位的工作。

 

分析三条语句:

1)set():因为是置1操作,所以只需要把原数组元素与位位置掩码进行或操作即可;

2)clr():因为是置0操作,所以先把位位置掩码取非,使特定位位0,再与原数组元素进行与操作即可。

3)test():判断特定位,只需要原始数组元素与位位置掩码进行与操作,即可判断当前位是否为1。