Programming Pearls 笔记(1)(续)
对于我上次提到的问题:怎么开107这么大的数组?这个问题可能是比较弱智的。。。。鄙视自己。。。
之所以要开107这么大的数组,如果是int a[107],则我们就是用一个int 存储一个信息。这就是大量浪费。而我们知道一个int 有四个字节,在一般的机器上都是32位,即一个int可以表示32个0和1。所以我们数组就可以开成a[1+107/32],这样数组不就小了?
刚好COLUMN1的习题二就是要求实现这个问题:How would you implement bit vectors usingbitwise logical operations(such as and,or and shift)?
我在官网找到代码如下,并做了简要分析:
/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */
/* bitsort.c -- bitmap sort from Column 1
* Sort distinct integers in the range [0..N-1]
*/
#include <stdio.h>
#define BITSPERWORD 32//int应该有四个字节,即可以表示32个 0与1
#define SHIFT 5//左移5位即为除以32
#define MASK 0x1F//31的十六进制表示
#define N 10000000//题目要求10^7的大小
int a[1 + N/BITSPERWORD];//10000000/32,马上把a[10000000]的大小降下来了。
//i>>SHIFT表示左移五位,“|=”就是a|=b即a=a|b,不用多说。关键是i&MASK等同于i%32,这个结论我会在后面解释。
void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); }//在相应的位置上置1
void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); }//置0
int test(int i){ return a[i>>SHIFT] & (1<<(i & MASK)); }//取出相应位置上的值
int main()
{ int i;
for (i = 0; i < N; i++)
clr(i);//初始化
/* Replace above 2 lines with below 3 for word-parallel init
int top = 1 + N/BITSPERWORD;
for (i = 0; i < top; i++)//另一种初始化,没什么说的
a[i] = 0;
*/
while (scanf("%d", &i) != EOF)
set(i);//对数据的读入
for (i = 0; i < N; i++)
if (test(i))
printf("%d\n", i);//如果在数组里存在i,就输出,明显是从小到大输出的
return 0;
}
//有个结论:m%n,当n为2的x次幕的时候,取余可表示为m&(n-1)
//分析完这段代码,只想说一句话:神一般的存在啊!!
刚开始,乍一看这个代码,顿时就懵了。。。。。。完全不知所云。后经查证,慢慢看懂了。关键部分其实就是这样
1 void set(int i) { a[i/32] |= (1<<(i % 32)); }
2 void clr(int i) { a[i/32] &= ~(1<<(i % 32)); }
3 int test(int i){ return a[i/32] & (1<<(i % 32)); }
然后我这里证明下那个结论,应该是说明下:
显然我们知道 : 21=10
22=100
23=1000(即2的N次幂,就是1后面n个0)
然后对于任意的m,我们改写成二进制形式,例如:m=1010110,可以写成m=1010000+110。
记a=1010000,b=110。则m%n=(a+b)%n=a%n+b%n。
设n=22=100,显然,a%n=0。而b%n ,此时b与n的位数相同,n=(1后面几个零的形式),所以b&(n-1)应该就是余数 即 110%100=10 110&011=10。
即 1010110%100=10,1010110&0000011=0000010。
基本上就是这样了,不知道说没说清楚。这也算不上是证明。
分析完这段代码,如果不是对底层汇编比较熟悉的话,是不可能写出来这样的代码的。理论上,移位操作是可以实现任何运算,且效率应该都是较高的。这个bitmap的思想是很好的。